OpenHarmony多媒体子系统的源码分析

简介
openatom openharmony(以下简称“openharmony”)作为“开源”世界的“连接器”,不断为智能社会的发展提供源源不断的“源动力”。深开鸿一直以来积极投身于openharmony社区建设,不断推动开源事业的发展。
身为深开鸿的一名os框架开发工程师,我在openharmony 开源项目成立伊始便积极加入openharmony 社区建设,负责openharmony框架和结构的研发工作,此次我将带来openharmony多媒体子系统的源码分析,希望能为广大的开发者提供参考。
openharmony多媒体子系统,是openharmony系统框架中的其中一个比较重要的子系统。openharmony中集成了ffmpeg的第三方库,多媒体的很多功能实现需要ffmpeg库。另外,媒体文件的处理包含了对音视频裁剪、音视频分离等应用场景的处理,有些功能多媒体子系统没有提供给外部相应的接口,对此可以通过napi的机制实现一套js接口,提供给应用层去调用,以此实现更多的多媒体功能。
效果展示
本文通过实现音视频文件裁剪的功能,让开发者熟悉实现该功能的整个操作流程。
以下是效果图:
首先选择源文件,在裁剪设置中设定裁剪的起始时间和结束时间(单位为秒),参数设定完以后,我们点击裁剪按钮,进而对源文件进行裁剪,裁剪成功后,会显示播放按钮。
在整个操作过程中,源文件选择模块的播放按钮是对源文件进行播放,裁剪模块的播放按钮是对裁剪后文件的播放,我们可以通过播放视频文件来查看裁剪前后的效果对比。
代码已经上传至sig仓库,链接如下:
https://gitee.com/openharmony-sig/knowledge_demo_entainment/tree/master/fa/mediacutedemo
https://gitee.com/openharmony-sig/knowledge_demo_entainment/tree/master/docs/mediacutedemo
源码分析
源码分析分为两个部分,一部分是napi实现的本地功能,另一部分是js实现的应用功能。
一、napi实现
以下是源码分析的内容,核心的模块主要代码是myffmpegsys,为应用端提供了js的接口。
1. myffmpegsys作为一个新的子系统集成到openharmony源码中,放置在openharmony源码的根目录下,和foundation在同一目录下。
2. 配置build/subsystem_config.json。
        myffmpegsys: { path: myffmpegsys, name: myffmpegsys },  
3. 配置产品的productdefine/common/products/xxxx.json(其中xxxx对应的设备型号)。
          parts:{ myffmpegsys:myffmpegpart:{}, ace:ace_engine_standard:{}, ...... }  
