• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![Invoking relationship of video decode stream](figures/video-decode.png)
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