写点什么

如何通过 OpenHarmony 系统中集成的 ffmpeg 库和 NAPI 机制,实现更多的多媒体功能?

  • 2022 年 7 月 12 日
  • 本文字数:3748 字

    阅读完需:约 12 分钟

如何通过OpenHarmony系统中集成的ffmpeg库和NAPI机制,实现更多的多媒体功能?

简介


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 < 0) {        ERROR_BUF(ret);        HiLog::Error(LABEL, "gyf avformat_alloc_output_context2 error = %{public}s", errbuf);        goto end;    }     ofmt = ofmt_ctx->oformat;
复制代码


根据输入流创建输出流,并且拷贝 codec 参数:

//创建流以及参数拷贝    for (int i = 0; i < (int)ifmt_ctx->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 < 0) {            break;        }
in_stream = ifmt_ctx->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 的方式来对外提供接口,以此实现更多功能。


开发工作是一条漫长的道路,开发者唯有举一反三、触类旁通,才能在未来的开发工作中达到事半功倍的效果。


用户头像

OpenHarmony开发者官方账号 2021.12.15 加入

OpenHarmony是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展

评论

发布
暂无评论
如何通过OpenHarmony系统中集成的ffmpeg库和NAPI机制,实现更多的多媒体功能?_OpenHarmony_OpenHarmony开发者社区_InfoQ写作社区