1# 视频解码 2 3开发者可以调用本模块的Native API接口,完成视频解码,即将媒体数据解码成YUV文件或送显。 4 5当前支持的解码能力如下: 6 7| 容器规格 | 视频硬解类型 | 视频软解类型 | 8| -------- | --------------------- | ---------------- | 9| mp4 | AVC(H.264)、HEVC(H.265) |AVC(H.264) | 10 11 12视频解码软/硬件解码存在差异,基于MimeType创建解码器时,软解当前仅支持 H264 ("video/avc"),硬解则支持 H264 ("video/avc") 和 H265 ("video/hevc")。 13 14## 开发指导 15 16详细的API说明请参考[API文档](../reference/native-apis/_video_decoder.md)。 17如下为视频解码调用关系图: 18 19 20### 在 CMake 脚本中链接动态库 21``` cmake 22target_link_libraries(sample PUBLIC libnative_media_codecbase.so) 23target_link_libraries(sample PUBLIC libnative_media_core.so) 24target_link_libraries(sample PUBLIC libnative_media_vdec.so) 25``` 26 27### 开发步骤 28 291. 创建编解码器实例对象。 30 31 应用可以通过名称或媒体类型创建解码器。 32 33 ``` c++ 34 // 通过 codecname 创建解码器, 应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。 35 OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false); 36 const char *name = OH_AVCapability_GetName(capability); 37 OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name); 38 ``` 39 ```c++ 40 // 通过 mimetype 创建解码器 41 // 软/硬解: 创建 H264 解码器,存在多个可选解码器时,系统会创建最合适的解码器 42 OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC); 43 // 硬解: 创建 H265 解码器 44 OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_HEVC); 45 ``` 46 ``` c++ 47 // 初始化队列 48 class VDecSignal { 49 public: 50 std::mutex inMutex_; 51 std::mutex outMutex_; 52 std::condition_variable inCond_; 53 std::condition_variable outCond_; 54 std::queue<uint32_t> inQueue_; 55 std::queue<uint32_t> outQueue_; 56 std::queue<OH_AVMemory *> inBufferQueue_; 57 std::queue<OH_AVMemory *> outBufferQueue_; 58 std::queue<OH_AVCodecBufferAttr> attrQueue_; 59 }; 60 VDecSignal *signal_; 61 ``` 62 632. 调用OH_VideoDecoder_SetCallback()设置回调函数。 64 65 注册回调函数指针集合OH_AVCodecAsyncCallback,包括: 66 67 - 解码器运行错误 68 - 码流信息变化,如码流宽、高变化。 69 - 运行过程中需要新的输入数据,即解码器已准备好,可以输入数据。 70 - 运行过程中产生了新的输出数据,即解码完成。(注:Surface模式data参数为空) 71 72 开发者可以通过处理该回调报告的信息,确保解码器正常运转。 73 74 ``` c++ 75 // 解码异常回调OH_AVCodecOnError实现 76 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) 77 { 78 (void)codec; 79 (void)errorCode; 80 (void)userData; 81 } 82 83 // 解码数据流变化回调OH_AVCodecOnStreamChanged实现 84 static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) 85 { 86 (void)codec; 87 (void)format; 88 (void)userData; 89 } 90 91 // 解码输入回调OH_AVCodecOnNeedInputData实现 92 static void OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData) 93 { 94 (void)codec; 95 VDecSignal *signal_ = static_cast<VDecSignal *>(userData); 96 std::unique_lock<std::mutex> lock(signal_->inMutex_); 97 // 解码输入帧id送入 inQueue_ 98 signal_->inQueue_.push(index); 99 // 解码输入帧数据送入 inBufferQueue_ 100 signal_->inBufferQueue_.push(data); 101 signal_->inCond_.notify_all(); 102 } 103 104 // 解码输出回调OH_AVCodecOnNewOutputData实现 105 static void OnNeedOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, 106 void *userData) 107 { 108 (void)codec; 109 VDecSignal *signal_ = static_cast<VDecSignal *>(userData); 110 std::unique_lock<std::mutex> lock(signal_->outMutex_); 111 // 将对应输出 buffer 的 index 送入 outQueue_ 112 signal_->outQueue_.push(index); 113 // 将对应解码完成的数据 data 送入 outBufferQueue_ (注: Surface模式下data为空) 114 signal_->outBufferQueue_.push(data); 115 signal_->attrQueue_.push(*attr); 116 signal_->outCond_.notify_all(); 117 } 118 OH_AVCodecAsyncCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputData, &OnNeedOutputData}; 119 // 配置异步回调 120 int32_t ret = OH_VideoDecoder_SetCallback(videoDec, cb, signal_); 121 ``` 122 1233. 调用OH_VideoDecoder_Configure()配置解码器。 124 125 配置必选项:视频帧宽度、视频帧高度、视频颜色格式。 126 127 ``` c++ 128 // 配置视频帧宽度(必须) 129 constexpr uint32_t DEFAULT_WIDTH = 320; 130 // 配置视频帧高度(必须) 131 constexpr uint32_t DEFAULT_HEIGHT = 240; 132 OH_AVFormat *format = OH_AVFormat_Create(); 133 // 写入 format 134 OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, DEFAULT_WIDTH); 135 OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, DEFAULT_HEIGHT); 136 OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_NV21); 137 // 配置解码器 138 int32_t ret = OH_VideoDecoder_Configure(videoDec, format); 139 OH_AVFormat_Destroy(format); 140 ``` 141 1424. (如需使用Surface送显,必须设置)设置Surface。应用需要从XComponent组件获取 nativeWindow,获取方式请参考 [XComponent](../reference/arkui-ts/ts-basic-components-xcomponent.md) 143 144 ``` c++ 145 // 配置送显窗口参数 146 int32_t ret = OH_VideoDecoder_SetSurface(videoDec, window); // 从 XComponent 获取 window 147 bool isSurfaceMode = true; 148 ``` 149 1505. (仅使用Surface时可配置)配置解码器surface参数。 151 152 ``` c++ 153 OH_AVFormat *format = OH_AVFormat_Create(); 154 // 配置显示旋转角度 155 OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, 90); 156 // 配置视频与显示屏匹配模式(缩放与显示窗口适配, 裁剪与显示窗口适配) 157 OH_AVFormat_SetIntValue(format, OH_MD_KEY_SCALING_MODE, SCALING_MODE_SCALE_CROP); 158 int32_t ret = OH_VideoDecoder_SetParameter(videoDec, format); 159 OH_AVFormat_Destroy(format); 160 ``` 161 1626. 调用OH_VideoDecoder_Start()启动解码器。 163 164 ``` c++ 165 string_view outputFilePath = "/*yourpath*.yuv"; 166 std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>(); 167 // 打开待解码二进制文件路径 168 inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary); 169 // buffer 模式下需要配置 170 if(!isSurfaceMode) { 171 // buffer 模式: 配置解码文件输出路径 172 std::unique_ptr<std::ofstream> outFile = std::make_unique<std::ofstream>(); 173 outFile->open(outputFilePath.data(), std::ios::out | std::ios::binary); 174 } 175 // 开始解码 176 int32_t ret = OH_VideoDecoder_Start(videoDec); 177 ``` 178 1797. 调用OH_VideoDecoder_PushInputData(),写入解码码流。 180 181 ``` c++ 182 // 配置 buffer info 信息 183 OH_AVCodecBufferAttr info; 184 // 调用 FFmpeg 接口 av_packet_alloc 进行初始化并返回一个容器 pkt 185 AVPacket pkt = av_packet_alloc(); 186 // 配置 info 的输入尺寸、偏移量、时间戳等字段信息 187 info.size = pkt->size; 188 info.offset = 0; 189 info.pts = pkt->pts; 190 info.flags = AVCODEC_BUFFER_FLAGS_NONE; 191 // 送入解码输入队列进行解码, index 为对应队列下标 192 int32_t ret = OH_VideoDecoder_PushInputData(videoDec, index, info); 193 ``` 194 1958. surface模式显示场景,调用OH_VideoDecoder_RenderOutputData()显示并释放解码帧; 196 surface模式不显示场景和buffer模式,调用OH_VideoDecoder_FreeOutputData()释放解码帧。 197 198 ``` c++ 199 int32_t ret; 200 // 将解码完成数据 data 写入到对应输出文件中 201 outFile->write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), data.size); 202 // buffer 模式, 释放已完成写入的数据, index 为对应 surface/buffer 队列下标 203 if (isSurfaceMode && isRender) { 204 ret = OH_VideoDecoder_RenderOutputData(videoDec, index); 205 } else { 206 ret = OH_VideoDecoder_FreeOutputData(videoDec, index); 207 } 208 if (ret != AV_ERR_OK) { 209 // 异常处理 210 } 211 ``` 212 2139. (可选)调用OH_VideoDecoder_Flush()刷新解码器。 214 215 调用OH_VideoDecoder_Flush()后,解码器仍处于运行态,但会将当前队列清空,将已解码的数据释放。 216 217 此时需要调用OH_VideoDecoder_Start()重新开始解码。 218 219 ``` c++ 220 int32_t ret; 221 // 刷新解码器 videoDec 222 ret = OH_VideoDecoder_Flush(videoDec); 223 if (ret != AV_ERR_OK) { 224 // 异常处理 225 } 226 // 重新开始解码 227 ret = OH_VideoDecoder_Start(videoDec); 228 ``` 229 23010. (可选)调用OH_VideoDecoder_Reset()重置解码器。 231 232 调用OH_VideoDecoder_Reset()后,解码器回到初始化的状态,需要调用OH_VideoDecoder_Configure()重新配置。 233 234 ``` c++ 235 int32_t ret; 236 // 重置解码器 videoDec 237 ret = OH_VideoDecoder_Reset(videoDec); 238 if (ret != AV_ERR_OK) { 239 // 异常处理 240 } 241 // 重新配置解码器参数 242 ret = OH_VideoDecoder_Configure(videoDec, format); 243 ``` 244 24511. 调用OH_VideoDecoder_Stop()停止解码器。 246 247 ``` c++ 248 int32_t ret; 249 // 终止解码器 videoDec 250 ret = OH_VideoDecoder_Stop(videoDec); 251 if (ret != AV_ERR_OK) { 252 // 异常处理 253 } 254 ``` 255 25612. 调用OH_VideoDecoder_Destroy()销毁解码器实例,释放资源。 257 258 ``` c++ 259 int32_t ret; 260 // 调用 OH_VideoDecoder_Destroy, 注销解码器 261 ret = OH_VideoDecoder_Destroy(videoDec); 262 if (ret != AV_ERR_OK) { 263 // 异常处理 264 } 265 ``` 266 267