?
?
?
簡(jiǎn)介
?
OpenAtom OpenHarmony(以下簡(jiǎn)稱“OpenHarmony”)作為“開源”世界的“連接器”,不斷為智能社會(huì)的發(fā)展提供源源不斷的“源動(dòng)力”。深開鴻一直以來積極投身于OpenHarmony社區(qū)建設(shè),不斷推動(dòng)開源事業(yè)的發(fā)展。
?
身為深開鴻的一名OS框架開發(fā)工程師,我在OpenHarmony 開源項(xiàng)目成立伊始便積極加入OpenHarmony 社區(qū)建設(shè),負(fù)責(zé)OpenHarmony框架和結(jié)構(gòu)的研發(fā)工作,此次我將帶來OpenHarmony多媒體子系統(tǒng)的源碼分析,希望能為廣大的開發(fā)者提供參考。
?
OpenHarmony多媒體子系統(tǒng),是OpenHarmony系統(tǒng)框架中的其中一個(gè)比較重要的子系統(tǒng)。OpenHarmony中集成了ffmpeg的第三方庫,多媒體的很多功能實(shí)現(xiàn)需要ffmpeg庫。另外,媒體文件的處理包含了對(duì)音視頻裁剪、音視頻分離等應(yīng)用場(chǎng)景的處理,有些功能多媒體子系統(tǒng)沒有提供給外部相應(yīng)的接口,對(duì)此可以通過NAPI的機(jī)制實(shí)現(xiàn)一套JS接口,提供給應(yīng)用層去調(diào)用,以此實(shí)現(xiàn)更多的多媒體功能。
?
效果展示
?
本文通過實(shí)現(xiàn)音視頻文件裁剪的功能,讓開發(fā)者熟悉實(shí)現(xiàn)該功能的整個(gè)操作流程。
?
以下是效果圖:
?

?
?
?首先選擇源文件,在裁剪設(shè)置中設(shè)定裁剪的起始時(shí)間和結(jié)束時(shí)間(單位為秒),參數(shù)設(shè)定完以后,我們點(diǎn)擊裁剪按鈕,進(jìn)而對(duì)源文件進(jìn)行裁剪,裁剪成功后,會(huì)顯示播放按鈕。
?
在整個(gè)操作過程中,源文件選擇模塊的播放按鈕是對(duì)源文件進(jìn)行播放,裁剪模塊的播放按鈕是對(duì)裁剪后文件的播放,我們可以通過播放視頻文件來查看裁剪前后的效果對(duì)比。
?
代碼已經(jīng)上傳至SIG倉(cāng)庫,鏈接如下:
?
https://gitee.com/openharmony-sig/knowledge_demo_entainment/tree/master/FA/MediaCuteDemo
?
https://gitee.com/openharmony-sig/knowledge_demo_entainment/tree/master/docs/MediaCuteDemo
?
源碼分析
?
源碼分析分為兩個(gè)部分,一部分是NAPI實(shí)現(xiàn)的本地功能,另一部分是JS實(shí)現(xiàn)的應(yīng)用功能。
?
一、NAPI實(shí)現(xiàn)
?
以下是源碼分析的內(nèi)容,核心的模塊主要代碼是myffmpegsys,為應(yīng)用端提供了js的接口。
?
1. myffmpegsys作為一個(gè)新的子系統(tǒng)集成到OpenHarmony源碼中,放置在OpenHarmony源碼的根目錄下,和foundation在同一目錄下。
?
2. 配置build/subsystem_config.json。
- ?
- ?
- ?
- ?
"myffmpegsys":?{"path": "myffmpegsys","name": "myffmpegsys"},
?
3. 配置產(chǎn)品的productdefine/common/products/XXXX.json(其中XXXX對(duì)應(yīng)的設(shè)備型號(hào))。
- ?
- ?
- ?
- ?
- ?
"parts":{"myffmpegsys:myffmpegpart":{},"ace:ace_engine_standard":{},......}
?
4. 配置好子系統(tǒng)以及對(duì)應(yīng)的組件后,下面再對(duì)myffmpegsys子系統(tǒng)的源碼進(jìn)行分析。
?
? (1)目錄結(jié)構(gòu)
?

