前一篇文章说过,为了在iOS支持dash播放,我把dash从Cicada的源码中迁移到了我们的项目里,并嵌入到demux功能,这里讲下具体的实施过程。
准备工作
- 首先,需要了解下Cicada的项目结构
framework里面的结构
上图中红框内的是我们需要迁移的内容,其中utils里面跟android和linux相关的我们可以删掉,用不着,另外还有一些文件名是xxtest的,也需要删掉,里面有main函数,会跟我们项目中的main函数冲突。
- 其次,需要编译好第三方库并添加到项目中,主要就是libcurl,其他的像ffmpeg和openssl,一般做音视频的都已经有了。
- 配置宏, 包括ENABLE_DASH_DEMUXER,ENABLE_HLS_DEMUXER,ENABLE_CURL_SOURCE
- 配置 search path。 除了常规引用第三方库必须依赖的header文件夹以外,由于Cicada有一些代码直接依赖于ffmpeg的源码,因此需要在我们项目的header search path中配置ffmpeg源码的路径,也就是Cicada目录下的external/external文件夹。如果你是直接拖第三方库进来的,还需要配置下library search path
迁移文件及解决编译报错
- 首先把相关文件拷贝到iOS的工程里
- 其次是删除掉无用的代码及有冲突的代码,比如android和linux相关的代码,这个很难一一列举,只能编译时根据提示慢慢去找
- 修改#include ,把<>改为""。 由于原工程是把ffmpeg及相关模块都打成静态库的方式进行依赖的,所以代码里面很多文件是用尖括号的方式进行引用的,我们都需要改成双引号。
- 将ffmpeg中所有用到AVMediaType的地方都改个名字,比如叫FFAVMediaType。这是因为引入了C++,导致ffmpeg里面的AVMediaType跟系统的AVMediaType冲突了。
- 由于引入了C++,有一些引用了C的代码的地方或者本身是C文件的可能还需要增加类似下图这种的宏定义来区分C代码与C++代码,避免编译报错
接入Demuxer service
我们解析dash视频,首先是从 Demuxer service开始。Demuxer service是解析的核心。解析的大致流程是:先调用datasource下载视频,然后根据视频类型创建不同的demuxer,比如dash就是使用playlistDemuxer,一般的视频则是使用avformatDemuxer。playlistDemuxer内部会调用dashManager解析dash的mpd文件,拿到segmentTemplate后,又回调用datasource下载分段的视频,并调用dashStream解析视频流。
我接入的代码步骤如下:
- 创建datasource
IDataSource *dataSource = dataSourcePrototype::create(url.UTF8String);
if (!dataSource) {
completion(false);
return;
}
self->mDataSource = dataSource;
ret = dataSource->Open(0);
if (ret<0) {
completion(false);
return;
}
2. 创建并初始化demuxer service
**self**->mDemuxerService = **static_cast**<unique_ptr<demuxer_service>>(**new** demuxer_service(dataSource));
ret = **self**->mDemuxerService->createDemuxer(demuxer_type_unknown);
**if** (ret<0) {
completion(**false**);
**return**;
}
**self**->mDemuxerService->getDemuxerHandle()->setBitStreamFormat(header_type::header_type_extract, header_type::header_type_extract);
ret = **self**->mDemuxerService->initOpen(demuxer_type_playlist);
**if** (ret<0) {
completion(**false**);
**return**;
}
- 获取流的元数据并设置缓存等
**int** nbStream = **self**->mDemuxerService->GetNbStreams();
unique_ptr<streamMeta> pMeta;
**int** bandWidthNearStreamIndex = -1;
**int** minBandWidthDelta = INT_MAX;
**int** mDefaultBandWidth = 0;
**int** videoStreamCount = 0;
printf("Demuxer service get nubmer streams is %d", nbStream);
**for** (**int** i = 0; i < nbStream; ++i) {
**self**->mDemuxerService->GetStreamMeta(pMeta, i, **false**);
**auto** *meta = (Stream_meta *) (pMeta.get());
**self**.duration = meta->duration;
**if** (meta->type == STREAM_TYPE_MIXED || meta->type == STREAM_TYPE_VIDEO) {
videoStreamCount++;
**int** metaBandWidth = (**int**) meta->bandwidth;
**if** (abs(mDefaultBandWidth - metaBandWidth) < minBandWidthDelta) {
bandWidthNearStreamIndex = i;
minBandWidthDelta = abs(mDefaultBandWidth - metaBandWidth);
}
}
}
**if** ( videoStreamCount > 1) {
**if** (dataSource) {
dataSource->enableCache(url.UTF8String, **false**);
}
} **else** {
**if** (dataSource) {
dataSource->enableCache(url.UTF8String, **true**);
}
}
- 打开流
**for** (**int** i = 0; i < nbStream; ++i) {
**int** openStreamRet = 0;
**self**->mDemuxerService->GetStreamMeta(pMeta, i, **false**);
**auto** *meta = (Stream_meta *) (pMeta.get());
**if**(meta->type == STREAM_TYPE_VIDEO){
**if** (bandWidthNearStreamIndex == i) {
printf("get a video stream\n");
openStreamRet = **self**->mDemuxerService->OpenStream(i);
**self**->video_index = i;
}
}**else** **if** (meta->type == STREAM_TYPE_AUDIO){
printf("get a audio stream\n");
openStreamRet = **self**->mDemuxerService->OpenStream(i);
**self**->audio_index = i;
}
**if** (openStreamRet < 0) {
completion(**false**);
**return**;
}
}
5.开启demuxer service
**self**->mDemuxerService->start();
- 开启一个while循环,从demuxer service中获取packet
AVPacket *pkt = **nil**;
IAFPacket *pPacket = **nullptr**;
std::unique_ptr<IAFPacket> pMedia_Frame{};
**if** (mDemuxerService == **nullptr**) {
assert(0);
}
**int** index = -1;
ret = mDemuxerService->readPacket(pMedia_Frame, index);
**if** (pMedia_Frame == **nullptr**) {
// AF_LOGD("Can't read packet %d\n", ret);
**if** (ret == 0) {
**if** (index != -1) {
ret = -EAGAIN;
}
}
}
**if** (ret == 0) {
pkt = av_packet_alloc();
[AVPacketUtil setNewRecordFlag:NewRecordFlagEnd forPacket:pkt];
[**self** pushPacket:pkt];
// [self freeContext];
**break**;
}**else** **if** (pMedia_Frame == **nullptr**) {
// av_packet_free(&pkt);
**continue**;
}
- 设置packet的关键数据,如extraData,pts等
pPacket = pMedia_Frame.get();
**if** (pPacket->getInfo().streamIndex == video_index) {
**if** (/*mAdaptiveVideo &&*/ pPacket->getInfo().flags) {
std::unique_ptr<streamMeta> meta;
mDemuxerService->GetStreamMeta(meta, pPacket->getInfo().streamIndex, **false**);
Stream_meta *pMeta = (Stream_meta*)*meta;
pPacket->setExtraData(pMeta->extradata, pMeta->extradata_size);
**if** (!mVideoStreamMeta) {
**self**->mVideoStreamMeta = (Stream_meta*)malloc(**sizeof**(Stream_meta));
memcpy(mVideoStreamMeta, &(*meta), **sizeof**(Stream_meta));
mVideoStreamMeta->extradata = (uint8_t*)malloc(pMeta->extradata_size);
memcpy(mVideoStreamMeta->extradata, pMeta->extradata, pMeta->extradata_size);
}
}
}**else** **if** (pPacket->getInfo().streamIndex == audio_index) {
std::unique_ptr<streamMeta> meta;
mDemuxerService->GetStreamMeta(meta, pPacket->getInfo().streamIndex, **false**);
Stream_meta *pMeta = (Stream_meta*)*meta;
**if** (!mAudioStreamMeta) {
**self**->mAudioStreamMeta = (Stream_meta*)malloc(**sizeof**(Stream_meta));
memcpy(mAudioStreamMeta, pMeta, **sizeof**(Stream_meta));
mAudioStreamMeta->extradata = (uint8_t*)malloc(pMeta->extradata_size);
memcpy(mAudioStreamMeta->extradata, pMeta->extradata, pMeta->extradata_size);
}
}
**if** (pPacket) {
**auto** *avAFPacket = **dynamic_cast**<AVAFPacket *>(pPacket);
pkt = av_packet_clone(avAFPacket->ToAVPacket());
pkt->pts = pPacket->getInfo().pts;
pkt->dts = pPacket->getInfo().dts;
pkt->stream_index = pPacket->getInfo().streamIndex;
}
**if** (!pkt) {
**continue**;
}
- 最后,就是将pkt传给解码器了,这里比较简单,就不贴代码了,
这里有几个点需要注意: 1)所有C++对象都是通过assign属性持有的,像mVideoStreamMeta这些,他们随时可能被销毁,所以使用时记得加上空判断,如果确定不用的时候,要把他置空。特别是metaInfo,本身就是从service 内部获取,不是由我们创建,所以更需要注意,如果需要长期持有,建议new一份,把需要字段copy出来。 2)我们自己创建的对象需要我们手动释放,包括demuxer service和data source,这里贴下代码,仅供参考
**if** (mDemuxerService) {
mDemuxerService->interrupt(1);
**if** (mDataSource) {
mDataSource->Interrupt(**true**);
}
mDemuxerService->stop();
mDemuxerService->close();
**if** (audio_index >= 0) {
mDemuxerService->CloseStream(audio_index);
}
**if** (video_index >= 0) {
mDemuxerService->CloseStream(video_index);
}
}
**if** (mDataSource) {
mDataSource->Close();
**delete** mDataSource;
mDataSource = **nullptr**;
}
**if** (mVideoStreamMeta) {
**if** (mVideoStreamMeta->extradata) {
free(mVideoStreamMeta->extradata);
}
free(mVideoStreamMeta);
mVideoStreamMeta = **nil**;
}
**if** (mAudioStreamMeta){
**if** (mAudioStreamMeta->extradata) {
free(mAudioStreamMeta->extradata);
}
free(mAudioStreamMeta);
mAudioStreamMeta = **nil**;
}
这些全部写完之后,我们的工作就差不多完成了。有不明白的地方欢迎提问。