FFDemux的Open實(shí)現(xiàn)打開媒體文件
FFdemux的Read讀取幀數(shù)據(jù)接口編寫
主要是增加了三個(gè)類XData, IDemux, FFDemux, 具體實(shí)現(xiàn)和作用如下
數(shù)據(jù)類:XData
- 該類用于存儲各個(gè)線程生產(chǎn)的需要交互的數(shù)據(jù)
- unsigned char *data 存儲的數(shù)據(jù)的地址
- int size 存儲的數(shù)據(jù)的大小
- 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巢寡,即適配器模式的目標(biāo)抽象類,定義了客戶所需的端口
- virtual bool Open(const char *url):接口打開文件或者流媒體鏈接书斜,并進(jìn)行解封裝,獲取音視頻基本信息(如封裝格式症杏、編碼格式和視頻時(shí)長等等)今豆,傳入?yún)?shù)為本地文件地址或流媒體url, 打開成功返回true崎页,失敗則返回false
- virtual XData Read():該接口負(fù)責(zé)讀取一幀的數(shù)據(jù)并返回出來阁苞,主要是將
//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;
//讀取一幀數(shù)據(jù),數(shù)據(jù)由調(diào)用者清理
virtual XData Read() = 0;
//總時(shí)長(單位ms)
int totalMs = 0;
};
#endif //BOPLAY_IDEMUX_H
//IDemux.cpp
#include "IDemux.h"
解封裝的實(shí)現(xiàn)類:FFDemux困檩,即適配器模式中的適配器類,適配器可以調(diào)用另一個(gè)接口那槽,作為一個(gè)轉(zhuǎn)換器悼沿,對Adaptee和Target進(jìn)行適配,適配器類是適配器模式的核心骚灸,在對象適配器中糟趾,它通過繼承Target并關(guān)聯(lián)一個(gè)Adaptee對象使二者產(chǎn)生聯(lián)系,F(xiàn)FDemux調(diào)用了ffmpeg
//FFDemux.h
#ifndef BOPLAY_FFDEMUX_H
#define BOPLAY_FFDEMUX_H
#include "IDemux.h"
//聲明, 不引用頭文件, 封裝后調(diào)用者不需要知道ffmpeg的頭文件
struct AVFormatContext;
class FFDemux: public IDemux{
public:
//打開文件或者流媒體 rtmp http rtsp
virtual bool Open(const char *url);
//讀取一幀數(shù)據(jù),數(shù)據(jù)由調(diào)用者清理
virtual XData Read();
FFDemux();
private:
//只有在無參數(shù)構(gòu)造函數(shù)時(shí), 這個(gè)賦值才會生效
//AVFormatContext描述了一個(gè)媒體文件或媒體流的構(gòu)成和基本信息
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時(shí)間計(jì)數(shù)單位, 一秒計(jì)數(shù)1000000次, 這個(gè)數(shù)值不一定有, 因?yàn)榭赡苄畔⒃诹骼锷跎辉诜庋b的文件頭中
this->totalMs = ic->duration/(AV_TIME_BASE)*1000;
XLOGI("total ms = %d", totalMs);
return true;
}
//讀取一幀數(shù)據(jù)
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() {
//這樣線程不安全, 創(chuàng)建對象注意不要同時(shí)創(chuàng)建
static bool isFirst = true;
if (isFirst){
isFirst = false;
//注冊所有封裝器
av_register_all();
//注冊所有解碼器
avcodec_register_all();
//初始化網(wǎng)絡(luò)
avformat_network_init();
XLOGI("register ffmpeg");
}
}