diff --git a/code/BasicFeature/Media/AVCodec/.gitignore b/code/BasicFeature/Media/AVCodec/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..fbabf771011fe78f9919db0b1195ab6cadffc2b0
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/.gitignore
@@ -0,0 +1,11 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/AppScope/app.json5 b/code/BasicFeature/Media/AVCodec/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..8f551dbb787b6f5477f1281f29e4165031b4d65f
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "com.example.avcodecsample",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:app_icon",
+ "label": "$string:app_name"
+ }
+}
diff --git a/code/BasicFeature/Media/AVCodec/AppScope/resources/base/element/string.json b/code/BasicFeature/Media/AVCodec/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..8427310926f445f5313595f2990e44c6ced2af54
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "AVCodec"
+ }
+ ]
+}
diff --git a/code/BasicFeature/Media/AVCodec/AppScope/resources/base/media/app_icon.png b/code/BasicFeature/Media/AVCodec/AppScope/resources/base/media/app_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd45accb1dfd2fd0da16c732c72faa6e46b26521
Binary files /dev/null and b/code/BasicFeature/Media/AVCodec/AppScope/resources/base/media/app_icon.png differ
diff --git a/code/BasicFeature/Media/AVCodec/README_zh.md b/code/BasicFeature/Media/AVCodec/README_zh.md
new file mode 100644
index 0000000000000000000000000000000000000000..e726f0751d6d6086811ddd0054279b3145461f9c
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/README_zh.md
@@ -0,0 +1,458 @@
+# AVCodecSample
+
+### 介绍
+
+AVCodec 部件示例 Sample,基于 API12 构建,提供视频播放和录制的功能。
+
+- 视频播放的主要流程是将视频文件通过解封装->解码->送显/播放。
+- 视频录制的主要流程是相机采集->编码->封装成mp4文件。
+
+### 播放支持的原子能力规格
+
+| 媒体格式 | 封装格式 | 码流格式 |
+|------|:--------|:------------------------------------|
+| 视频 | mp4 | 视频码流:H.264/H.265, 音频码流:Audio |
+| 视频 | mkv | 视频码流:H.264/H.265, 音频码流:aac/mp3/opus |
+| 视频 | mpeg-ts | 视频码流:H.264, 音频码流:Audio |
+
+### 录制支持的原子能力规格
+
+| 封装格式 | 视频编解码类型 |
+|------|-------------|
+| mp4 | H.264/H.265 | AAC、MPEG(MP3) |
+
+注意,目前仅支持视频录制,未集成音频能力
+
+### 效果预览
+
+| 播放(横屏) | 播放(竖屏) | 录制 | 录制 |
+|---------------------------------|-------------------------------------|---------------------------------|-------------------------------------|
+|  |  |  |  |
+
+### 使用说明
+
+弹出是否允许“AVCodec”使用相机?点击“允许”
+
+- 推送视频到文件管理?
+ hdc file send xx.xx storage/media/100/local/files/Docs
+- 推送视频到图库?
+ hdc file send xx.mp4 storage/media/100/local/files
+ hdc shell mediatool send /storage/media/100/local/files/xx.mp4
+
+#### 播放
+
+1. 点击下方“录制”,录制一个视频文件(无音频)或推送文件到本地(可单独音频、单独视频、视频含音频)
+
+2. 点击播放按钮,选择从文件管理选取或从图库选取,点击确定,选择文件播放
+
+#### 录制
+
+1. (可选)选择相机分辨率
+
+2. 点击“录制”
+
+3. 选取视频输出路径
+
+4. 点击“开始录制”
+
+5. 点击“停止录制”
+
+### 目录
+
+仓目录结构如下:
+
+```
+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 # 共用的工具类
+│ │ ├── CameraCheck.ets # 相机能力查询
+│ │ ├── DateTimeUtils.ets # 获取当前时间
+│ │ └── Logger.ts # 日志工具
+│ ├── 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. [Start时](entry%2Fsrc%2Fmain%2Fcpp%2Fsample%2Fplayer%2FPlayer.cpp),起两个专门用于输入和输出的线程。
+3. 具体实现原理:
+ -
+ 解码器Start后,输入回调会调起,用户需要把待解码的数据填入OH_AVBuffer中,调用PushInputBuffer接口,归还解码器解码(每次Start后,至少要Push一次XPS帧)。
+ - 解码器每解出来一帧,输出回调就会调起一次,用户需要及时调用送显或释放接口,归还buffer给解码器。
+ 注意:由于解码器给用户的buffer数量有上限,用户需要及时归还回调上来的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. 在输出线程中,调用封装接口WriteSample,这一帧封装入MP4中了,之后调用释放接口,归还buffer给avcodec框架。
+
+### 音画同步
+
+#### 前言
+
+##### 背景和目的
+
+目前手机播放器在输出设备为蓝牙耳机时会出现严重音视频不同步现象, 严重影响用户体验。本文旨在指导第三方视频播放应用正确获取并使用音频相关信息来保证音视频同步。
+
+精确的音视频同步是媒体播放的关键性能指标之一。一般来说,在录音设备上同时录制的音频和视频需要在播放设备(例如手机,电视,媒体播放器)上同时播放。为了实现设备上的音视频同步,可以按如下指南操作。
+
+##### 概念定义
+
+| Abbreviations缩略语 | Full spelling 英文全名 | Chinese explanation 中文解释 |
+|------------------|:------------------------|:-------------------------|
+| PTS | Presentation Time Stamp | 送显时间戳 |
+| DTS | Decoding Time Stamp | 解码时间戳 |
+
+- DTS(解码时间戳)
+ 指音视频数据在解码器中开始解码的时间戳。它表示解码器应该从输入数据流中读取和解码的特定时间点。DTS用于控制解码器的解码顺序,确保音视频数据按照正确的顺序解码。
+- PTS(显示时间戳)
+ 指音视频数据在播放时应该显示给用户的时间戳。它表示解码后的音视频数据在播放时应该出现在屏幕上或传递给音视频输出设备的时间点。PTS用于控制音视频的播放顺序和时序,以确保音视频在正确的时间点进行显示或播放。
+
+##### 音画同步原理
+
+音视频数据的最小处理单元称为帧。音频流和视频流都被分割成帧,所有帧都被标记为需要按特定的时间戳显示。音频和视频可以独立下载和解码,但就具有匹配时间戳的音频和视频帧应同时呈现,达到A/V同步的效果。
+
+理论上,因为音频通路存在时延,匹配音频和视频处理,有三种A/V同步解决方案可用;
+(1)连续播放音频帧:使用音频播放位置作为主时间参考,并将视频播放位置与其匹配。
+(2)使用系统时间作为参考:将音频和视频播放与系统时间匹配。
+(3)使用视频播放作为参考:让音频匹配视频。
+
+| 策略名称 | 优点 | 缺点 |
+|-------------|:----------------------------------------------------|:-------------------------------------------------------------------------|
+| 连续播放音频帧(推荐) | ①用户肉眼的敏感度较弱,不易察觉视频微小的调整。
②容易实现,因为视频刷新时间的调整相对容易。 | ①如果视频帧率不稳定或延迟渲染大,可能导致视频卡顿或跳帧。 |
+| 使用系统时间作为参考 | 可以最大限度地保证音频和视频都不发生跳帧行为。 | ①需要额外依赖系统时钟,增加系统复杂性和维护成本。
②系统时钟的准确性对同步效果影响较大,如果系统时钟不准确,可能导致同步效果大打折扣。 |
+| 使用视频播放作为参考 | 音频可以根据视频帧进行调整,减少音频跳帧的情况。 | ①音频播放可能会出现等待或加速的情况,相较于视频,会对用户的影响更为严重和明显。
②如果视频帧率不稳定,可能导致音频同步困难。 |
+
+第一个选项是唯一一个具有连续音频数据流的选项,没有对音频帧的显示时间、播放速度或持续时间进行任何调整。这些参数的任何调整都很容易被人的耳朵注意到,并导致干扰的音频故障,除非音频被重新采样;但是,重新采样也会改变音调。因此,一般的多媒体应用使用音频播放位置作为主时间参考。以下段落将讨论此解决方案。(其它两个选项不在本文档的范围内)
+
+#### 效果展示
+
+##### 场景说明
+
+##### 适用范围
+
+适用于应用中视频播放过程中,由于设备渲染延迟、播放链路异常导致的音画不同步的场景
+
+##### 场景体验指标
+
+音画同步标准
+① 为了衡量音画同步的性能,用对应音频和视频帧实际播放时间的差值作为数值指标,数值大于0表示声音提前画面,小于0表示声音落后画面。
+② 最大卡顿时长,单帧图像停滞时间超过100ms的,定义为卡顿一次。连续测试5分钟,建议设置为100ms。
+③ 平均播放帧率,平均每秒播放帧数,不反映每帧显示时长。
+测试基准:一倍速场景
+
+| | 范围 | 主观体验 |
+|--------|:---------------|:-----|
+| S标(建议) | [-80ms, 25ms] | 无法察觉 |
+| A标 | [-125ms, 45ms] | 能够察觉 |
+| B标 | [-185ms, 90ms] | 能够察觉 |
+
+| 描述 | 应用内播放视频,音画同步指标应满足[-125ms, 45ms]。 |
+|------|:---------------------------------|
+| 类型 | 规则 |
+| 适用设备 | 手机、折叠屏、平板 |
+| 说明 | 无 |
+
+#### 场景分析
+
+##### 典型场景及优化方案
+
+**典型场景描述**
+应用内播放视频,音画同步指标应满足[-80ms, 25ms].
+**场景优化方案**
+该解决方案使用:
+
+- 视频同步到音频(主流方案)
+- 获取音频渲染进度动态调整视频渲染进度
+
+最终实现音画同步[-80ms,25ms]的效果。
+
+**图2 音画同步示意图**
+
+
+#### 场景实现
+
+##### 场景整体介绍
+
+音频和视频的管道必须同时以相同的时间戳呈现每帧数据。音频播放位置用作主时间参考,而视频管道只输出与最新渲染音频匹配的视频帧。对于所有可能的实现,精确计算最后一次呈现的音频时间戳是至关重要的。OS提供API来查询音频管道各个阶段的音频时间戳和延迟。
+
+音频管道支持查询最新呈现的时间戳,getTimeStamp()
+方法提供了一种简单的方法来确定我们要查找的值。如果时间戳可用,则audioTimestamp实例将填充以帧单位表示的位置,以及显示该帧时的估计时间。此信息可用于控制视频管道,使视频帧与音频帧匹配。
+
+##### 接口说明
+
+```cpp
+/*
+ * Query the the time at which a particular frame was presented.
+ *
+ * @since 10
+ *
+ * @param renderer Reference created by OH_AudioStreamBuilder_GenerateRenderer()
+ * @param clockId {@link #CLOCK_MONOTONIC}
+ * @param framePosition Pointer to a variable to receive the position
+ * @param timestamp Pointer to a variable to receive the timestamp
+ * @return Function result code:
+ * {@link AUDIOSTREAM_SUCCESS} If the execution is successful.
+ * {@link AUDIOSTREAM_ERROR_INVALID_PARAM}:
+ * 1.The param of renderer is nullptr;
+ * 2.The param of clockId invalid.
+ * {@link AUDIOSTREAM_ERROR_ILLEGAL_STATE} Execution status exception.
+ */
+OH_AudioStream_Result OH_AudioRenderer_GetTimestamp(OH_AudioRenderer* renderer,
+ clockid_t clockId, int64_t* framePosition, int64_t* timestamp);
+```
+
+注意事项:
+
+(1)
+OH_AudioRenderer_Start到真正写入硬件有一定延迟,因此该接口在OH_AudioRenderer_Start之后过一会儿才会再拿到有效值,期间音频未发声时建议画面帧先按照正常速度播放,后续再逐步追赶音频位置从而提升用户看到画面的起搏时延。
+
+(2)当framePosition和timeStamp稳定之前,调用可以比较频繁(如100ms)
+,当以稳定的速度增长前进后,建议OH_AudioRenderer_GetTimestamp的频率不要太频繁,可以每分钟一次,最好不要低于500ms一次,因为频繁调用可能会带来功耗问题,因此在能保证音画同步效果的情况下,不需要频繁地查询时间戳。
+
+(3)OH_AudioRenderer_Flush接口执行后,framePosition返回值会重新(从0)开始计算。
+
+(4)OH_AudioRenderer_GetFramesWritten 接口在Flush的时候不会清空,该接口和OH_AudioRenderer_GetTimestamp接口并不建议配合使用。
+
+(5)音频设备切换过程中OH_AudioRenderer_GetTimestamp返回的framePosition和timestamp不会倒退,但由于新设备写入有时延,会出现短暂时间内音频进度无增长,建议画面帧保持流程播放不要产生卡顿。
+
+(6)
+OH_AudioRenderer_GetTimeStamp获取的是实际写到硬件的采样帧数,不受倍速影响。对AudioRender设置了倍速的场景下,播放进度计算需要特殊处理,系统保证应用设置完倍速播放接口后,新写入AudioRender的采样点才会做倍速处理。
+
+##### 关键代码片段
+
+(1)获取音频渲染的位置
+
+```cpp
+// get audio render position
+int64_t framePosition = 0;
+int64_t timestamp = 0;
+int32_t ret = OH_AudioRenderer_GetTimestamp(audioRenderer_, CLOCK_MONOTONIC, &framePosition, ×tamp);
+AVCODEC_SAMPLE_LOGI("VD framePosition: %{public}li, nowTimeStamp: %{public}li", framePosition, nowTimeStamp);
+audioTimeStamp = timestamp; // ns
+```
+
+(2)音频启动前暂不做音画同步
+
+- 音频未启动前,timestamp和framePosition返回结果为0,为避免出现卡顿等问题,暂不同步
+
+```cpp
+// audio render getTimeStamp error, render it
+if (ret != AUDIOSTREAM_SUCCESS || (timestamp == 0) || (framePosition == 0)) {
+ // first frame, render without wait
+ videoDecoder_->FreeOutputBuffer(bufferInfo.bufferIndex, true);
+ lastPushTime = std::chrono::system_clock::now();
+ continue;
+}
+```
+
+(3)根据视频帧pts和音频渲染位置计算延迟waitTimeUs
+
+- audioPlayedTime音频帧期望渲染时间
+- videoPlayedTime视频帧期望送显时间
+
+```cpp
+// after seek, audio render flush, framePosition = 0, then writtenSampleCnt = 0
+int64_t latency = (audioDecContext_->frameWrittenForSpeed - framePosition) * 1000 *
+ 1000 / sampleInfo_.audioSampleRate / speed;
+AVCODEC_SAMPLE_LOGI("VD latency: %{public}li writtenSampleCnt: %{public}li", latency, writtenSampleCnt);
+
+nowTimeStamp = GetCurrentTime();
+int64_t anchordiff = (nowTimeStamp - audioTimeStamp) / 1000;
+
+// us, audio buffer accelerate render time
+int64_t audioPlayedTime = audioDecContext_->currentPosAudioBufferPts - latency + anchorDiff;
+// us, video buffer expected render time
+int64_t videoPlayedTime = bufferInfo.attr.pts;
+
+// audio render timestamp and now timestamp diff
+int64_t waitTimeUs = videoPlayedTime - audioPlayedTime; // us
+```
+
+(4)根据业务延迟做音画同步策略
+
+- [,-40ms) 视频帧较晚,此帧丢掉
+- [-40ms,0ms)视频帧直接送显
+- [0ms,)视频帧较早,根据业务需要选择现象追帧
+
+```cpp
+// video buffer is too late, drop it
+if (waitTimeUs < WAIT_TIME_US_THRESHOLD_WARNING) {
+ dropFrame = true;
+ AVCODEC_SAMPLE_LOGI("VD buffer is too late");
+} else {
+ AVCODEC_SAMPLE_LOGE("VD buffer is too early waitTimeUs:%{public}ld", waitTimeUs);
+ // [0, ), render it wait waitTimeUs, max 1s
+ // [-40, 0), render it
+ if (waitTimeUs > WAIT_TIME_US_THRESHOLD) {
+ waitTimeUs = WAIT_TIME_US_THRESHOLD;
+ }
+ // per frame render time reduced by 33ms
+ if (waitTimeUs > sampleInfo_.frameInterval + PER_SINK_TIME_THRESHOLD) {
+ waitTimeUs = sampleInfo_.frameInterval + PER_SINK_TIME_THRESHOLD;
+ AVCODEC_SAMPLE_LOGE("VD buffer is too early and reduced 33ms, waitTimeUs: %{public}ld", waitTimeUs);
+ }
+}
+```
+
+(5)进行音画同步
+若视频帧的时间大于2倍vsync的时间,则需要sleep超过的时间。
+
+```cpp
+if (static_cast(waitTimeUs) > FRAME_TIME * LIP_SYNC_BALANCE_VALUE) {
+ std::this_thread::sleep_for(std::chrono::microseconds(
+ static_cast(static_cast(waitTimeUs)-FRAME_TIME * LIP_SYNC_BALANCE_VALUE)));
+}
+ret = videoDecoder_->FreeOutputBuffer(bufferInfo.bufferIndex, !dropFrame, FRAME_TIME * LIP_SYNC_BALANCE_VALUE * 1000 + GetCurrentTime());
+CHECK_AND_BREAK_LOG(ret == AVCODEC_SAMPLE_ERR_OK, "Decoder output thread");
+```
+
+### 倍速播放方案
+
+#### 当前问题
+
+
+通过Audio GetTimeStamp拿到的Position始终是一倍速参考系下计算的,导致应用写下多倍速的音频帧后不清楚底层实际播放的原始位置。
+
+**比如假设采样率是48k,应用写的frameIn A一共写了48000,2倍速后的frameOut A' 只有24000,
+底层播了一半后返回给应用的position是12000 - 硬件latency(假设是100ms)
+,也就是倍速后播了150ms,但应用实际播放的pts应该是24000-硬件latency×2 = 300ms**
+
+Position表示的是音频帧,一个音频帧包括左右声道的采样点交织形成的数据包,比如双声道16bit采样点,一帧数据是4个字节,48k采样率的音频,一秒播放48000帧
+
+应用一般音画同步做法:
+
+视频每解码一帧,获取一下音频clock,视频帧永远跟随音频pts
+
+#### **倍速的音频时间戳计算算法(此方法也同样适用于三方自研播放器)**
+
+原理:记录每次setSpeed时的最后position状态作为基准,更新speed之后,按照上一次speed末尾的基准+数据delta×最新speed返回给应用
+
+| **时间线** | **应用行为** | **播放范围(写给AudioRender的数据)** | **此刻音频服务处理的位置(frameOutC)** | **pulseaudio实际返回的position** | **audiorender矫正后返回给应用的值** | **音频PTS(假设起始时间是X)** |
+|:---------------:|:--------:|:---------------------------------------:|:--------------------------:|:-----------------------------------------------------------------------------------------------------------------:|:-------------------------:|:----------------------------------------------------------------:|
+| **T0时刻** | 先一倍速 | 1-1000 | 800 | 600 | 600 | X + 600/48000 |
+| **T1时刻** | 倍速调节成2 | | | 记录倍速调节之前写的位置
lastSpeedX = 1000
lastSpeedFramesWritten = 1000 | | |
+| **T2时刻** | 2倍速 | 原始数据1001-2000,倍速后送给Audio服务的是(1001-1500) | 1400 | 1200 | | |
+| **计算T2时刻音频PTS** | | | | 1200如何倒推音源Position?
实际位置=(position-lastSpeedIdx)*speed + lastSpeedFramesWritten
(1200-1000)×2+1000 = 1400 | 1400 | X+1400/48000
记录lastPosition = 1400
lastPositionTime = T2 |
+| **视频出帧T2'时刻** | | | | | | 送显delay = 视频PTS - (X + 1400 / 48000 + (T2' - T2)*2 |
+| **T3时刻** | 倍速调节成3 | | | 记录倍速调节之前写的位置
lastSpeedIdx = 1500
lastSpeedFramesWritten = 2000 | | |
+| **T4时刻** | 3倍速 | 原始数据2001-3500,倍速后送欸Audio服务的是(1501-2000) | 1600 | 1400 | | |
+| **计算T4时刻音频PTS** | | | | 1400 < 1500, 说明底层还在播老倍速的数据,复用上一次的音频pts做偏移 | 1400+(T4-T2)×2 | X+(lastPosition+(T4-T2)×2) |
+| **T5时刻** | 3倍速 | 原始数据2001-3500 播放中 | 1900 | 1700 | | |
+| **计算T5时刻音频PTS** | | Content | | 实际位置=(position-lastSpeedIdx)*speed + lastSpeedFramesWritten
(1700-1500)×3+2000 = 2600 | 2600 | X+2600/48000 |
+
+### 相关权限
+
+#### [ohos.permission.CAMERA](https://docs.openharmony.cn/pages/v3.2/zh-cn/application-dev/security/permission-list.md/#ohospermissioncamera)
+
+### 依赖
+
+不涉及。
+
+### 约束与限制
+
+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)
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/build-profile.json5 b/code/BasicFeature/Media/AVCodec/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..46db5f046565014cdea020e8d70664335f77d6f1
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/build-profile.json5
@@ -0,0 +1,45 @@
+{
+ "app": {
+ "signingConfigs": [
+ {
+ "name": "default",
+ "type": "HarmonyOS",
+ "material": {
+ "certpath": "C:\\Users\\leo\\.ohos\\config\\default_AVCodecSample_WoskRniMF1sh_wN3SLgTN2z2y57z4jO_NTIgkFjSqAo=.cer",
+ "storePassword": "0000001BFFAEF8E43682FCB052F381D0FF4F4C2691570B8DA6F86B2C9EC10FC78B3908C068D1308DF05486",
+ "keyAlias": "debugKey",
+ "keyPassword": "0000001BD0BB8B5222D7F602A58F6C67C0B541C19177FDC6602BB8F7DF2FEA023D181DD6DF669ADF6EE284",
+ "profile": "C:\\Users\\leo\\.ohos\\config\\default_AVCodecSample_WoskRniMF1sh_wN3SLgTN2z2y57z4jO_NTIgkFjSqAo=.p7b",
+ "signAlg": "SHA256withECDSA",
+ "storeFile": "C:\\Users\\leo\\.ohos\\config\\default_AVCodecSample_WoskRniMF1sh_wN3SLgTN2z2y57z4jO_NTIgkFjSqAo=.p12"
+ }
+ }
+ ],
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compatibleSdkVersion": "5.0.0(12)",
+ //指定HarmonyOS应用/服务兼容的最低版本。注意使用英文.和()
+ "targetSdkVersion": "5.0.0(12)",
+ //指定HarmonyOS应用/服务目标版本。若没有设置,默认为compatibleSdkVersion
+ "runtimeOS": "HarmonyOS",
+ //指定为HarmonyOS
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/.gitignore b/code/BasicFeature/Media/AVCodec/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/build-profile.json5 b/code/BasicFeature/Media/AVCodec/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..4284436e687906bbbacd940c3fc75e0b00088d16
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/build-profile.json5
@@ -0,0 +1,20 @@
+{
+ "apiType": 'stageMode',
+ "buildOption": {
+ "externalNativeOptions": {
+ "abiFilters": ["arm64-v8a", "x86_64"],
+ "path": "./src/main/cpp/CMakeLists.txt",
+ "arguments": "",
+ "cppFlags": "",
+ }
+ },
+ "targets": [
+ {
+ "name": "default",
+ "runtimeOS": "HarmonyOS"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/hvigorfile.ts b/code/BasicFeature/Media/AVCodec/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..80e4ec5b81689f238c34614b167a0b9e9c83e8d9
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/hvigorfile.ts
@@ -0,0 +1,2 @@
+// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
+export { hapTasks } from '@ohos/hvigor-ohos-plugin';
diff --git a/code/BasicFeature/Media/AVCodec/entry/oh-package-lock.json5 b/code/BasicFeature/Media/AVCodec/entry/oh-package-lock.json5
new file mode 100644
index 0000000000000000000000000000000000000000..4b35fc4253b119c76ce1fec3d5e3b159ea3c0169
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/oh-package-lock.json5
@@ -0,0 +1,25 @@
+{
+ "meta": {
+ "stableOrder": true
+ },
+ "lockfileVersion": 3,
+ "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
+ "specifiers": {
+ "libplayer.so@src/main/cpp/types/libplayer": "libplayer.so@src/main/cpp/types/libplayer",
+ "librecorder.so@src/main/cpp/types/librecorder": "librecorder.so@src/main/cpp/types/librecorder"
+ },
+ "packages": {
+ "libplayer.so@src/main/cpp/types/libplayer": {
+ "name": "libplayer.so",
+ "version": "1.0.0",
+ "resolved": "src/main/cpp/types/libplayer",
+ "registryType": "local"
+ },
+ "librecorder.so@src/main/cpp/types/librecorder": {
+ "name": "librecorder.so",
+ "version": "1.0.0",
+ "resolved": "src/main/cpp/types/librecorder",
+ "registryType": "local"
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/oh-package.json5 b/code/BasicFeature/Media/AVCodec/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..ca8155cfe235af4ec786491046316f827e22af3d
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/oh-package.json5
@@ -0,0 +1,13 @@
+{
+ "license": "",
+ "devDependencies": {},
+ "author": "",
+ "name": "entry",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "version": "1.0.0",
+ "dependencies": {
+ "libplayer.so": "file:./src/main/cpp/types/libplayer",
+ "librecorder.so": "file:./src/main/cpp/types/librecorder"
+ }
+}
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/CMakeLists.txt b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5cfaabf41fcc2d48176bb2eb9d661392df346495
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,41 @@
+# the minimum version of CMake.
+cmake_minimum_required(VERSION 3.4.1)
+project(videoCodecSample)
+
+set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+
+include_directories(${NATIVERENDER_ROOT_PATH}
+ ${NATIVERENDER_ROOT_PATH}/capbilities/include
+ ${NATIVERENDER_ROOT_PATH}/common
+ ${NATIVERENDER_ROOT_PATH}/common/dfx/err
+ ${NATIVERENDER_ROOT_PATH}/common/dfx/log
+ ${NATIVERENDER_ROOT_PATH}/render/include
+ ${NATIVERENDER_ROOT_PATH}/sample/player
+ ${NATIVERENDER_ROOT_PATH}/sample/recorder
+)
+
+set(BASE_LIBRARY
+ libace_napi.z.so libGLESv3.so libace_ndk.z.so libuv.so libhilog_ndk.z.so
+ libnative_media_codecbase.so libnative_media_core.so libnative_media_vdec.so libnative_window.so
+ libnative_media_venc.so libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so libnative_media_avmuxer.so
+ libohaudio.so
+)
+add_library(player SHARED sample/player/PlayerNative.cpp
+ sample/player/Player.cpp
+ capbilities/demuxer.cpp
+ capbilities/video_decoder.cpp
+ capbilities/audio_decoder.cpp
+ render/plugin_render.cpp
+ render/plugin_manager.cpp
+ common/sample_callback.cpp
+)
+
+add_library(recorder SHARED sample/recorder/RecorderNative.cpp
+ sample/recorder/Recorder.cpp
+ capbilities/muxer.cpp
+ capbilities/video_encoder.cpp
+ common/sample_callback.cpp
+)
+
+target_link_libraries(player PUBLIC ${BASE_LIBRARY})
+target_link_libraries(recorder PUBLIC ${BASE_LIBRARY})
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/audio_decoder.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/audio_decoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8109a113cd0f540a1da959f4ed5ba4268063d1ee
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/audio_decoder.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "audio_decoder.h"
+
+#undef LOG_TAG
+#define LOG_TAG "AudioDecoder"
+
+AudioDecoder::~AudioDecoder()
+{
+ Release();
+}
+
+int32_t AudioDecoder::Create(const std::string &codecMime)
+{
+ decoder_ = OH_AudioCodec_CreateByMime(codecMime.c_str(), false);
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::SetCallback(CodecUserData *codecUserData)
+{
+ int32_t ret = AV_ERR_OK;
+ ret = OH_AudioCodec_RegisterCallback(decoder_,
+ {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
+ SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer},
+ codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set callback failed, ret: %{public}d", ret);
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::Configure(const SampleInfo &sampleInfo)
+{
+ OH_AVFormat *format = OH_AVFormat_Create();
+ CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "AVFormat create failed");
+
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S16LE); // SAMPLE_S16LE SAMPLE_F32P
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, sampleInfo.audioChannelCount);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleInfo.audioSampleRate);
+ OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, sampleInfo.audioChannelLayout);
+
+ if (sampleInfo.codecConfigLen > 0) {
+ AVCODEC_SAMPLE_LOGI("====== AudioDecoder config ====== codecConfig:%{public}p, len:%{public}i, "
+ "adts:${public}i, 0:0x%{public}02x, 1:0x%{public}02x",
+ sampleInfo.codecConfig, sampleInfo.codecConfigLen, sampleInfo.aacAdts,
+ sampleInfo.codecConfig[0], sampleInfo.codecConfig[1]);
+ uint8_t tmpCodecConfig[2];
+ tmpCodecConfig[0] = 0x13; // 0x11
+ tmpCodecConfig[1] = 0x10; // 0x90
+ tmpCodecConfig[0] = sampleInfo.codecConfig[0]; // 0x11
+ tmpCodecConfig[1] = sampleInfo.codecConfig[1]; // 0x90
+ AVCODEC_SAMPLE_LOGI("====== AudioDecoder config ====== 0:0x%{public}02x, 1:0x%{public}02x", tmpCodecConfig[0],
+ tmpCodecConfig[1]);
+ OH_AVFormat_SetBuffer(format, OH_MD_KEY_CODEC_CONFIG, sampleInfo.codecConfig, sampleInfo.codecConfigLen);
+ }
+
+ AVCODEC_SAMPLE_LOGI("====== AudioDecoder config ======");
+ int ret = OH_AudioCodec_Configure(decoder_, format);
+ AVCODEC_SAMPLE_LOGI("====== AudioDecoder config ======");
+ if (ret != AV_ERR_OK) {
+ AVCODEC_SAMPLE_LOGE("Config failed, ret: %{public}d", ret);
+ OH_AVFormat_Destroy(format);
+ format = nullptr;
+ return AVCODEC_SAMPLE_ERR_ERROR;
+ }
+ OH_AVFormat_Destroy(format);
+ format = nullptr;
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+ CHECK_AND_RETURN_RET_LOG(codecUserData != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Invalid param: codecUserData");
+
+ // Configure audio decoder
+ int32_t ret = Configure(sampleInfo);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Configure failed");
+
+ // SetCallback for audio decoder
+ ret = SetCallback(codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR,
+ "Set callback failed, ret: %{public}d", ret);
+
+ // Prepare audio decoder
+ {
+ int ret = OH_AudioCodec_Prepare(decoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Prepare failed, ret: %{public}d", ret);
+ }
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::Start()
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int ret = OH_AudioCodec_Start(decoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Start failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::PushInputBuffer(CodecBufferInfo &info)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+ int32_t ret = OH_AVBuffer_SetBufferAttr(reinterpret_cast(info.buffer), &info.attr);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set avbuffer attr failed");
+ ret = OH_AudioCodec_PushInputBuffer(decoder_, info.bufferIndex);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Push input data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::FreeOutputBuffer(uint32_t bufferIndex, bool render)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int32_t ret = AVCODEC_SAMPLE_ERR_OK;
+ ret = OH_AudioCodec_FreeOutputBuffer(decoder_, bufferIndex);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Free output data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t AudioDecoder::Release()
+{
+ if (decoder_ != nullptr) {
+ OH_AudioCodec_Flush(decoder_);
+ OH_AudioCodec_Stop(decoder_);
+ OH_AudioCodec_Destroy(decoder_);
+ decoder_ = nullptr;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/demuxer.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/demuxer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..68bf5d62ad564d24d07e4a51fa3a70b626cc4ad0
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/demuxer.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "demuxer.h"
+#include
+
+#undef LOG_TAG
+#define LOG_TAG "Demuxer"
+
+Demuxer::~Demuxer() { Release(); }
+
+int32_t Demuxer::Create(SampleInfo &info)
+{
+#ifdef CREATE_WITH_URI
+ // Need request Internet Permission first in module.json.
+ const char *url = "https://hd.ijycnd.com/play/Ddw1W2Ra/index.m3u8";
+ source_ = OH_AVSource_CreateWithURI(const_cast(url));
+#endif
+ source_ = OH_AVSource_CreateWithFD(info.inputFd, info.inputFileOffset, info.inputFileSize);
+ CHECK_AND_RETURN_RET_LOG(source_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR,
+ "Create demuxer source failed, fd: %{public}d, offset: %{public}" PRId64
+ ", file size: %{public}" PRId64,
+ info.inputFd, info.inputFileOffset, info.inputFileSize);
+ demuxer_ = OH_AVDemuxer_CreateWithSource(source_);
+ CHECK_AND_RETURN_RET_LOG(demuxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create demuxer failed");
+
+ auto sourceFormat = std::shared_ptr(OH_AVSource_GetSourceFormat(source_), OH_AVFormat_Destroy);
+ CHECK_AND_RETURN_RET_LOG(sourceFormat != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Get source format failed");
+
+ int32_t ret = GetTrackInfo(sourceFormat, info);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Get video track info failed");
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Demuxer::ReadSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr)
+{
+ CHECK_AND_RETURN_RET_LOG(demuxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Demuxer is null");
+ int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer_, trackId, buffer);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Read sample failed");
+ ret = OH_AVBuffer_GetBufferAttr(buffer, &attr);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "GetBufferAttr failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Demuxer::Release()
+{
+ if (demuxer_ != nullptr) {
+ OH_AVDemuxer_Destroy(demuxer_);
+ demuxer_ = nullptr;
+ }
+ if (source_ != nullptr) {
+ OH_AVSource_Destroy(source_);
+ source_ = nullptr;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Demuxer::GetTrackInfo(std::shared_ptr sourceFormat, SampleInfo &info)
+{
+ int32_t trackCount = 0;
+ OH_AVFormat_GetIntValue(sourceFormat.get(), OH_MD_KEY_TRACK_COUNT, &trackCount);
+
+ for (int32_t index = 0; index < trackCount; index++) {
+ auto trackFormat = GetTrackFormat(index);
+ int trackType = GetTrackType(trackFormat);
+
+ if (trackType == MEDIA_TYPE_VID) {
+ ProcessVideoTrack(trackFormat, index, info);
+ } else if (trackType == MEDIA_TYPE_AUD) {
+ ProcessAudioTrack(trackFormat, index, info);
+ }
+ }
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+std::shared_ptr Demuxer::GetTrackFormat(int32_t index)
+{
+ return std::shared_ptr(OH_AVSource_GetTrackFormat(source_, index), OH_AVFormat_Destroy);
+}
+
+int Demuxer::GetTrackType(std::shared_ptr trackFormat)
+{
+ int trackType = -1;
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_TRACK_TYPE, &trackType);
+ return trackType;
+}
+
+void Demuxer::ProcessVideoTrack(std::shared_ptr trackFormat, int32_t index, SampleInfo &info)
+{
+ OH_AVDemuxer_SelectTrackByID(demuxer_, index);
+
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_WIDTH, &info.videoWidth);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_HEIGHT, &info.videoHeight);
+ OH_AVFormat_GetDoubleValue(trackFormat.get(), OH_MD_KEY_FRAME_RATE, &info.frameRate);
+ OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_BITRATE, &info.bitrate);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_ROTATION, &info.rotation);
+
+ char *videoCodecMime;
+ OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast(&videoCodecMime));
+ info.videoCodecMime = videoCodecMime;
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_PROFILE, &info.hevcProfile);
+ videoTrackId_ = index;
+
+ LogVideoConfig(info, videoCodecMime);
+}
+
+void Demuxer::ProcessAudioTrack(std::shared_ptr trackFormat, int32_t index, SampleInfo &info)
+{
+ OH_AVDemuxer_SelectTrackByID(demuxer_, index);
+
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUDIO_SAMPLE_FORMAT, &info.audioSampleForamt);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_CHANNEL_COUNT, &info.audioChannelCount);
+ OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_CHANNEL_LAYOUT, &info.audioChannelLayout);
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_SAMPLE_RATE, &info.audioSampleRate);
+
+ char *audioCodecMime;
+ OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast(&audioCodecMime));
+
+ HandleCodecConfig(trackFormat, info);
+
+ OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AAC_IS_ADTS, &info.aacAdts);
+ info.audioCodecMime = audioCodecMime;
+ audioTrackId_ = index;
+
+ LogAudioConfig(info, audioCodecMime);
+}
+
+void Demuxer::HandleCodecConfig(std::shared_ptr trackFormat, SampleInfo &info)
+{
+ uint8_t *codecConfig = nullptr;
+ OH_AVFormat_GetBuffer(trackFormat.get(), OH_MD_KEY_CODEC_CONFIG, &codecConfig, &info.codecConfigLen);
+
+ if (info.codecConfigLen > 0 && info.codecConfigLen < sizeof(info.codecConfig)) {
+ memcpy(info.codecConfig, codecConfig, info.codecConfigLen);
+ LogCodecConfigDetails(info);
+ }
+}
+
+void Demuxer::LogVideoConfig(const SampleInfo &info, const char *videoCodecMime)
+{
+ AVCODEC_SAMPLE_LOGI("====== Demuxer Video config ======");
+ AVCODEC_SAMPLE_LOGI("Mime: %{public}s", videoCodecMime);
+ AVCODEC_SAMPLE_LOGI("%{public}d * %{public}d, %{public}.1ffps, %{public}" PRId64 "kbps",
+ info.videoWidth, info.videoHeight, info.frameRate, info.bitrate / 1024);
+ AVCODEC_SAMPLE_LOGI("====== Demuxer Video config ======");
+}
+
+void Demuxer::LogAudioConfig(const SampleInfo &info, const char *audioCodecMime)
+{
+ AVCODEC_SAMPLE_LOGI("====== Demuxer Audio config ======");
+ AVCODEC_SAMPLE_LOGI("audioMime:%{public}s sampleForamt:%{public}d sampleRate:%{public}d "
+ "channelCount:%{public}d channelLayout:%{public}ld adts:%{public}i",
+ audioCodecMime, info.audioSampleForamt, info.audioSampleRate,
+ info.audioChannelCount, info.audioChannelLayout, info.aacAdts);
+ AVCODEC_SAMPLE_LOGI("====== Demuxer Audio config ======");
+}
+
+void Demuxer::LogCodecConfigDetails(const SampleInfo &info)
+{
+ AVCODEC_SAMPLE_LOGI("codecConfig:%{public}p, len:%{public}i, 0:0x%{public}02x 1:0x:%{public}02x, bufLen:%{public}u",
+ info.codecConfig, static_cast(info.codecConfigLen),
+ info.codecConfig[0], info.codecConfig[1],
+ static_cast(sizeof(info.codecConfig)));
+}
+
+int32_t Demuxer::GetVideoTrackId() { return videoTrackId_; }
+int32_t Demuxer::GetAudioTrackId() { return audioTrackId_; }
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/audio_decoder.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/audio_decoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e4d5979c32d11208f1044c4254a7efe9676b320
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/audio_decoder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIODECODER_H
+#define AUDIODECODER_H
+
+#include "multimedia/player_framework/native_avcodec_audiocodec.h"
+#include "multimedia/player_framework/native_avbuffer_info.h"
+#include "sample_callback.h"
+#include "dfx/error/av_codec_sample_error.h"
+#include "av_codec_sample_log.h"
+
+class AudioDecoder {
+public:
+ AudioDecoder() = default;
+ ~AudioDecoder();
+
+ int32_t Create(const std::string &codecMime);
+ int32_t Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData);
+ int32_t Start();
+ int32_t PushInputBuffer(CodecBufferInfo &info);
+ int32_t FreeOutputBuffer(uint32_t bufferIndex, bool render);
+ int32_t Release();
+
+private:
+ int32_t SetCallback(CodecUserData *codecUserData);
+ int32_t Configure(const SampleInfo &sampleInfo);
+
+ bool isAVBufferMode_ = false;
+ OH_AVCodec *decoder_;
+};
+#endif // AUDIODECODER_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/demuxer.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/demuxer.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1fdc48b87dfc085aa96d0d08c1838c92f9c43c6
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/demuxer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEMUXER_H
+#define DEMUXER_H
+
+#include
+#include "napi/native_api.h"
+#include "multimedia/player_framework/native_avdemuxer.h"
+#include "sample_info.h"
+#include "dfx/error/av_codec_sample_error.h"
+#include "av_codec_sample_log.h"
+
+
+class Demuxer {
+public:
+ Demuxer() = default;
+ ~Demuxer();
+ int32_t Create(SampleInfo &sampleInfo);
+ int32_t ReadSample(int32_t trackId, OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr);
+ int32_t Release();
+ int32_t GetVideoTrackId();
+ int32_t GetAudioTrackId();
+
+private:
+ int32_t GetTrackInfo(std::shared_ptr sourceFormat, SampleInfo &info);
+ std::shared_ptr GetTrackFormat(int32_t index);
+ int GetTrackType(std::shared_ptr trackFormat);
+ void ProcessVideoTrack(std::shared_ptr trackFormat, int32_t index, SampleInfo &info);
+ void ProcessAudioTrack(std::shared_ptr trackFormat, int32_t index, SampleInfo &info);
+ void HandleCodecConfig(std::shared_ptr trackFormat, SampleInfo &info);
+ void LogVideoConfig(const SampleInfo &info, const char *videoCodecMime);
+ void LogAudioConfig(const SampleInfo &info, const char *audioCodecMime);
+ void LogCodecConfigDetails(const SampleInfo &info);
+
+ OH_AVSource *source_;
+ OH_AVDemuxer *demuxer_;
+ int32_t videoTrackId_;
+ int32_t audioTrackId_;
+};
+
+#endif // DEMUXER_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/muxer.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/muxer.h
new file mode 100644
index 0000000000000000000000000000000000000000..938f381ac5ecd8dfa9b0c28249c18a79882ee826
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/muxer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MUXER_H
+#define MUXER_H
+
+#include
+#include "multimedia/player_framework/native_avmuxer.h"
+#include "sample_info.h"
+#include "dfx/error/av_codec_sample_error.h"
+#include "av_codec_sample_log.h"
+
+class Muxer {
+public:
+ Muxer() = default;
+ ~Muxer();
+
+ int32_t Create(int32_t fd);
+ int32_t Config(SampleInfo &sampleInfo);
+ int32_t Start();
+ int32_t WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr);
+ int32_t Stop();
+ int32_t Release();
+
+private:
+ OH_AVMuxer *muxer_ = nullptr;
+ int32_t videoTrackId_ = -1;
+};
+
+#endif // MUXER_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_decoder.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_decoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..418379dd61a2e42845f712b241d3c949360b97df
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_decoder.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEODECODER_H
+#define VIDEODECODER_H
+
+#include "multimedia/player_framework/native_avcodec_videodecoder.h"
+#include "multimedia/player_framework/native_avcapability.h"
+#include "multimedia/player_framework/native_avbuffer_info.h"
+#include "sample_info.h"
+#include "sample_callback.h"
+#include "dfx/error/av_codec_sample_error.h"
+#include "av_codec_sample_log.h"
+
+class VideoDecoder {
+public:
+ VideoDecoder() = default;
+ ~VideoDecoder();
+
+ int32_t Create(const std::string &videoCodecMime, int32_t videoDecoderType);
+ int32_t Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData);
+ int32_t PushInputBuffer(CodecBufferInfo &info);
+ int32_t FreeOutputBuffer(uint32_t bufferIndex, bool render);
+ int32_t FreeOutputBuffer(uint32_t bufferIndex, bool render, int64_t timeStamp);
+ int32_t Start();
+ int32_t Release();
+
+private:
+ int32_t SetCallback(CodecUserData *codecUserData);
+ int32_t Configure(const SampleInfo &sampleInfo);
+ OH_AVCodec *GetCodecByCategory(const char *mime, bool isEncoder, OH_AVCodecCategory category);
+
+ bool isAVBufferMode_ = false;
+ OH_AVCodec *decoder_;
+};
+#endif // VIDEODECODER_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_encoder.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..108978c2cb99ae8f92a701fa19a2d7b39b81203d
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/include/video_encoder.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEOENCODER_H
+#define VIDEOENCODER_H
+
+#include "multimedia/player_framework/native_avcodec_videoencoder.h"
+#include "multimedia/player_framework/native_avbuffer_info.h"
+#include "sample_info.h"
+#include "native_window/external_window.h"
+#include "native_window/buffer_handle.h"
+#include "sample_callback.h"
+#include "dfx/error/av_codec_sample_error.h"
+#include "av_codec_sample_log.h"
+
+class VideoEncoder {
+public:
+ VideoEncoder() = default;
+ ~VideoEncoder();
+
+ int32_t Create(const std::string &videoCodecMime);
+ int32_t Config(SampleInfo &sampleInfo, CodecUserData *codecUserData);
+ int32_t Start();
+ int32_t PushInputBuffer(CodecBufferInfo &info);
+ int32_t FreeOutputBuffer(uint32_t bufferIndex);
+ int32_t NotifyEndOfStream();
+ int32_t Stop();
+ int32_t Release();
+
+private:
+ int32_t SetCallback(CodecUserData *codecUserData);
+ int32_t Configure(const SampleInfo &sampleInfo);
+ int32_t GetSurface(SampleInfo &sampleInfo);
+ bool isAVBufferMode_ = false;
+ OH_AVCodec *encoder_ = nullptr;
+};
+#endif // VIDEOENCODER_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/muxer.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/muxer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3218b36dfc596012012f2370fb1d1222d8f485a3
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/muxer.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "muxer.h"
+
+#undef LOG_TAG
+#define LOG_TAG "Muxer"
+
+namespace {
+constexpr int32_t VERTICAL_ANGLE = 90;
+constexpr int32_t HORIZONTAL_ANGLE = 0;
+}
+
+Muxer::~Muxer()
+{
+ Release();
+}
+
+int32_t Muxer::Create(int32_t fd)
+{
+ muxer_ = OH_AVMuxer_Create(fd, AV_OUTPUT_FORMAT_MPEG_4);
+ CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer create failed, fd: %{public}d", fd);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Muxer::Config(SampleInfo &sampleInfo)
+{
+ CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null");
+
+ OH_AVFormat *formatVideo = OH_AVFormat_CreateVideoFormat(sampleInfo.videoCodecMime.data(),
+ sampleInfo.videoWidth, sampleInfo.videoHeight);
+ CHECK_AND_RETURN_RET_LOG(formatVideo != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create video format failed");
+
+ OH_AVFormat_SetDoubleValue(formatVideo, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_WIDTH, sampleInfo.videoWidth);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight);
+ OH_AVFormat_SetStringValue(formatVideo, OH_MD_KEY_CODEC_MIME, sampleInfo.videoCodecMime.data());
+ if (sampleInfo.isHDRVivid) {
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_VIDEO_IS_HDR_VIVID, 1);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_RANGE_FLAG, sampleInfo.rangFlag);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_COLOR_PRIMARIES, sampleInfo.primary);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_TRANSFER_CHARACTERISTICS, sampleInfo.transfer);
+ OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_MATRIX_COEFFICIENTS, sampleInfo.matrix);
+ }
+
+ int32_t ret = OH_AVMuxer_AddTrack(muxer_, &videoTrackId_, formatVideo);
+ OH_AVFormat_Destroy(formatVideo);
+ // 由于相机只有1920×1080的profile,没有1080×1920的profile,所以得往文件里封装一个90度的角度信息,后续播放才会是竖屏显示。
+ OH_AVMuxer_SetRotation(muxer_, 90);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "AddTrack failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Muxer::Start()
+{
+ CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null");
+
+ int ret = OH_AVMuxer_Start(muxer_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Start failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Muxer::WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr)
+{
+ CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null");
+ CHECK_AND_RETURN_RET_LOG(buffer != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Get a empty buffer");
+
+ int32_t ret = OH_AVBuffer_SetBufferAttr(buffer, &attr);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "SetBufferAttr failed");
+
+ ret = OH_AVMuxer_WriteSampleBuffer(muxer_, videoTrackId_, buffer);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Write sample failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Muxer::Stop()
+{
+ CHECK_AND_RETURN_RET_LOG(muxer_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Muxer is null");
+
+ int32_t ret = OH_AVMuxer_Stop(muxer_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Muxer stop failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t Muxer::Release()
+{
+ if (muxer_ != nullptr) {
+ OH_AVMuxer_Destroy(muxer_);
+ muxer_ = nullptr;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_decoder.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_decoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f70330bb2366a19854535e76bf4eb4236ec1d23
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_decoder.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "video_decoder.h"
+
+#undef LOG_TAG
+#define LOG_TAG "VideoDecoder"
+
+namespace {
+constexpr int LIMIT_LOGD_FREQUENCY = 50;
+constexpr int ROTATION_ANGLE = 90;
+} // namespace
+
+VideoDecoder::~VideoDecoder() { Release(); }
+
+OH_AVCodec *VideoDecoder::GetCodecByCategory(const char *mime, bool isEncoder, OH_AVCodecCategory category)
+{
+ OH_AVCapability *capability = OH_AVCodec_GetCapabilityByCategory(mime, isEncoder, category);
+ CHECK_AND_RETURN_RET_LOG(capability != nullptr, nullptr, "Capability is nullptr");
+ const char *codecName = OH_AVCapability_GetName(capability);
+ return OH_VideoDecoder_CreateByName(codecName);
+}
+
+int32_t VideoDecoder::Create(const std::string &videoCodecMime, int32_t videoDecoderType)
+{
+ switch (videoDecoderType) {
+ case AUTO:
+ decoder_ = OH_VideoDecoder_CreateByMime(videoCodecMime.c_str());
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create failed");
+ break;
+ case VIDEO_HW_DECODER:
+ if (!strcmp(videoCodecMime.data(), "video/avc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE);
+ } else if (!strcmp(videoCodecMime.data(), "video/hevc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, false, HARDWARE);
+ } else if (!strcmp(videoCodecMime.data(), "video/vvc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_VVC, false, HARDWARE);
+ } else {
+ AVCODEC_SAMPLE_LOGE("INVALID MIMETYPE");
+ return AVCODEC_SAMPLE_ERR_ERROR;
+ }
+ break;
+ case VIDEO_SW_DECODER:
+ if (!strcmp(videoCodecMime.data(), "video/avc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, SOFTWARE);
+ } else if (!strcmp(videoCodecMime.data(), "video/hevc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, false, SOFTWARE);
+ } else if (!strcmp(videoCodecMime.data(), "video/vvc")) {
+ decoder_ = GetCodecByCategory(OH_AVCODEC_MIMETYPE_VIDEO_VVC, false, SOFTWARE);
+ } else {
+ AVCODEC_SAMPLE_LOGE("INVALID MIMETYPE");
+ return AVCODEC_SAMPLE_ERR_ERROR;
+ }
+ break;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::SetCallback(CodecUserData *codecUserData)
+{
+ int32_t ret = AV_ERR_OK;
+ ret = OH_VideoDecoder_RegisterCallback(decoder_,
+ {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
+ SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer},
+ codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set callback failed, ret: %{public}d", ret);
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::Configure(const SampleInfo &sampleInfo)
+{
+ OH_AVFormat *format = OH_AVFormat_Create();
+ CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "AVFormat create failed");
+
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, sampleInfo.videoWidth);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight);
+ OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, sampleInfo.rotation);
+
+ AVCODEC_SAMPLE_LOGI("====== VideoDecoder config ======");
+ AVCODEC_SAMPLE_LOGI("%{public}d*%{public}d, %{public}.1ffps", sampleInfo.videoWidth, sampleInfo.videoHeight,
+ sampleInfo.frameRate);
+ AVCODEC_SAMPLE_LOGI("====== VideoDecoder config ======");
+
+ int ret = OH_VideoDecoder_Configure(decoder_, format);
+ OH_AVFormat_Destroy(format);
+ format = nullptr;
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Config failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::Config(const SampleInfo &sampleInfo, CodecUserData *codecUserData)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+ CHECK_AND_RETURN_RET_LOG(codecUserData != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Invalid param: codecUserData");
+
+ // Configure video decoder
+ int32_t ret = Configure(sampleInfo);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Configure failed");
+
+ // SetSurface from video decoder
+ if (sampleInfo.window != nullptr) {
+ int ret = OH_VideoDecoder_SetSurface(decoder_, sampleInfo.window);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && sampleInfo.window, AVCODEC_SAMPLE_ERR_ERROR,
+ "Set surface failed, ret: %{public}d", ret);
+ }
+
+ // SetCallback for video decoder
+ ret = SetCallback(codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR,
+ "Set callback failed, ret: %{public}d", ret);
+
+ // Prepare video decoder
+ {
+ int ret = OH_VideoDecoder_Prepare(decoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Prepare failed, ret: %{public}d", ret);
+ }
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::Start()
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int ret = OH_VideoDecoder_Start(decoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Start failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::PushInputBuffer(CodecBufferInfo &info) {
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+ int32_t ret = OH_VideoDecoder_PushInputBuffer(decoder_, info.bufferIndex);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Push input data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::FreeOutputBuffer(uint32_t bufferIndex, bool render)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int32_t ret = AVCODEC_SAMPLE_ERR_OK;
+ if (render) {
+ ret = OH_VideoDecoder_RenderOutputBuffer(decoder_, bufferIndex);
+ } else {
+ ret = OH_VideoDecoder_FreeOutputBuffer(decoder_, bufferIndex);
+ }
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Free output data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::FreeOutputBuffer(uint32_t bufferIndex, bool render, int64_t timeStamp)
+{
+ CHECK_AND_RETURN_RET_LOG(decoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int32_t ret = AVCODEC_SAMPLE_ERR_OK;
+ if (render) {
+ ret = OH_VideoDecoder_RenderOutputBufferAtTime(decoder_, bufferIndex, timeStamp);
+ } else {
+ ret = OH_VideoDecoder_FreeOutputBuffer(decoder_, bufferIndex);
+ }
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Free output data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoDecoder::Release()
+{
+ if (decoder_ != nullptr) {
+ OH_VideoDecoder_Destroy(decoder_);
+ decoder_ = nullptr;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_encoder.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_encoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e6c64797a9c6c3a0f4e67a99092a076e336926d
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/capbilities/video_encoder.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "video_encoder.h"
+
+#undef LOG_TAG
+#define LOG_TAG "VideoEncoder"
+
+namespace {
+
+int32_t ToGraphicPixelFormat(int32_t avPixelFormat, bool isHDRVivid)
+{
+ if (isHDRVivid) {
+ return NATIVEBUFFER_PIXEL_FMT_YCBCR_P010;
+ }
+ switch (avPixelFormat) {
+ case AV_PIXEL_FORMAT_RGBA:
+ return NATIVEBUFFER_PIXEL_FMT_RGBA_8888;
+ case AV_PIXEL_FORMAT_YUVI420:
+ return NATIVEBUFFER_PIXEL_FMT_YCBCR_420_P;
+ case AV_PIXEL_FORMAT_NV21:
+ return NATIVEBUFFER_PIXEL_FMT_YCRCB_420_SP;
+ default: // NV12 and others
+ return NATIVEBUFFER_PIXEL_FMT_YCRCB_420_SP;
+ }
+}
+} // namespace
+
+VideoEncoder::~VideoEncoder()
+{
+ Release();
+}
+
+int32_t VideoEncoder::Create(const std::string &videoCodecMime)
+{
+ encoder_ = OH_VideoEncoder_CreateByMime(videoCodecMime.c_str());
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Create failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserData)
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null");
+ CHECK_AND_RETURN_RET_LOG(codecUserData != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Invalid param: codecUserData");
+
+ // Configure video encoder
+ int32_t ret = Configure(sampleInfo);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Configure failed");
+
+ // GetSurface from video encoder
+ ret = GetSurface(sampleInfo);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Get surface failed");
+
+ // SetCallback for video encoder
+ ret = SetCallback(codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AVCODEC_SAMPLE_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR,
+ "Set callback failed, ret: %{public}d", ret);
+
+ // Prepare video encoder
+ ret = OH_VideoEncoder_Prepare(encoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Prepare failed, ret: %{public}d", ret);
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::Start()
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null");
+
+ int ret = OH_VideoEncoder_Start(encoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Start failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::PushInputBuffer(CodecBufferInfo &info)
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Decoder is null");
+
+ int32_t ret = OH_AVBuffer_SetBufferAttr(reinterpret_cast(info.buffer), &info.attr);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set avbuffer attr failed");
+ ret = OH_VideoEncoder_PushInputBuffer(encoder_, info.bufferIndex);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Push input data failed");
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::FreeOutputBuffer(uint32_t bufferIndex)
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null");
+
+ int32_t ret = OH_VideoEncoder_FreeOutputBuffer(encoder_, bufferIndex);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR,
+ "Free output data failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::NotifyEndOfStream()
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null");
+
+ int32_t ret = OH_VideoEncoder_NotifyEndOfStream(encoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR,
+ "Notify end of stream failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::Stop()
+{
+ CHECK_AND_RETURN_RET_LOG(encoder_ != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "Encoder is null");
+
+ int ret = OH_VideoEncoder_Flush(encoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Flush failed, ret: %{public}d", ret);
+
+ ret = OH_VideoEncoder_Stop(encoder_);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Stop failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::Release()
+{
+ if (encoder_ != nullptr) {
+ OH_VideoEncoder_Destroy(encoder_);
+ encoder_ = nullptr;
+ }
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::SetCallback(CodecUserData *codecUserData)
+{
+ int32_t ret = OH_VideoEncoder_RegisterCallback(encoder_,
+ {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
+ SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer},
+ codecUserData);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Set callback failed, ret: %{public}d", ret);
+
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::Configure(const SampleInfo &sampleInfo)
+{
+ OH_AVFormat *format = OH_AVFormat_Create();
+ CHECK_AND_RETURN_RET_LOG(format != nullptr, AVCODEC_SAMPLE_ERR_ERROR, "AVFormat create failed");
+
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, sampleInfo.videoWidth);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight);
+ OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, sampleInfo.pixelFormat);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, sampleInfo.bitrateMode);
+ OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, sampleInfo.bitrate);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, sampleInfo.hevcProfile);
+ if (sampleInfo.isHDRVivid) {
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, sampleInfo.iFrameInterval);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, sampleInfo.rangFlag);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, sampleInfo.primary);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, sampleInfo.transfer);
+ OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, sampleInfo.matrix);
+ }
+ AVCODEC_SAMPLE_LOGI("====== VideoEncoder config ======");
+ AVCODEC_SAMPLE_LOGI("%{public}d*%{public}d, %{public}.1ffps",
+ sampleInfo.videoWidth, sampleInfo.videoHeight, sampleInfo.frameRate);
+ // 1024: ratio of kbps to bps
+ AVCODEC_SAMPLE_LOGI("BitRate Mode: %{public}d, BitRate: %{public}" PRId64 "kbps",
+ sampleInfo.bitrateMode, sampleInfo.bitrate / 1024);
+ AVCODEC_SAMPLE_LOGI("====== VideoEncoder config ======");
+
+ int ret = OH_VideoEncoder_Configure(encoder_, format);
+ OH_AVFormat_Destroy(format);
+ format = nullptr;
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, "Config failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
+int32_t VideoEncoder::GetSurface(SampleInfo &sampleInfo)
+{
+ int32_t ret = OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.window);
+ CHECK_AND_RETURN_RET_LOG(ret == AV_ERR_OK && sampleInfo.window, AVCODEC_SAMPLE_ERR_ERROR,
+ "Get surface failed, ret: %{public}d", ret);
+ return AVCODEC_SAMPLE_ERR_OK;
+}
+
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/error/av_codec_sample_error.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/error/av_codec_sample_error.h
new file mode 100644
index 0000000000000000000000000000000000000000..243380754cabfd20d5c35dedd32c18b335dd8870
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/error/av_codec_sample_error.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVCODEC_SAMPLE_ERROE_H
+#define AVCODEC_SAMPLE_ERROE_H
+
+enum AVCodecSampleError : int {
+ AVCODEC_SAMPLE_ERR_OK = 0,
+ AVCODEC_SAMPLE_ERR_ERROR = -1,
+};
+
+#endif // AVCODEC_SAMPLE_ERROE_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/log/av_codec_sample_log.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/log/av_codec_sample_log.h
new file mode 100644
index 0000000000000000000000000000000000000000..6da1a6dc302ca88e713bd7c6044334437c2b78e7
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/dfx/log/av_codec_sample_log.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVCODEC_SAMPLE_LOG_H
+#define AVCODEC_SAMPLE_LOG_H
+
+#include
+#include
+
+#undef LOG_DOMAIN
+#define LOG_DOMAIN 0x0002B66
+
+#define AVCODEC_SAMPLE_LOG_FREQ_LIMIT(frequency) \
+ if (1) { \
+ thread_local uint64_t currentTimes = 0; \
+ if (currentTimes++ % ((uint64_t)(frequency)) != 0) { \
+ break; \
+ } \
+ }
+
+#define AVCODEC_SAMPLE_LOG(func, fmt, args...) \
+ do { \
+ (void)func(LOG_APP, "{%{public}s():%{public}d} " fmt, __FUNCTION__, __LINE__, ##args); \
+ } while (0)
+
+#define AVCODEC_SAMPLE_LOGF(fmt, ...) AVCODEC_SAMPLE_LOG(OH_LOG_FATAL, fmt, ##__VA_ARGS__)
+#define AVCODEC_SAMPLE_LOGE(fmt, ...) AVCODEC_SAMPLE_LOG(OH_LOG_ERROR, fmt, ##__VA_ARGS__)
+#define AVCODEC_SAMPLE_LOGW(fmt, ...) AVCODEC_SAMPLE_LOG(OH_LOG_WARN, fmt, ##__VA_ARGS__)
+#define AVCODEC_SAMPLE_LOGI(fmt, ...) AVCODEC_SAMPLE_LOG(OH_LOG_INFO, fmt, ##__VA_ARGS__)
+#define AVCODEC_SAMPLE_LOGD(fmt, ...) AVCODEC_SAMPLE_LOG(OH_LOG_DEBUG, fmt, ##__VA_ARGS__)
+#define AVCODEC_SAMPLE_LOGD_LIMIT(frequency, fmt, ...) \
+ do { \
+ AVCODEC_SAMPLE_LOG_FREQ_LIMIT(frequency); \
+ AVCODEC_SAMPLE_LOGD(fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define CHECK_AND_RETURN_RET_LOG(cond, ret, fmt, ...) \
+ do { \
+ if (!(cond)) { \
+ AVCODEC_SAMPLE_LOGE(fmt, ##__VA_ARGS__); \
+ return ret; \
+ } \
+ } while (0)
+
+#define CHECK_AND_RETURN_LOG(cond, fmt, ...) \
+ do { \
+ if (!(cond)) { \
+ AVCODEC_SAMPLE_LOGE(fmt, ##__VA_ARGS__); \
+ return; \
+ } \
+ } while (0)
+
+#define CHECK_AND_BREAK_LOG(cond, fmt, ...) \
+ if (1) { \
+ if (!(cond)) { \
+ AVCODEC_SAMPLE_LOGW(fmt, ##__VA_ARGS__); \
+ break; \
+ } \
+ } else void (0)
+
+#define CHECK_AND_CONTINUE_LOG(cond, fmt, ...) \
+ if (1) { \
+ if (!(cond)) { \
+ AVCODEC_SAMPLE_LOGW(fmt, ##__VA_ARGS__); \
+ continue; \
+ } \
+ } else void (0)
+
+#endif // AVCODEC_SAMPLE_LOG_H
\ No newline at end of file
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.cpp b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..510ff78a1ea2d991d8f0ad5f0b67b0aca69cd499
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sample_callback.h"
+#include "av_codec_sample_log.h"
+
+namespace {
+constexpr int LIMIT_LOGD_FREQUENCY = 50;
+constexpr int32_t BYTES_PER_SAMPLE_2 = 2;
+} // namespace
+
+// 自定义写入数据函数
+int32_t SampleCallback::OnRenderWriteData(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t length)
+{
+ (void)renderer;
+ (void)length;
+ CodecUserData *codecUserData = static_cast(userData);
+
+ // 将待播放的数据,按length长度写入buffer
+ uint8_t *dest = (uint8_t *)buffer;
+ size_t index = 0;
+ std::unique_lock lock(codecUserData->outputMutex);
+ // 从队列中取出需要播放的长度为length的数据
+ while (!codecUserData->renderQueue.empty() && index < length) {
+ dest[index++] = codecUserData->renderQueue.front();
+ codecUserData->renderQueue.pop();
+ }
+ AVCODEC_SAMPLE_LOGD("render BufferLength:%{public}d Out buffer count: %{public}u, renderQueue.size: %{public}u "
+ "renderReadSize: %{public}u",
+ length, codecUserData->outputFrameCount, codecUserData->renderQueue.size(), index);
+
+ codecUserData->frameWrittenForSpeed +=
+ length / codecUserData->speed / codecUserData->sampleInfo->audioChannelCount / BYTES_PER_SAMPLE_2;
+ codecUserData->currentPosAudioBufferPts =
+ codecUserData->endPosAudioBufferPts - codecUserData->renderQueue.size() /
+ codecUserData->sampleInfo->audioSampleRate /
+ codecUserData->sampleInfo->audioChannelCount / BYTES_PER_SAMPLE_2;
+
+ if (codecUserData->renderQueue.size() < length) {
+ codecUserData->renderCond.notify_all();
+ }
+ return 0;
+}
+// 自定义音频流事件函数
+int32_t SampleCallback::OnRenderStreamEvent(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Event event)
+{
+ (void)renderer;
+ (void)userData;
+ (void)event;
+ // 根据event表示的音频流事件信息,更新播放器状态和界面
+ return 0;
+}
+// 自定义音频中断事件函数
+int32_t SampleCallback::OnRenderInterruptEvent(OH_AudioRenderer *renderer, void *userData,
+ OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint)
+{
+ (void)renderer;
+ (void)userData;
+ (void)type;
+ (void)hint;
+ // 根据type和hint表示的音频中断信息,更新播放器状态和界面
+ return 0;
+}
+// 自定义异常回调函数
+int32_t SampleCallback::OnRenderError(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Result error)
+{
+ (void)renderer;
+ (void)userData;
+ (void)error;
+ AVCODEC_SAMPLE_LOGE("OnRenderError");
+ // 根据error表示的音频异常信息,做出相应的处理
+ return 0;
+}
+
+void SampleCallback::OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData)
+{
+ (void)codec;
+ (void)errorCode;
+ (void)userData;
+ AVCODEC_SAMPLE_LOGE("On codec error, error code: %{public}d", errorCode);
+}
+
+void SampleCallback::OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
+{
+ AVCODEC_SAMPLE_LOGI("On codec format change");
+}
+
+void SampleCallback::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ if (userData == nullptr) {
+ return;
+ }
+ (void)codec;
+ CodecUserData *codecUserData = static_cast(userData);
+ std::unique_lock lock(codecUserData->inputMutex);
+ if (codecUserData->isEncFirstFrame) {
+ OH_AVFormat *format = OH_VideoEncoder_GetInputDescription(codec);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_WIDTH, &codecUserData->width);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &codecUserData->height);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &codecUserData->widthStride);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &codecUserData->heightStride);
+ OH_AVFormat_Destroy(format);
+ codecUserData->isEncFirstFrame = false;
+ }
+ codecUserData->inputBufferInfoQueue.emplace(index, buffer);
+ codecUserData->inputCond.notify_all();
+}
+
+void SampleCallback::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
+{
+ if (userData == nullptr) {
+ return;
+ }
+ (void)codec;
+ CodecUserData *codecUserData = static_cast(userData);
+ std::unique_lock lock(codecUserData->outputMutex);
+ if (codecUserData->isDecFirstFrame) {
+ OH_AVFormat *format = OH_VideoDecoder_GetOutputDescription(codec);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_WIDTH, &codecUserData->width);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &codecUserData->height);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &codecUserData->widthStride);
+ OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &codecUserData->heightStride);
+ OH_AVFormat_Destroy(format);
+ codecUserData->isDecFirstFrame = false;
+ }
+ codecUserData->outputBufferInfoQueue.emplace(index, buffer);
+ codecUserData->outputCond.notify_all();
+}
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.h
new file mode 100644
index 0000000000000000000000000000000000000000..43633bf737b4c083282533d90ff356d044a86641
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_callback.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVCODEC_SAMPLE_CALLBACK_H
+#define AVCODEC_SAMPLE_CALLBACK_H
+
+#include
+#include
+#include
+#include "sample_info.h"
+class SampleCallback {
+public:
+ static int32_t OnRenderWriteData(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t length);
+ static int32_t OnRenderStreamEvent(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Event event);
+ static int32_t OnRenderInterruptEvent(OH_AudioRenderer *renderer, void *userData, OH_AudioInterrupt_ForceType type,
+ OH_AudioInterrupt_Hint hint);
+ static int32_t OnRenderError(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Result error);
+
+ static void OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData);
+ static void OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData);
+ static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
+ static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
+};
+
+#endif // AVCODEC_SAMPLE_CALLBACK_H
diff --git a/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_info.h b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_info.h
new file mode 100644
index 0000000000000000000000000000000000000000..65171576eea69e9a17e2ea3b2ea66bb8e0483e41
--- /dev/null
+++ b/code/BasicFeature/Media/AVCodec/entry/src/main/cpp/common/sample_info.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ */
+/*
+ * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVCODEC_SAMPLE_INFO_H
+#define AVCODEC_SAMPLE_INFO_H
+#include
+#include
+#include