如何透過OpenHarmony系統中整合的ffmpeg庫和NAPI機制,實現更多的多媒體功能?

OpenHarmony開發者社群發表於2022-07-12

簡介

OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)作為“開源”世界的“聯結器”,不斷為智慧社會的發展提供源源不斷的“源動力”。深開鴻一直以來積極投身於OpenHarmony社群建設,不斷推動開源事業的發展。

身為深開鴻的一名OS框架開發工程師,我在OpenHarmony 開源專案成立伊始便積極加入OpenHarmony 社群建設,負責OpenHarmony框架和結構的研發工作,此次我將帶來OpenHarmony多媒體子系統的原始碼分析,希望能為廣大的開發者提供參考。

OpenHarmony多媒體子系統,是OpenHarmony系統框架中的其中一個比較重要的子系統。OpenHarmony中整合了ffmpeg的第三方庫,多媒體的很多功能實現需要ffmpeg庫。另外,媒體檔案的處理包含了對音影片裁剪、音影片分離等應用場景的處理,有些功能多媒體子系統沒有提供給外部相應的介面,對此可以透過NAPI的機制實現一套JS介面,提供給應用層去呼叫,以此實現更多的多媒體功能 。

效果展示

本文透過實現音影片檔案裁剪的功能,讓開發者熟悉實現該功能的整個操作流程。以下是效果圖:

               

首先選擇原始檔,在裁剪設定中設定裁剪的起始時間和結束時間(單位為秒),引數設定完以後,我們點選裁剪按鈕,進而對原始檔進行裁剪,裁剪成功後,會顯示播放按鈕。

在整個操作過程中,原始檔選擇模組的播放按鈕是對原始檔進行播放,裁剪模組的播放按鈕是對裁剪後檔案的播放,我們可以透過播放影片檔案來檢視裁剪前後的效果對比。

程式碼已經上傳至SIG倉庫,連結如下:

原始碼分析

原始碼分析分為兩個部分,一部分是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的方式來對外提供介面,以此實現更多功能。

開發工作是一條漫長的道路,開發者唯有舉一反三、觸類旁通,才能在未來的開發工作中達到事半功倍的效果。

               




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011554/viewspace-2905417/,如需轉載,請註明出處,否則將追究法律責任。

相關文章