# videoCodecSample_1 **Repository Path**: wang-ziweix/video-codec-sample_1 ## Basic Information - **Project Name**: videoCodecSample_1 - **Description**: av_codec简单demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 20 - **Created**: 2024-04-30 - **Last Updated**: 2024-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AVCodecSample ### 介绍 AVCodec 部件示例 Sample,基于 API12 构建,提供视频播放和录制的功能。 - 视频播放的主要流程是将视频文件通过解封装->解码->送显/播放。 - 视频录制的主要流程是相机采集->编码->封装成mp4文件。 ### 播放支持的原子能力规格 | 媒体格式 | 封装格式 | 码流格式 | |------| :----------------------------|:-------------------------------------| | 视频 | mp4 | 视频码流:H.264/H.265, 音频码流:AudioVivid | | 视频 | mkv | 视频码流:H.264/H.265, 音频码流:aac/mp3/opus | | 视频 | mpeg-ts | 视频码流:H.264, 音频码流:AudioVivid | ### 录制支持的原子能力规格 | 封装格式 | 视频编解码类型 | | -------- |----------------------| | mp4 | H.264/H.265 | AAC、MPEG(MP3) | | m4a | AVC(H.264) | 注意,目前仅支持视频录制,未集成音频能力 ### 效果预览 | 播放 | 录制 | 录制 | |---------------------------------|---------------------------------|-----------------------------------| | ![播放.jepg](screenshots/播放.jpeg) | ![录制.jpeg](screenshots/录制.jpeg) | ![录制2.jpeg](screenshots/录制2.jpeg) | ### 使用说明 1. 弹出是否允许“AVCodec”访问图片与视频?点击“允许” 2. 弹出是否允许“AVCodec”访问文件?点击“允许” 3. 弹出是否允许“AVCodec”使用相机?点击“允许” #### 播放 1. 推送视频文件至storage/media/100/local/files/Docs下或点击下方“开始录制”,录制一个视频文件(无音频) 2. 点击播放按钮,选择文件 #### 录制 1. 点击“录制” 2. 选取视频输出路径 3. 点击“开始录制” 4. 点击“停止录制” ### 目录 仓目录结构如下: ``` video-codec-sample/entry/src/main/ ├── cpp # Native层 │ ├── capbilities # 能力接口和实现 │ │ ├── include # 能力接口 │ │ ├── audio_decoder.cpp # 音频解码实现 │ │ ├── demuxer.cpp # 解封装实现 │ │ ├── muxer.cpp # 封装实现 │ │ ├── video_decoder.cpp # 视频解码实现 │ │ └── video_encoder.cpp # 视频编码实现 │ ├── common # 公共模块 │ │ ├── dfx # 日志 │ │ ├── sample_callback.cpp # 编解码回调实现 │ │ ├── sample_callback.h # 编解码回调定义 │ │ └── sample_info.h # 功能实现公共类 │ ├── render # 送显模块接口和实现 │ │ ├── include # 送显模块接口 │ │ ├── egl_core.cpp # 送显参数设置 │ │ ├── plugin_manager.cpp # 送显模块管理实现 │ │ └── plugin_render.cpp # 送显逻辑实现 │ ├── sample # Native层 │ │ ├── player # Native层播放接口和实现 │ │ │ ├── Player.cpp # Native层播放功能调用逻辑的实现 │ │ │ ├── Player.h # Native层播放功能调用逻辑的接口 │ │ │ ├── PlayerNative.cpp # Native层 播放的入口 │ │ │ └── PlayerNative.h # │ │ └── recorder # Native层录制接口和实现 │ │ ├── Recorder.cpp # Native层录制功能调用逻辑的实现 │ │ ├── Recorder.h # Native层录制功能调用逻辑的接口 │ │ ├── RecorderNative.cpp # Native层 录制的入口 │ │ └── RecorderNative.h # │ ├── types # Native层暴露上来的接口 │ │ ├── libplayer # 播放模块暴露给UI层的接口 │ │ └── librecorder # 录制模块暴露给UI层的接口 │   └── CMakeLists.txt # 编译入口 ├── ets # UI层 │ ├── common # 公共模块 │ │ └──utils # 共用的工具类 │ │ ├── DateTimeUtils.ets # 获取当前时间 │ │ ├── Logger.ts # 日志工具 │ │ └── SaveAsset.ets # 选取文件保持位置 │ ├── CommonConstants.ets # 参数常量 │ ├── entryability # 应用的入口 │ │   └── EntryAbility.ts # 申请权限弹窗实现 │ ├── pages # EntryAbility 包含的页面 │ │ └── Index.ets # 首页/播放页面 │   └── sample # sample │ └── recorder # 录制 │ └── Recorder.ets # 录制页面 ├── resources # 用于存放应用所用到的资源文件 │ ├── base # 该目录下的资源文件会被赋予唯一的ID │ │ ├── element # 用于存放字体和颜色 │ │ ├── media # 用于存放图片 │ │ └── profile # 应用入口首页 │ ├── en_US # 设备语言是美式英文时,优先匹配此目录下资源 │ └── zh_CN # 设备语言是简体中文时,优先匹配此目录下资源 └── module.json5 # 模块配置信息 ``` ### 具体实现 #### *播放* ##### UI层 1. 在UI层[Index.ets](entry%2Fsrc%2Fmain%2Fets%2Fpages%2FIndex.ets)页面,用户点击播放按钮后,触发点击事件,调起selectFile()函数,该函数会调起文件管理的选择文件模块,拿到用户选取文件的路径。 2. 用户选择文件成功后,调起play()函数,该函数会根据上一步获取到的路径,打开一个文件,并获取到该文件的大小,改变按钮状态为不可用,之后调起js层暴露给应用层的[playNative()](entry%2Fsrc%2Fmain%2Fcpp%2Ftypes%2Flibplayer%2Findex.d.ts)接口 3. 根据playNative字段,调起[PlayerNative::Play](entry%2Fsrc%2Fmain%2Fcpp%2Fsample%2Fplayer%2FPlayerNative.cpp)函数,此处会注册播放结束的回调。 4. 播放结束时,Callback()中napi_call_function()接口调起,通知应用层,恢复按钮状态为可用。 ##### JS层 1. 在[Init()](entry%2Fsrc%2Fmain%2Fcpp%2Fsample%2Fplayer%2FPlayerNative.cpp)中调用PluginManager()中的Export()方法,注册OnSurfaceCreatedCB()回调,当屏幕上出现新的Xcomponent时,将其转换并赋给单例类PluginManager中的pluginWindow_; ##### Native层 参考[开发者文档](https://gitee.com/openharmony/docs/tree/master/zh-cn/application-dev/media/avcodec),以下是补充: 1. 解码器config阶段,OH_VideoDecoder_SetSurface接口的入参OHNativeWindow*,即为PluginManager中的pluginWindow_。 2. 解码器config阶段,SetCallback接口,[输入输出回调](entry%2Fsrc%2Fmain%2Fcpp%2Fcommon%2Fsample_callback.cpp)需将回调上来的帧buffer和index存入一个[用户自定义容器](entry%2Fsrc%2Fmain%2Fcpp%2Fcommon%2Fsample_info.h)中,方便后续操作。 3. [Start时](entry%2Fsrc%2Fmain%2Fcpp%2Fsample%2Fplayer%2FPlayer.cpp),起两个专门用于输入和输出的线程。 4. 具体实现原理: - 解码器Start后,解码器每拿到一帧,OnNeedInputBuffer就会被调起一次,avcodec框架会给用户一个OH_AVBuffer。 - 在输入回调中,用户需手动把帧bufer、index存入输入队列中,并同时输入线程解锁。 - 在输入线程中,把上一步的帧信息储存为bufferInfo后,pop出队。 - 在输入线程中,使用上一步的bufferInfo,调用ReadSample接口解封装帧数据。 - 在输入线程中,使用解封装后的bufferInfo,调用解码的PushInputData接口,此时这片buffer用完,返回框架,实现buffer轮转。 - PushInputData后,这一帧开始解码,每解码完成一帧,输出回调会被调起一次,用户需手动把帧buffer、index存入输出队列中,并。 - 在输出线程中,把上一步的帧信息储存为bufferInfo后,pop出队。 - 在输出线程中,调用FreeOutputData接口后,就会送显并释放buffer。释放的buffer会返回框架,实现buffer轮转。 #### *录制* ##### UI层 1. 在UI层[Index.ets](entry%2Fsrc%2Fmain%2Fets%2Fpages%2FIndex.ets)页面,用户点击“开始录制”后,会调起文件管理,用户选择一个输出地址。录制结束后,文件会存放于此。 2. 选择好文件后,会用刚刚打开的fd,和用户预设的录制参数,掉起JS层的initNative,待初始化结束后,调用OH_NativeWindow_GetSurfaceId接口,得到NativeWindow的surfaceId,并把surfaceId回调回UI层。 3. UI层拿到编码器给的surfaceId后,调起页面路由,携带该surfaceId,跳转到[Recorder.ets](entry%2Fsrc%2Fmain%2Fets%2Frecorder%2FRecorder.ets) 4. 录制页面构建时,Xcomponent构建时,会调起.onLoad()方法,此方法,首先会拿到Xcomponent的surfaceId,然后调起createDualChannelPreview(),此函数会建立一个相机生产,Xcomponent和编码器的surface消费的生产消费模型。 ##### Native层 参考[开发者文档](https://gitee.com/openharmony/docs/tree/master/zh-cn/application-dev/media/avcodec),以下是补充: 1. 用户点击开始录制后,编码器启动,开始对UI层相机预览流进行编码。 2. 编码器每编码成功一帧,输出回调[OnNewOutputBuffer()](entry%2Fsrc%2Fmain%2Fcpp%2Fcommon%2Fsample_callback.cpp)就会掉起一次,此时用户会拿到avcodec框架给出的OH_AVBuffer。 3. 在输出回调中,用户需手动把帧buffer、index存入输出队列中,并通知输出线程解锁。 4. 在输出线程中,把上一步的帧信息储存为bufferInfo后,pop出队。 5. 在输出线程中,使用上一步的bufferInfo,调用封装接口WriteSample后,这一帧被封装入MP4中了 6. 最后调用FreeOutputBuffer接口后,这一帧buffer释放回avcodec框架,实现buffer轮转 ### 相关权限 #### [ohos.permission.READ_MEDIA](https://docs.openharmony.cn/pages/v3.2/zh-cn/application-dev/security/permission-list.md/#ohospermissionreadmedia) #### [ohos.permission.WRITE_MEDIA](https://docs.openharmony.cn/pages/v3.2/zh-cn/application-dev/security/permission-list.md/#ohospermissionwritemedia) #### [ohos.permission.MEDIA_LOCATION](https://docs.openharmony.cn/pages/v3.2/zh-cn/application-dev/security/permission-list.md/#ohospermissionmedialocation) #### [ohos.permission.CAMERA](https://docs.openharmony.cn/pages/v3.2/zh-cn/application-dev/security/permission-list.md/#ohospermissioncamera) ### HarmonyOS 适配指南 1. 删除entry目录build-profile.json5中第5行"abiFilters"整行和第14行"runtimeOS"整行。 2. 将最外层build-profile.json5中第21行"runtimeOS"字段后的OpenHarmony改为HarmonyOS,并点击Sync Now。 3. 点击右侧报错通知Natifications-Sync failed-Learn More,替换最外层build-profile.json5中"products"部分所有内容。 4. 点击右侧报错通知Notifications-Sync failed 下的迁移工具->迁移。 5. 点击右上角搜索图标旁的项目结构-Signing Configs-勾选Support HarmonyOS、Automatically generate signature,自动签名完成后,点击OK。 ### 依赖 不涉及。 ### 约束与限制 1.本示例仅支持标准系统上运行; 2.本示例仅支持 API12 及以上版本SDK,SDK版本号(API Version 12 Release),镜像版本号(5.0 Release); 3.本示例需要使用DevEco Studio 5.0 才可编译运行。 ### 相关仓 - [multimedia_av_codec](https://gitee.com/openharmony/multimedia_av_codec)