0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FFmpeg解封装WMV和M4V格式

OpenAtom OpenHarmony 来源:OpenAtom OpenHarmony 2026-01-21 12:57 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

开源鸿蒙具备多格式音视频播放能力,其播放器需依次完成解协议、解封装、解码、渲染四大核心步骤,方可将音视频内容完整呈现给用户;其中,解封装作为衔接协议解析与音视频解码的关键环节,具有极高的研究价值;本文将以扩展 WMV、M4V 格式解封装能力为实践切入点,带大家深入熟悉开源鸿蒙的多媒体框架。

开源鸿蒙多媒体解封装框架

1.1、HiStreamer播放器管道

播放管道构建:框架启动播放时,会先创建一条完整的播放管道,并按处理流程依次连接过滤器(Filter)、解封装器、解码器与渲染器,形成音视频数据的处理链路;

插件加载与注册:每个过滤器会根据待处理文件的格式,加载系统固定目录下以 so 文件形式存储的对应插件(Plugin);插件加载完成后,会自动触发注册接口调用,将自身的插件描述信息(如支持的格式、处理能力等)存入框架的插件列表中;

插件匹配与任务执行:当具体文件开始播放时,音视频数据会沿播放管道从左至右在各过滤器间流转。

每个过滤器会遍历插件列表中的描述信息,筛选并返回评分最高的插件,由该插件完成 “解协议”“解封装”“解码”“音视频渲染” 等对应处理任务。

1.2、HiStreamer解封装插件加载流程

插件加载主体:解封装插件的加载操作由PluginManager(插件管理器) 统一执行;

扩展前置条件:若要扩展解封装框架,首要步骤是在PluginList(插件列表) 中完成解封装插件描述信息的注册;

插件调度逻辑:当音视频数据流经解封装器时,框架会遍历 PluginList 中所有已注册的解封装插件,最终选取评分最高的插件执行解封装核心任务。

开源鸿蒙扩展M4V、WMV解封装能力

2.1、FFmpeg层扩展M4V、WMV的解封装能力

步骤1:修改FFmpeg ohos_config.sh配置,打开M4V、WMV的解封装能力。需要注意:M4V与MOV封装协议一致,WMV与ASF封装格式一致。

--enable-demuxer=mov,asf

步骤2:修改BUILD.gn,配置模块增加ASF宏定义。

"-DCONFIG_ASF_DEMUXER","-DCONFIG_MOV_DEMUXER",

步骤3: 查看libavformat模块MakeFile文件,确认M4V和WMV解封装涉及的文件。

OBJS-$(CONFIGMOVDEMUXER) += mov.omov_chan.omov_esds.oqtpalette.oreplaygain.odovi_isom.o
OBJS-$(CONFIGASFDEMUXER) += asfdec_f.oasf.oasfcrypt.o asf_tags.oavlanguage.o

步骤4:打开BUILD.gn中增加WMV和M4V的文件编译。

"//third_party/ffmpeg/libavformat/dovi_isom.c","//third_party/ffmpeg/libavformat/mov.c",
"//third_party/ffmpeg/libavformat/mov_chan.c","//third_party/ffmpeg/libavformat/mov_esds.c",
"//third_party/ffmpeg/libavformat/movenc.c","//third_party/ffmpeg/libavformat/qtpalette.c",
"//third_party/ffmpeg/libavformat/replaygain.c","//third_party/ffmpeg/libavformat/asf.c",
"//third_party/ffmpeg/libavformat/asfcrypt.c","//third_party/ffmpeg/libavformat/asfdec_f.c",
"//third_party/ffmpeg/libavformat/asfdec_o.c",

2.2、 多媒体框架层扩展WMV、M4V解封装能力

步骤1:修改PluginList,注册WMV、M4V解封装插件描述信息。

