乐趣区

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

简介
OpenAtom OpenHarmony(以下简称“OpenHarmony”)作为“开源”世界的“连接器”,一直为智能社会的倒退提供源源不断的“源能源”。深开鸿始终以来踊跃投身于 OpenHarmony 社区建设,一直推动开源事业的倒退。
身为深开鸿的一名 OS 框架开发工程师,我在 OpenHarmony 开源我的项目成立伊始便踊跃退出 OpenHarmony 社区建设,负责 OpenHarmony 框架和构造的研发工作,此次我将带来 OpenHarmony 多媒体子系统的源码剖析,心愿能为宽广的开发者提供参考。
OpenHarmony 多媒体子系统,是 OpenHarmony 零碎框架中的其中一个比拟重要的子系统。OpenHarmony 中集成了 ffmpeg 的第三方库,多媒体的很多性能实现须要 ffmpeg 库。另外,媒体文件的解决蕴含了对音视频裁剪、音视频拆散等利用场景的解决,有些性能多媒体子系统没有提供给内部相应的接口,对此能够通过 NAPI 的机制实现一套 JS 接口,提供给应用层去调用,以此实现更多的多媒体性能。
成果展现
本文通过实现音视频文件裁剪的性能,让开发者相熟实现该性能的整个操作流程。以下是效果图:

首先抉择源文件,在裁剪设置中设定裁剪的起始工夫和完结工夫(单位为秒),参数设定完当前,咱们点击裁剪按钮,进而对源文件进行裁剪,裁剪胜利后,会显示播放按钮。
在整个操作过程中,源文件抉择模块的播放按钮是对源文件进行播放,裁剪模块的播放按钮是对裁剪后文件的播放,咱们能够通过播放视频文件来查看裁剪前后的成果比照。
代码曾经上传至 SIG 仓库,链接如下:
https://gitee.com/openharmony…
https://gitee.com/openharmony…

源码剖析

源码剖析分为两个局部,一部分是 NAPI 实现的本地性能,另一部分是 JS 实现的利用性能。一、NAPI一、实现
以下是源码剖析的内容,外围的模块次要代码是 myffmpegsys,为利用端提供了 js 的接口。
1. myffmpegsys 作为一个新的子系统集成到 OpenHarmony 源码中,搁置在 OpenHarmony 源码的根目录下,和 foundation 在同一目录下。
2. 配置 build/subsystem_config.json。

 "myffmpegsys": {
             "path": "myffmpegsys",
            "name": "myffmpegsys"
        },
  1. 配置产品的 productdefine/common/products/XXXX.json(其中 XXXX 对应的设施型号)。
"parts":{"myffmpegsys:myffmpegpart":{},
     "ace:ace_engine_standard":{},
     ......
  }
  1. 配置好子系统以及对应的组件后,上面再对 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 的形式来对外提供接口,以此实现更多功能。
开发工作是一条漫长的路线,开发者唯有触类旁通、举一反三,能力在将来的开发工作中达到事倍功半的成果。

退出移动版