4. 配置好子系统以及对应的组件后,下面再对myffmpegsys子系统的源码进行分析。
(1)目录结构
myffmpegdemo中主要处理napi相关的接口转换,ffmpeg_utils通过调用ffmpeg三方库处理实际的视频文件裁剪功能。
(2)openharmony集成的ffmpeg三方库的路径是third_party/ffmpeg,myffmpegdemo会依赖ffmpeg,并且头文件也会引用ffmpeg头文件,所以在build.gn文件中会添加相关的依赖和路径。
                                            import(//build/ohos.gni)ohos_shared_library(myffmpegdemo) { include_dirs = [ //foundation/ace/napi/interfaces/kits, //myffmpegsys/myffmpegpart/myffmpegdemo/include, //third_party/ffmpeg, ] sources = [ myffmpegdemo.cpp, ffmpeg_utils.cpp, ] public_deps = [ //foundation/ace/napi:ace_napi, //third_party/ffmpeg:libohosffmpeg ] external_deps = [ hiviewdfx_hilog_native:libhilog, ] relative_install_dir = module subsystem_name = myffmpegsys part_name = myffmpegpart}  
(3)流程图
(4)代码分析
napi接口注册:
                        /*********************************************** * module export and register ***********************************************/static napi_value registermyffmpegdemo(napi_env env, napi_value exports){ static napi_property_descriptor desc[] = { declare_napi_function(videocute, videocute), declare_napi_function(videotoaach264, videotoaach264), }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports;}  
napi实现videocute接口,将napi类型转换成c++类型,然后调用ffmpegutils的videocute接口:
                static void executevideocute(napi_env env, void* data) { videocuteaddondata *addondata = (videocuteaddondata *) data; //调用视频剪切的功能 addondata->result = ffmpegutils::videocute((const char*)addondata->args0.c_str(), addondata->args1, addondata->args2, (const char*)addondata->args3.c_str());}  
ffmpegutils初始化输入,输出格式上下文:
                            //初始化上下文 ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0); if (ret < 0) { error_buf(ret); hilog::error(label, gyf avformat_open_input error = %{public}s, errbuf); return ret; } ret = avformat_alloc_output_context2(&ofmt_ctx, null, null, out_filename); if (ret oformat;  
根据输入流创建输出流,并且拷贝codec参数:
                      //创建流以及参数拷贝 for (int i = 0; i nb_streams; i++) { in_stream = ifmt_ctx->streams[i]; avstream *out_stream = avformat_new_stream(ofmt_ctx, null); if (!out_stream) { ret = averror_unknown; goto end; } avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); out_stream->codecpar->codec_tag = 0; }  
打开输出文件,并写入头文件:
                          //打开输出文件 ret = avio_open(&ofmt_ctx->pb, out_filename, avio_flag_write); if (ret < 0) { error_buf(ret); hilog::error(label, gyf avio_open error = %{public}s, errbuf); goto end; } // 写头信息 ret = avformat_write_header(ofmt_ctx, null); if (ret < 0) { error_buf(ret); hilog::error(label, gyf avformat_write_header error = %{public}s, errbuf); goto end; }  
根据设置的截取时间段,跳转到指定帧:
              //跳转到指定帧 ret = av_seek_frame(ifmt_ctx, -1, start_seconds * av_time_base, avseek_flag_any); if (ret < 0) { error_buf(ret); hilog::error(label, gyf av_seek_frame error = %{public}s, errbuf); goto end; }  
循环读取帧数据,当达到截取时间点后,退出循环:
                            //读取数据 ret = av_read_frame(ifmt_ctx, &pkt); if (ret streams[pkt.stream_index]; out_stream = ofmt_ctx->streams[pkt.stream_index]; // 时间超过要截取的时间,就退出循环 if (av_q2d(in_stream->time_base) * pkt.pts > end_seconds) { av_packet_unref(&pkt); break; }  
写入文件尾部信息:
     //写文件尾信息 ret = av_write_trailer(ofmt_ctx);  
二、js应用实现
目录结构
代码主要包含两部分,index主要是裁剪相关的设置,player是针对视频文件进行播放的页面。
index中设置了源文件,裁剪的起始时间,结束时间以后,通过裁剪按钮,进行视频的裁剪功能,这一部分的代码是通过底层napi提供的接口进行的。
cutevideo() { globalthis.iscutesuccess = false; console.log('gyf cutevideo'); myffmpegdemo.videocute(this.src, this.starttime, this.endtime, this.srcout, function (result) { console.log('gyf cutevideo callback result = ' + result); globalthis.showprompt('videocute finished!'); if (0 === result) { globalthis.iscutesuccess = true; } else { globalthis.iscutesuccess = false; } } );},  
视频一旦裁剪成功以后,页面就会出现播放的按钮,点击播放按钮后,便可对裁剪后的文件进行观看。
总结
本文通过napi方式给大家讲解了如何利用openharmony系统能力实现更多的功能。开发者可以利用openharmony自带的三方库,实现音视频分离、音视频转码、音视频编解码等多媒体处理功能,而且这些功能都可以在系统层实现,并通过napi的方式提供对应的接口进行调用。对于openharmony集成的其他内在的能力,也可以通过napi的方式来对外提供接口,以此实现更多功能。
开发工作是一条漫长的道路,开发者唯有举一反三、触类旁通,才能在未来的开发工作中达到事半功倍的效果。


全球最大钢制传感器生产企业柯力传感发布2022第一季度报告
泰克频谱分析仪荣获2009年“最佳测试产品”奖
软件中的硬件问题怎样解决比较好
2022年下半年全球半导体芯片短缺将得到缓解
一名嵌入式软件工程师对生活的重新思考
OpenHarmony多媒体子系统的源码分析
IR2117内部结构及工作原理
HDMI继续演进 泰克提供高效方案
火焰图系列之使用火焰图隐藏功能提高绘制精度
PCB的板级电磁兼容问题
盘点国产芯片品牌产家
AI视觉检测的应用价值都有哪些
时辰已到,互道珍重 高通放弃收购恩智浦后的第一天
什么是人工智能处理器
ADI公司推出了第四代宽带RF收发器ADRV9026
TCP IP转EtherCAT网关以太网和TCP协议区别
未来五年MCU的消费电子市场复合年度增长率将达10%
汇总几种典型主动均衡电路
关于标本对自动血凝仪检测结果的影响
全新车规级MCU GD32A503正式发布