voidPluginList::AddMovDemuxerPlugin() {
 PluginDescription movDemuxerPlugin;
 movDemuxerPlugin.pluginName ="avdemux_mov,mp4,m4a,3gp,3g2,mj2";
 movDemuxerPlugin.packageName ="FFmpegDemuxer";
 movDemuxerPlugin.pluginType =PluginType::DEMUXER;
 movDemuxerPlugin.cap ="";
 movDemuxerPlugin.rank = DEFAULT_RANK;
 pluginDescriptionList_.push_back(movDemuxerPlugin);
}
voidPluginList::AddAsfDemuxerPlugin()
{
 PluginDescription asfDemuxerPlugin;
 asfDemuxerPlugin.pluginName ="avdemux_asf";
 asfDemuxerPlugin.packageName ="FFmpegDemuxer";
 asfDemuxerPlugin.pluginType =PluginType::DEMUXER;
 asfDemuxerPlugin.cap ="";
 asfDemuxerPlugin.rank = DEFAULT_RANK;
 pluginDescriptionList_.push_back(asfDemuxerPlugin);
}

步骤2:修改MediaType, 定义M4V和WMV的MediaType类型。

enumclassFileType: int32_t{
 M4V =111,
 WMV =112
}

步骤3:修改FFmpegFormatHelper,完善和M4V、WMV的探测逻辑。

FileTypeFFmpegFormatHelper::GetFileTypeByName(constAVFormatContext& avFormatContext) {
 FALSE_RETURN_V_MSG_E(avFormatContext.iformat != nullptr,FileType::UNKNOW,"Iformat is nullptr");
 constchar*fileName = avFormatContext.iformat->name;
 FileType fileType =FileType::UNKNOW;
 if(StartWith(fileName,"mov,mp4,m4a")) {
   if(StartWith(type->value,"m4v") ||StartWith(type->value,"M4V")) {
     fileType =FileType::M4V;
   }
 }else{
   if(g_convertFfmpegFileType.count(fileName) !=0) {
     fileType = g_convertFfmpegFileType[fileName];
   }
 }
returnfileType;
}

步骤4:修改MediaFileUtils,扩展图库支持M4V、WMV格式的显示。

staticconststd::unordered_map MEDIA_MIME_TYPE_MAP = {
{"video/x-ms-asf", {"asf","asx"}
},
{"video/mp4", {"m4v","f4v","mp4v","mpeg4","mp4"}
},
}

解封装性能优化

3.1、 插件加载优化

插件是以so的形式存储到本地,起播时需要加载注册解封装插件,对插件做Cache处理,已加载的插件直接从内存中获取,防止二次加载插件耗时。

std::shared_ptrCachedPluginPackage::GetPluginDef(PluginDescriptionpluginDescription)
{
 AutoLocklock(pluginMutex);
 std::vector>::iterator itPluginPackage;
 for(itPluginPackage = pluginPackageList_.begin();
   itPluginPackage != pluginPackageList_.end(); itPluginPackage++) {
   if(*itPluginPackage ==nullptr) {
     returnnullptr;
   }
   std::vector> pluginDefList = (*itPluginPackage)->GetAllPlugins();
   std::vector>::iterator itPluginDef;
   for(itPluginDef = pluginDefList.begin(); itPluginDef != pluginDefList.end(); itPluginDef++) {
     if(*itPluginDef ==nullptr) {
       returnnullptr;
     }
     if(strcmp((*itPluginDef)->name.c_str(), pluginDescription.pluginName.c_str()) ==0) {
       return(*itPluginDef);
     }
   }
 }
 std::shared_ptr pluginPackage = std::make_shared();
 boolret = pluginPackage->LoadPluginPackage(pluginDescription.packageName);
 if(!ret) {
   returnnullptr;
 }
 pluginPackageList_.push_back(pluginPackage);
 std::vector> pluginDefList = pluginPackage->GetAllPlugins();
 std::vector>::iterator itPluginDef;
 for(itPluginDef = pluginDefList.begin(); itPluginDef != pluginDefList.end(); itPluginDef++) {
   if(*itPluginDef ==nullptr) {
     returnnullptr;
   }
   if(strcmp((*itPluginDef)->name.c_str(), pluginDescription.pluginName.c_str()) ==0) {
     return(*itPluginDef);
   }
 }
 returnnullptr;
}