?
myffmpegdemo中主要處理napi相關(guān)的接口轉(zhuǎn)換,ffmpeg_utils通過調(diào)用ffmpeg三方庫處理實(shí)際的視頻文件裁剪功能。
?
(2)OpenHarmony集成的ffmpeg三方庫的路徑是third_party/ffmpeg,myffmpegdemo會(huì)依賴ffmpeg,并且頭文件也會(huì)引用ffmpeg頭文件,所以在BUILD.gn文件中會(huì)添加相關(guān)的依賴和路徑。
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
import("//build/ohos.gni")ohos_shared_library("myffmpegdemo") {include_dirs = ["http://foundation/ace/napi/interfaces/kits","http://myffmpegsys/myffmpegpart/myffmpegdemo/include","http://third_party/ffmpeg",]sources = ["myffmpegdemo.cpp","ffmpeg_utils.cpp",]public_deps = ["http://foundation/ace/napi:ace_napi","http://third_party/ffmpeg:libohosffmpeg"]external_deps = ["hiviewdfx_hilog_native:libhilog",]relative_install_dir = "module"subsystem_name = "myffmpegsys"part_name = "myffmpegpart"}
?
(3)流程圖
?

?
(4)代碼分析
?
? ? Napi接口注冊(cè):
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
/************************************************ 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實(shí)現(xiàn)videoCute接口,將NAPI類型轉(zhuǎn)換成C++類型,然后調(diào)用FfmpegUtils的videoCute接口:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
static?void?executeVideoCute(napi_env?env,?void*?data)?{VideoCuteAddOnData *addonData = (VideoCuteAddOnData *) data;//調(diào)用視頻剪切的功能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;
?
根據(jù)輸入流創(chuàng)建輸出流,并且拷貝codec參數(shù):
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//創(chuàng)建流以及參數(shù)拷貝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;}
?
根據(jù)設(shè)置的截取時(shí)間段,跳轉(zhuǎn)到指定幀:
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//跳轉(zhuǎn)到指定幀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;}
?
循環(huán)讀取幀數(shù)據(jù),當(dāng)達(dá)到截取時(shí)間點(diǎn)后,退出循環(huán):
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
//讀取數(shù)據(jù)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];// 時(shí)間超過要截取的時(shí)間,就退出循環(huán)if (av_q2d(in_stream->time_base) * pkt.pts > end_seconds) {av_packet_unref(&pkt);break;}
?
寫入文件尾部信息:
- ?
- ?
?//寫文件尾信息ret = av_write_trailer(ofmt_ctx);
?
二、JS應(yīng)用實(shí)現(xiàn)
?
目錄結(jié)構(gòu)
?

?
代碼主要包含兩部分,index主要是裁剪相關(guān)的設(shè)置,player是針對(duì)視頻文件進(jìn)行播放的頁面。
?
index中設(shè)置了源文件,裁剪的起始時(shí)間,結(jié)束時(shí)間以后,通過裁剪按鈕,進(jìn)行視頻的裁剪功能,這一部分的代碼是通過底層NAPI提供的接口進(jìn)行的。
?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
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;}});},
?
視頻一旦裁剪成功以后,頁面就會(huì)出現(xiàn)播放的按鈕,點(diǎn)擊播放按鈕后,便可對(duì)裁剪后的文件進(jìn)行觀看。
?
總結(jié)
?
本文通過NAPI方式給大家講解了如何利用OpenHarmony系統(tǒng)能力實(shí)現(xiàn)更多的功能。開發(fā)者可以利用OpenHarmony自帶的三方庫,實(shí)現(xiàn)音視頻分離、音視頻轉(zhuǎn)碼、音視頻編解碼等多媒體處理功能,而且這些功能都可以在系統(tǒng)層實(shí)現(xiàn),并通過NAPI的方式提供對(duì)應(yīng)的接口進(jìn)行調(diào)用。對(duì)于OpenHarmony集成的其他內(nèi)在的能力,也可以通過NAPI的方式來對(duì)外提供接口,以此實(shí)現(xiàn)更多功能。
?
開發(fā)工作是一條漫長(zhǎng)的道路,開發(fā)者唯有舉一反三、觸類旁通,才能在未來的開發(fā)工作中達(dá)到事半功倍的效果。
?
電子發(fā)燒友App















評(píng)論