简介

OpenAtom OpenHarmony(以下简称“OpenHarmony”)作为“开源”世界的“连接器”,不断为智能社会的发展提供源源不断的“源动力”。深开鸿一直以来积极投身于OpenHarmony社区建设,不断推动开源事业的发展。

身为深开鸿的一名OS框架开发工程师,我在OpenHarmony 开源项目成立伊始便积极加入OpenHarmony 社区建设,负责OpenHarmony框架和结构的研发工作,此次我将带来OpenHarmony多媒体子系统的源码分析,希望能为广大的开发者提供参考。

OpenHarmony多媒体子系统,是OpenHarmony系统框架中的其中一个比较重要的子系统。OpenHarmony中集成了ffmpeg的第三方库,多媒体的很多功能实现需要ffmpeg库。另外,媒体文件的处理包含了对音视频裁剪、音视频分离等应用场景的处理,有些功能多媒体子系统没有提供给外部相应的接口,对此可以通过NAPI的机制实现一套JS接口,提供给应用层去调用,以此实现更多的多媒体功能 。

效果展示

本文通过实现音视频文件裁剪的功能,让开发者熟悉实现该功能的整个操作流程。以下是效果图:

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

首先选择源文件,在裁剪设置中设定裁剪的起始时间和结束时间(单位为秒),参数设定完以后,我们点击裁剪按钮,进而对源文件进行裁剪,裁剪成功后,会显示播放按钮。

在整个操作过程中,源文件选择模块的播放按钮是对源文件进行播放,裁剪模块的播放按钮是对裁剪后文件的播放,我们可以通过播放视频文件来查看裁剪前后的效果对比。

源码分析

源码分析分为两个部分,一部分是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)目录结构

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

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)流程图

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

(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应用实现

目录结构

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

代码主要包含两部分,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的方式来对外提供接口,以此实现更多功能。

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

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

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

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

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

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

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

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

鸿蒙开发面试真题(含参考答案)

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

OpenHarmony 开发环境搭建

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

《OpenHarmony源码解析》

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

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

OpenHarmony 设备开发学习手册

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