3.2、探测遍历优化

文件格式探测时,会遍历所有的demuxer,找出评分最高的demuxer,FFmpeg本身支持的demuxer比较多,限定探索范围为PluginList中已注册的demuxer, 可以加快起播速度。

std::stringPluginManagerV2::SnifferPlugin(PluginTypepluginType, std::shared_ptrdataSource)
{
 MEDIA_LOG_I("SnifferPlugin pluginType: "PUBLIC_LOG_D32, pluginType);
 std::vectormatchedPluginsDescriptions =
   PluginList::GetInstance().GetPluginsByType(pluginType);
 int maxProb =0;
 std::iterator it;
 PluginDescriptionbestMatchedPlugin;
for(it = matchedPluginsDescriptions.begin(); it != matchedPluginsDescriptions.end(); it++) {
   std::shared_ptrpluginDef = cachedPluginPackage_->GetPluginDef(*it);
   if(pluginDef != nullptr) {
     auto prob = pluginDef->GetSniffer()(pluginDef->name, dataSource);
     if(prob > maxProb) {
       maxProb = prob;
       bestMatchedPlugin = (*it);
     }
   }
 }
 returnbestMatchedPlugin.pluginName;
}

3.3、 限定探测文件大小

文件格式探测时,需要读取文件,然后解析相应的属性,并限定sniff的大小,防止过多读取导致的起播延时和内存开销。

intSniff(conststd::string& pluginName, std::shared_ptr dataSource)
{
 FALSE_RETURN_V_MSG_E(!pluginName.empty(),0,"Plugin name is empty");
 FALSE_RETURN_V_MSG_E(dataSource !=nullptr,0,"DataSource is nullptr");
 returnSniffWithSize(pluginName, dataSource, DEFAULT_SNIFF_SIZE);
}

3.4、 优化丢帧逻辑

目前的丢帧逻辑:HiStreamer管道有音画同步逻辑,音频管道解析较快,在sink阶段,会丢弃超时的渲染帧,这种丢帧逻辑其实无法提高解码和解封装的性能。

优化后的丢帧逻辑:在管道的Demuxer阶段,当探测到视频帧延时,丢弃非参考帧,提升视频管道的速度。

3.5、 优化内存

步骤一、HiStreamer管道中不同插件传递Buffer数据时使用共享内存, 减少帧复制。

步骤二、建立内存池,循环使用已分配好的内存,减少内存创建耗时。

步骤三、Demuxer解析后的数据直接放入解码器内存,不进行复制,减少耗时。

解封装能力测试

4.1 设计测试用例

用思维导图整理要测试的逻辑。

4.2 编写测试用例

步骤1、首先准备测试文件,使用FFMpeg探测文件中的帧数和关键帧。

执行ffprobe命令,获取总帧数2641。

ffprobe-verror-select_streams v:0-count_frames -show_entries stream=nb_read_frames wmv_wmv3_no.wmv

执行ffprobe命令,获取关键帧数为68。

ffprobe-loglevelerror-select_streams v:0-show_frames -show_entries frame=pict_type wmv_wmv3_no.wmv | grep -c pict_type=I

步骤2、打开avcodec工程,在unitest/demuxer_test目录单独创建m4v和wmv的单元测试文件, 编写测试用例。

