FFDemux的Open实现打开媒体文件
FFdemux的Read读取帧数据接口编写
主要是增加了三个类XData, IDemux, FFDemux, 具体实现和作用如下
数据类:XData
- 该类用于存储各个线程生产的需要交互的数据
- unsigned char *data 存储的数据的地址
- int size 存储的数据的大小
- void Drop() 释放资源
//XData.h
#ifndef BOPLAY_XDATA_H
#define BOPLAY_XDATA_H
struct XData {
unsigned char *data = 0;
int size = 0;
void Drop();
};
#endif //BOPLAY_XDATA_H
//XData.cpp
#include "XData.h"
extern "C"{
#include "libavformat/avformat.h"
}
void XData::Drop() {
if(!data){
return;
}
av_packet_free((AVPacket **)&data);
data = 0;
size = 0;
}
解封装接口类:IDemux,即适配器模式的目标抽象类,定义了客户所需的端口
- virtual bool Open(const char *url):接口打开文件或者流媒体链接,并进行解封装,获取音视频基本信息(如封装格式、编码格式和视频时长等等),传入参数为本地文件地址或流媒体url, 打开成功返回true,失败则返回false
- virtual XData Read():该接口负责读取一帧的数据并返回出来,主要是将
//IDemux.h
#ifndef BOPLAY_IDEMUX_H
#define BOPLAY_IDEMUX_H
#include "XData.h"
//解封装接口类
class IDemux {
public:
//打开文件或者流媒体 rtmp http rtsp
virtual bool Open(const char *url) = 0;
//读取一帧数据,数据由调用者清理
virtual XData Read() = 0;
//总时长(单位ms)
int totalMs = 0;
};
#endif //BOPLAY_IDEMUX_H
//IDemux.cpp
#include "IDemux.h"
解封装的实现类:FFDemux,即适配器模式中的适配器类,适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系,FFDemux调用了ffmpeg
//FFDemux.h
#ifndef BOPLAY_FFDEMUX_H
#define BOPLAY_FFDEMUX_H
#include "IDemux.h"
//声明, 不引用头文件, 封装后调用者不需要知道ffmpeg的头文件
struct AVFormatContext;
class FFDemux: public IDemux{
public:
//打开文件或者流媒体 rtmp http rtsp
virtual bool Open(const char *url);
//读取一帧数据,数据由调用者清理
virtual XData Read();
FFDemux();
private:
//只有在无参数构造函数时, 这个赋值才会生效
//AVFormatContext描述了一个媒体文件或媒体流的构成和基本信息
AVFormatContext *ic = 0;
};
#endif //BOPLAY_FFDEMUX_H
//FFDemux.cpp
#include "FFDemux.h"
#include "XLog.h"
extern "C"{
#include "libavformat/avformat.h"
}
bool FFDemux::Open(const char *url) {
XLOGI("open url %s begin", url);
int re = avformat_open_input(&ic, url, 0, 0);
if (re != 0){
char buf[1024] = {0};
av_strerror(re, buf, sizeof(buf));
XLOGE("FFDemux open %s failed!", url);
return false;
}
XLOGI("FFDemux open %s successfully!", url);
//读取文件信息
re = avformat_find_stream_info(ic, 0);
if (re != 0){
char buf[1024] = {0};
av_strerror(re, buf, sizeof(buf));
XLOGE("FFDemux avformat_find_stream_info %s failed!", url);
return false;
}
//duration时间计数单位, 一秒计数1000000次, 这个数值不一定有, 因为可能信息在流里,不在封装的文件头中
this->totalMs = ic->duration/(AV_TIME_BASE)*1000;
XLOGI("total ms = %d", totalMs);
return true;
}
//读取一帧数据
XData FFDemux::Read() {
if (!ic){
return XData();
}
XData xData;
AVPacket *pkt = av_packet_alloc();
int re = av_read_frame(ic, pkt);
if (re != 0){
av_packet_free(&pkt);
return XData();
}
XLOGI("pack size %d pts %lld", pkt->size, pkt->pts);
xData.data = (unsigned char*)pkt;
xData.size = pkt->size;
return xData;
}
FFDemux::FFDemux() {
//这样线程不安全, 创建对象注意不要同时创建
static bool isFirst = true;
if (isFirst){
isFirst = false;
//注册所有封装器
av_register_all();
//注册所有解码器
avcodec_register_all();
//初始化网络
avformat_network_init();
XLOGI("register ffmpeg");
}
}