HWTEST_F(DemuxerUnitTest,Demuxer_WMV_ReadSample_0001,TestSize.Level1)
{
 InitResource(g_wmvPath,LOCAL);
ASSERTTRUE(initStatus);
 ASSERT_EQ(demuxer_->SelectTrackByID(0),AV_ERR_OK);
 sharedMem_=AVMemoryMockFactory::CreateAVMemoryMock(bufferSize);
 ASSERT_NE(sharedMem_, nullptr);
 ASSERT_TRUE(SetInitValue());
 while(!isEOS(eosFlag)){
 for(autoidx:selectedTrackIds){
     ASSERT_EQ(demuxer_->ReadSample(idx,sharedMem,&info,flag),AV_ERR_OK);
     CountFrames(idx);
   }
 }
 printf("frames_[0]=%d | kFrames[0]=%d
", frames_[0], keyFrames_[0]);
ASSERTEQ(frames[0],2641);
ASSERTEQ(keyFrames[0],68);
 RemoveValue();
}

4.3 编译运行测试用例

步骤1、编译测试用例。

./build.sh --product-name rk3568 --ccache -fast-rebuild --build-target=demuxer_native_module_test

步骤2、生成产物推入板卡/bin目录。

rhdc shell mount -o remount,rw /
rhdc file send E:sharedemuxer_native_module_test /bin/

步骤3、将资源文件推入板卡。

rhdc file sendtest/moduletest/resources/demuxer data/test/media/

步骤4、执行测试用例。

demuxer_native_module_test--gtest_filter=Demuxer3gpFuncNdkTest.*
demuxer_native_module_test --gtest_filter=Demuxer3G2FuncNdkTest.*
demuxer_native_module_test --gtest_filter=DemuxerM4vFuncNdkTest.*
demuxer_native_module_test --gtest_filter=DemuxerWmvFuncNdkTest.*
demuxer_native_module_test --gtest_filter=DemuxerVobFuncNdkTest.*

总结

通过以上内容,不仅能帮助开发者深入理解开源鸿蒙多媒体框架的运行逻辑,熟练掌握多媒体解封装能力的扩展方法;还能针对解封装环节进行性能调优,并通过编写单元测试用例,从功能与性能双重维度,保障了解封装模块的鲁棒性与高效性。

供稿:李晓飞、傅巧妮、魏宏亮

责编:开发者与活动运营组 李健

编审:品牌管理组 丽娜

审核:开源鸿蒙项目群工作委员会执行总监 陶铭

开源鸿蒙项目群工作委员会执行秘书 曹云菲

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 封装
    +关注

    关注

    128

    文章

    9313

    浏览量

    148999
  • 音视频
    +关注

    关注

    4

    文章

    605

    浏览量

    31471
  • 开源
    +关注

    关注

    3

    文章

    4312

    浏览量

    46409
  • 鸿蒙
    +关注

    关注

    60

    文章

    3009

    浏览量

    46143

原文标题:拆·应用 | 第九期:基于FFmpeg解封装WMV和M4V格式

文章出处:【微信号:gh_e4f28cfa3159,微信公众号:OpenAtom OpenHarmony】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    《深入理解FFmpeg阅读体验》FFmpeg摄像头测试

    0% /dev/shm tmpfs 763.8M 184.0K 763.7M0% /tmp tmpfs 763.8M 468.0K 763.4M0% /run 然后测试下
    发表于 04-17 19:06

    专门录制屏幕动作的工具

    M4V、CAMV、MOV、RM、GIF 动画等多种常见格式,是制作视频演示的绝佳工具。 下载地址 :专门录制屏幕动作的工具 [此贴子已经被admin于2009-10-24 9:17:08编辑过]
    发表于 06-09 14:27

    ti8168 rdk mpeg4 解码异常

    ffmpeg的解码命令为: ffplay -i m4v -f  in_mp4_err2.mpg4,可以正常解码播放。 这个问题该如何解决?
    发表于 05-31 06:41

    怎么使用ffmpeg控制视频输出p格式(1080p50)或者i格式(1080i50)?

    我在使用ffmpeg指令接收udp视频流,然后通过blackmagic设备的SDI接口输出到显示器显示,但是视频默认是p格式的,我用-format_code Hi50设置视频格式为50i,参数没有
    发表于 08-07 09:05

    MK20DX256VLL10解封装测试失败/假冒嫌疑是什么意思?

    我们一直在拼命地通过经纪人找到一批 KINETIS ARM Cortex M4,确切代码 MK20DX256VLL10。官方经销商缺货。由于解封装测试存在问题,测试实验室向我们发送了一份结果为“失败
    发表于 03-15 07:23

    怎样查询在FFMPEG中所有被支持的bm hardware decoder?

    root ffmpeg -decoders | grep _bm V….. avs_bm bm AVS decoder wrapper (codec avs) V….. cavs_bm bm
    发表于 09-19 07:37

    M4V系列图示面板表的介绍

    M4V系列W75H25mm马赛克控制器专用图示面板表的特点:各种DC输入功能:0-2VDC,0-10VDC,1-5VDC,DC0-1mA,DC4-20mA、预设功能(High/Low缩放设定)、最大显示:-999~9999、错误显示功能和自诊断功能、内置高品质的微处理器。
    发表于 09-28 17:01 1次下载
    <b class='flag-5'>M4V</b>系列图示面板表的介绍

    视频的编码和格式的详细资料说明

    第一, AVI, WMV, MP4, MOV, MKV等等, 通通都是封装(即文件名),不是真正的格式。真正的格式取决于视频压缩编码。常用的
    发表于 01-09 08:00 47次下载
    视频的编码和<b class='flag-5'>格式</b>的详细资料说明

    2种将DAT转换为MP4WMV,MOV(QuickTime)的实用方法

    解决您的问题,您需要在Windows和Mac上将DAT视频转换为MP4WMV,MOV(QuickTime)和大多数其他流行的标准视频格式。本文将推荐一款出色的DAT视频转换器,以及在线和免费方法,可以
    的头像 发表于 12-08 11:39 2.4w次阅读

    M4V编辑器:如何在Windows/Mac上轻松编辑M4V文件

    要裁剪M4V视频中不需要的部分、设置边框、添加特殊效果并自定义以创建个人视频,您需要一个功能丰富的M4V编辑器。专业的视频编辑工具将帮助您快速和直观地编辑M4V文件,在本文中,您可以了解iTunes
    的头像 发表于 01-14 09:19 4963次阅读

    涨知识~Windows/Mac上推荐的11款WMV播放器

    WMV是最古老的数字视频格式之一。然而,尽管有许多开发出来的格式,但是WMV始终拥有着区别于其他格式的众多优势。这也使得它至今仍被不少用户选
    的头像 发表于 01-14 15:28 3738次阅读

    FFmpeg 6.0 发布

    FFmpeg 6.0 中新的解码器包括有 Bonk、RKA、Radiance、SC-4、APAC、VQC、WavArc 和一些 ADPCM 格式,且 QSV 和 NVenc 现在支持 AV1 编码。
    的头像 发表于 03-06 09:55 1893次阅读

    Pydub:一个基于ffmpeg的Python音频处理模块

    Pydub是一个基于ffmpeg的Python音频处理模块,封装了许多ffmpeg底层接口,因此用它来做音乐歌曲文件格式转换会非常方便。 如果你阅读过我们之前的文章:《 剪辑音乐要很久
    的头像 发表于 10-21 10:40 3498次阅读

    Pydub音乐文件格式转换功能介绍

    Pydub是一个基于ffmpeg的Python音频处理模块,封装了许多ffmpeg底层接口,因此用它来做音乐歌曲文件格式转换会非常方便。 如果你阅读过我们之前的文章:《 剪辑音乐要很久
    的头像 发表于 10-31 14:51 2007次阅读

    如何基于FFmpeg解码WMV3视频

    在音视频开发的世界里,WMV3 就像一位沉静的老友——它曾是 Windows Media 时代的主角,如今虽已淡出主流视野,却仍在企业录像、历史资料、监控存档中默默守候;而 FFmpeg,这位开源世界的“瑞士军刀”,正是我们与这位老友对话的最佳翻译官。
    的头像 发表于 12-25 09:29 562次阅读
    如何基于<b class='flag-5'>FFmpeg</b>解码<b class='flag-5'>WMV</b>3视频