• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 视频解码
2
3<!--Kit: AVCodec Kit-->
4<!--Subsystem: Multimedia-->
5<!--Owner: @zhanghongran-->
6<!--Designer: @dpy2650--->
7<!--Tester: @cyakee-->
8<!--Adviser: @zengyawen-->
9
10开发者可以调用本模块的Native API接口,完成视频解码,即将媒体数据解码成YUV文件或送显。
11
12<!--RP3--><!--RP3End-->
13
14当前支持的解码能力请参考[AVCodec支持的格式](avcodec-support-formats.md#视频解码)。
15
16<!--RP1--><!--RP1End-->
17
18通过视频解码,应用可以实现以下重点能力,包括:
19
20|          支持的能力                       |             使用简述                                                                     |
21| --------------------------------------- | ---------------------------------------------------------------------------------- |
22| 变分辨率         | 解码器支持输入码流分辨率发生变化,发生变化后会触发OH_VideoDecoder_RegisterCallback接口设置的回调函数OnStreamChanged()。具体可参考下文中:Surface模式步骤-3或Buffer模式步骤-3  |
23| 动态切换surface  | 通过调用OH_VideoDecoder_SetSurface接口配置,仅Surface模式支持。具体可参考下文中:Surface模式步骤-6    |
24| 低时延解码  | 通过调用OH_VideoDecoder_Configure接口配置,具体可参考下文中:Surface模式的步骤-5或Buffer模式步骤-5      |
25
26## 限制约束
27
281. Flush,Reset,Stop之后,重新Start时,需要重新传PPS/SPS。具体示例请参考[Surface模式](#surface模式)“步骤-13:调用OH_VideoDecoder_Flush()”。
292. Flush,Reset,Stop,Destroy在非回调线程中执行时,会等待所有回调执行完成后,将执行结果返回给开发者。
303. 由于硬件解码器资源有限,每个解码器在使用完毕后都必须调用OH_VideoDecoder_Destroy接口来销毁实例并释放资源。
314. 视频解码输入码流仅支持AnnexB格式,且支持的AnnexB格式支持多slice,要求同一帧的多个slice一次送入解码器。
325. 在调用Flush,Reset,Stop的过程中,开发者不应对之前回调函数获取到的OH_AVBuffer继续进行操作。
336. DRM解密能力在[Surface模式](#surface模式)下既支持非安全视频通路,也支持安全视频通路,在[Buffer模式](#buffer模式)下仅支持非安全视频通路。
347. Buffer模式和Surface模式使用方式一致的接口,所以只提供了Surface模式的示例。
358. 在Buffer模式下,开发者通过输出回调函数OH_AVCodecOnNewOutputBuffer获取到OH_AVBuffer的指针实例后,必须通过调用OH_VideoDecoder_FreeOutputBuffer接口来通知系统该实例已被使用完毕。这样系统才能够将后续解码的数据写入到相应的位置。如果开发者在调用OH_AVBuffer_GetNativeBuffer接口时获取到OH_NativeBuffer指针实例,并且该实例的生命周期超过了当前的OH_AVBuffer指针实例,那么需要进行一次数据的拷贝操作。在这种情况下,开发者需要自行管理新生成的OH_NativeBuffer实例的生命周期,确保其正确使用和释放。
36<!--RP6--><!--RP6End-->
37
38## surface输出与buffer输出
39
401. 两者数据的输出方式不同。
412. 两者的适用场景不同:
42    - surface输出是指用OHNativeWindow来传递输出数据,可以与其他模块对接,例如XComponent。
43    - buffer输出是指经过解码的数据会以共享内存的方式输出。
44
453. 在接口调用的过程中,两种方式的接口调用方式基本一致,但存在以下差异点:
46    - 在Surface模式下,可选择调用OH_VideoDecoder_FreeOutputBuffer接口丢弃输出帧(不送显);在Buffer模式下,应用必须调用OH_VideoDecoder_FreeOutputBuffer接口释放数据。
47    - Surface模式下,应用在解码器就绪前,必须调用OH_VideoDecoder_SetSurface接口设置OHNativeWindow,启动后,调用OH_VideoDecoder_RenderOutputBuffer接口将解码数据送显。
48    - 输出回调传出的buffer,在Buffer模式下,可以获取共享内存的地址和数据信息;在Surface模式下,只能获取buffer的数据信息。
49
504. Surface模式的数据流转性能优于Buffer模式。
51
52两种模式的开发步骤详细说明请参考:[Surface模式](#surface模式)和[Buffer模式](#buffer模式)。
53
54## 状态机调用关系
55
56如下为状态机调用关系图:
57
58![Invoking relationship of state](figures/state-invocation.png)
59
601. 有两种方式可以使解码器进入Initialized状态:
61   - 初始创建解码器实例时,解码器处于Initialized状态。
62   - 任何状态下,调用OH_VideoDecoder_Reset接口,解码器将会移回Initialized状态。
63
642. Initialized状态下,调用OH_VideoDecoder_Configure接口配置解码器,配置成功后解码器进入Configured状态。
653. Configured状态下,调用OH_VideoDecoder_Prepare接口进入Prepared状态。
664. Prepared状态下,调用OH_VideoDecoder_Start接口使解码器进入Executing状态:
67   - 处于Executing状态时,调用OH_VideoDecoder_Stop接口可以使解码器返回到Prepared状态。
68
695. 在极少数情况下,解码器可能会遇到错误并进入Error状态。解码器的错误传递,可以通过队列操作返回无效值或者抛出异常:
70   - Error状态下,可以调用解码器OH_VideoDecoder_Reset接口将解码器移到Initialized状态;或者调用OH_VideoDecoder_Destroy接口移动到最后的Released状态。
71
726. Executing状态具有三个子状态:Flushed、Running和End-of-Stream:
73    - 在调用了OH_VideoDecoder_Start接口之后,解码器立即进入Running子状态。
74    - 对于处于Executing状态的解码器,可以调用OH_VideoDecoder_Flush接口返回到Flushed子状态。
75    - 当待处理数据全部传递给解码器后,在input buffers队列中为最后一个入队的input buffer中添加[AVCODEC_BUFFER_FLAGS_EOS](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags-1)标记,遇到这个标记时,解码器会转换为End-of-Stream子状态。在此状态下,解码器不再接受新的输入,但是仍然会继续生成输出,直到输出到达尾帧。
76
777. 使用完解码器后,必须调用OH_VideoDecoder_Destroy接口销毁解码器实例,使解码器进入Released状态。
78
79## 开发指导
80
81详细的API说明请参考[API文档](../../reference/apis-avcodec-kit/_video_decoder.md)。
82如下为视频解码调用关系图:
83
84- 虚线表示可选。
85
86- 实线表示必选。
87
88![Invoking relationship of video decode stream](figures/video-decode.png)
89
90### 在 CMake 脚本中链接动态库
91
92``` cmake
93target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
94target_link_libraries(sample PUBLIC libnative_media_core.so)
95target_link_libraries(sample PUBLIC libnative_media_vdec.so)
96```
97
98> **说明:**
99>
100> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。
101>
102
103### 定义基础结构
104
105本部分示例代码按照C++17标准编写,仅作参考。开发者可以参考此部分,定义自己的buffer对象。
106
1071. 添加头文件。
108
109    ```c++
110    #include <condition_variable>
111    #include <memory>
112    #include <mutex>
113    #include <queue>
114    #include <shared_mutex>
115    ```
116
1172. 解码器回调buffer的信息。
118
119    ```c++
120    struct CodecBufferInfo {
121        CodecBufferInfo(uint32_t index, OH_AVBuffer *buffer): index(index), buffer(buffer), isValid(true) {}
122        // 回调buffer。
123        OH_AVBuffer *buffer = nullptr;
124        // 回调buffer对应的index。
125        uint32_t index = 0;
126        // 判断当前buffer信息是否有效。
127        bool isValid = true;
128    };
129    ```
130
1313. 解码输入输出队列。
132
133    ```c++
134    class CodecBufferQueue {
135    public:
136        // 将回调buffer的信息传入队列。
137        void Enqueue(const std::shared_ptr<CodecBufferInfo> bufferInfo)
138        {
139            std::unique_lock<std::mutex> lock(mutex_);
140            bufferQueue_.push(bufferInfo);
141            cond_.notify_all();
142        }
143
144        // 获取回调buffer的信息。
145        std::shared_ptr<CodecBufferInfo> Dequeue(int32_t timeoutMs = 1000)
146        {
147            std::unique_lock<std::mutex> lock(mutex_);
148            (void)cond_.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() { return !bufferQueue_.empty(); });
149            if (bufferQueue_.empty()) {
150                return nullptr;
151            }
152            std::shared_ptr<CodecBufferInfo> bufferInfo = bufferQueue_.front();
153            bufferQueue_.pop();
154            return bufferInfo;
155        }
156
157        // 清空队列,之前的回调buffer设置为不可用。
158        void Flush()
159        {
160            std::unique_lock<std::mutex> lock(mutex_);
161            while (!bufferQueue_.empty()) {
162                std::shared_ptr<CodecBufferInfo> bufferInfo = bufferQueue_.front();
163                // Flush、Stop、Reset、Destroy操作之后,之前回调的buffer信息设置为无效。
164                bufferInfo->isValid = false;
165                bufferQueue_.pop();
166            }
167        }
168
169    private:
170        std::mutex mutex_;
171        std::condition_variable cond_;
172        std::queue<std::shared_ptr<CodecBufferInfo>> bufferQueue_;
173    };
174    ```
175
1764. 全局变量。
177
178    仅作参考,可以根据实际情况将其封装到对象中。
179
180    ```c++
181    // 视频帧宽度。
182    int32_t width = 320;
183    // 视频帧高度。
184    int32_t height = 240;
185    // 视频像素格式。
186     OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12;
187    // 视频宽跨距。
188    int32_t widthStride = 0;
189    // 视频高跨距。
190    int32_t heightStride = 0;
191    // 解码器实例指针。
192    OH_AVCodec *videoDec = nullptr;
193    // 解码器同步锁。
194    std::shared_mutex codecMutex;
195    // 解码器输入队列。
196    CodecBufferQueue inQueue;
197    // 解码器输出队列。
198    CodecBufferQueue outQueue;
199    ```
200
201### Surface模式
202
203参考以下示例代码,可以完成Surface模式下视频解码的全流程,实现异步模式的数据轮转。此处以输入H.264码流文件,解码送显输出为例。
204
2051. 添加头文件。
206
207    ```c++
208    #include <multimedia/player_framework/native_avcodec_videodecoder.h>
209    #include <multimedia/player_framework/native_avcapability.h>
210    #include <multimedia/player_framework/native_avcodec_base.h>
211    #include <multimedia/player_framework/native_avformat.h>
212    #include <multimedia/player_framework/native_avbuffer.h>
213    #include <fstream>
214    ```
215
2162. 创建解码器实例。
217
218    开发者可以通过名称或媒体类型创建解码器。示例中的变量说明如下:
219
220    - videoDec:视频解码器实例的指针;
221    - capability:解码器能力查询实例的指针;
222    - OH_AVCODEC_MIMETYPE_VIDEO_AVC:AVC格式视频编解码器。
223
224    ```c++
225    // 通过codecname创建解码器,应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。
226    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
227    // 创建硬件解码器实例。
228    OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE);
229    const char *name = OH_AVCapability_GetName(capability);
230    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
231    ```
232
233    ```c++
234    // 通过MIME TYPE创建解码器,只能创建系统推荐的特定编解码器。
235    // 涉及创建多路编解码器时,优先创建硬件解码器实例,硬件资源不够时再创建软件解码器实例。
236    // 软/硬解:创建H.264解码器实例。
237    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
238    // 软/硬解:创建H.265解码器实例。
239    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_HEVC);
240    ```
241
2423. 调用OH_VideoDecoder_RegisterCallback()设置回调函数。
243
244    注册回调函数指针集合OH_AVCodecCallback,包括:
245
246    - OH_AVCodecOnError 解码器运行错误,返回的错误码详情请参见:[OH_AVCodecOnError](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodeconerror);
247    - OH_AVCodecOnStreamChanged 码流信息变化,如码流宽、高变化;
248    - OH_AVCodecOnNeedInputBuffer 运行过程中需要新的输入数据,即解码器已准备好,可以输入数据;
249    - OH_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据,即解码完成。
250
251    开发者可以通过处理该回调报告的信息,确保解码器正常运转。
252
253    <!--RP2--><!--RP2End-->
254
255    ```c++
256    // 解码异常回调OH_AVCodecOnError实现。
257    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
258    {
259        // 回调的错误码由开发者判断处理。
260        (void)codec;
261        (void)errorCode;
262        (void)userData;
263    }
264
265    // 解码数据流变化回调OH_AVCodecOnStreamChanged实现。
266    static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
267    {
268        // 可通过format获取到变化后的视频宽、高等。
269        (void)codec;
270        (void)userData;
271        bool ret = OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
272                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &height);
273        if (!ret) {
274         	// 异常处理。
275        }
276    }
277
278    // 解码输入回调OH_AVCodecOnNeedInputBuffer实现。
279    static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
280    {
281        // 输入帧的数据buffer和对应的index送入inQueue队列。
282        (void)codec;
283        (void)userData;
284        inQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
285    }
286
287    // 解码输出回调OH_AVCodecOnNewOutputBuffer实现。
288    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
289    {
290        // 完成帧的数据buffer和对应的index送入outQueue队列。
291        (void)codec;
292        (void)userData;
293        outQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
294    }
295    // 配置异步回调,调用 OH_VideoDecoder_RegisterCallback 接口。
296    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
297    // 配置异步回调。
298    OH_AVErrCode ret = OH_VideoDecoder_RegisterCallback(videoDec, cb, nullptr); // nullptr:开发者执行回调所依赖的数据userData为空。
299    if (ret != AV_ERR_OK) {
300        // 异常处理。
301    }
302    ```
303
304    > **说明:**
305    >
306    > 1. 在回调函数中,对数据队列进行操作时,需要注意多线程同步的问题。
307    > 2. 播放视频时,若视频码流的SPS中包含颜色信息,解码器会把这些信息(RangeFlag、ColorPrimary、MatrixCoefficient、TransferCharacteristic)通过OH_AVCodecOnStreamChanged接口中的OH_AVFormat返回。
308    > 3. 视频解码的Surface模式下,内部数据默认是走HEBC(High Efficiency Bandwidth Compression,高效带宽压缩),无法获取到widthStride和heightStride的值。
309    >
310
3114. (可选)OH_VideoDecoder_SetDecryptionConfig设置解密配置。在获取到DRM信息(参考[音视频解封装](audio-video-demuxer.md)开发步骤第4步),完成DRM许可证申请后,通过此接口进行解密配置。此接口需在Prepare前调用。在Surface模式下,DRM解密能力既支持安全视频通路,也支持非安全视频通路。DRM相关接口详见[DRM API文档](../../reference/apis-drm-kit/capi-drm.md)。
312
313    添加头文件。
314
315    ```c++
316    #include <multimedia/drm_framework/native_mediakeysystem.h>
317    #include <multimedia/drm_framework/native_mediakeysession.h>
318    #include <multimedia/drm_framework/native_drm_err.h>
319    #include <multimedia/drm_framework/native_drm_common.h>
320    ```
321
322    在 CMake 脚本中链接动态库。
323
324    ``` cmake
325    target_link_libraries(sample PUBLIC libnative_drm.so)
326    ```
327
328    <!--RP4-->使用示例:<!--RP4End-->
329
330    ```c++
331    // 根据DRM信息创建指定的DRM系统,以创建"com.clearplay.drm"为例。
332    MediaKeySystem *system = nullptr;
333    int32_t ret = OH_MediaKeySystem_Create("com.clearplay.drm", &system);
334    if (system == nullptr) {
335        printf("create media key system failed");
336        return;
337    }
338
339    // 创建解密会话,如果使用安全视频通路,应创建CONTENT_PROTECTION_LEVEL_HW_CRYPTO及其以上内容保护级别的MediaKeySession;
340    // 如果使用非安全视频通路,应创建CONTENT_PROTECTION_LEVEL_SW_CRYPTO及以上内容保护级别的MediaKeySession。
341    MediaKeySession *session = nullptr;
342    DRM_ContentProtectionLevel contentProtectionLevel = CONTENT_PROTECTION_LEVEL_SW_CRYPTO;
343    ret = OH_MediaKeySystem_CreateMediaKeySession(system, &contentProtectionLevel, &session);
344    if (ret != DRM_OK) {
345        // 如创建失败,请查看DRM接口文档及日志信息。
346        printf("create media key session failed.");
347        return;
348    }
349    if (session == nullptr) {
350        printf("media key session is nullptr.");
351        return;
352    }
353
354    // 获取许可证请求、设置许可证响应等。
355
356    // 设置解密配置,即将解密会话、安全视频通路标志设置到解码器中。
357    // 如果DRM解决方案支持安全视频通路,在使用安全视频通路时,需将secureVideoPath设置为true,并在此之前须创建安全解码器。
358    // 即在步骤2使用OH_VideoDecoder_CreateByName函数、参数为解码器名称后拼接.secure(如“[CodecName].secure”)创建安全解码器。
359    bool secureVideoPath = false;
360    ret = OH_VideoDecoder_SetDecryptionConfig(videoDec, session, secureVideoPath);
361    ```
362
3635. 调用OH_VideoDecoder_Configure()配置解码器。
364
365    详细可配置选项的说明请参考[视频专有键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
366
367    参数校验规则请参考[OH_VideoDecoder_Configure() 参考文档](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure)。
368
369    参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力](obtain-supported-codecs.md)。
370
371    目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度。
372
373    ```c++
374
375    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
376    if (format == nullptr) {
377        // 异常处理。
378    }
379    // 写入format。
380    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
381    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
382    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);
383    // 可选,配置低时延解码。
384    // 若平台支持,当使能OH_MD_KEY_VIDEO_ENABLE_LOW_LATENCY接口时,视频解码器将按照解码序输出帧。
385    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_VIDEO_ENABLE_LOW_LATENCY, 1);
386    // 配置解码器。
387    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
388    if (ret != AV_ERR_OK) {
389        // 异常处理。
390    }
391    ```
392
3936. 设置surface。
394
395    本例中的nativeWindow,有两种方式获取:
396
397    6.1 如果解码后直接显示,则从XComponent组件获取。
398
399    添加头文件。
400
401    ```c++
402    #include <native_window/external_window.h>
403    ```
404
405    在 CMake 脚本中链接动态库。
406
407    ``` cmake
408    target_link_libraries(sample PUBLIC libnative_window.so)
409    ```
410
411    6.1.1 在ArkTS侧,通过xComponentController组件的getXComponentSurfaceId接口获取XComponent对应的surface的ID。详情请参考[自定义渲染 (XComponent)](../../ui/napi-xcomponent-guidelines.md)。
412
413    6.1.2 在Native侧,调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。
414
415    ```c++
416    OHNativeWindow* nativeWindow;
417    // 基于步骤1.1中获取的surfaceId创建对应的nativeWindow实例。
418    OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
419    ```
420
421    6.2 如果解码后接OpenGL后处理,则从NativeImage获取,获取方式请参考 [NativeImage](../../graphics/native-image-guidelines.md)。
422
423    Surface模式,开发者可以在解码过程中执行该步骤,即动态切换surface。
424
425    ```c++
426    // 设置surface。
427    // 配置送显窗口参数。
428    OH_AVErrCode ret = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);  // nativeWindow通过以上两种方式获取。
429    if (ret != AV_ERR_OK) {
430        // 异常处理。
431    }
432    // 配置视频与显示屏匹配模式(缓冲区按原比例缩放,使得缓冲区的较小边与窗口匹配,较长边超出窗口的部分被视为透明)。
433    OH_NativeWindow_NativeWindowSetScalingModeV2(nativeWindow, OH_SCALING_MODE_SCALE_CROP_V2);
434    ```
435
436    > **注意:**
437    > 若应用对1号和2号解码器均通过调用OH_VideoDecoder_SetSurface接口绑定至同一个NativeWindow。在2号解码器处于Running状态时,1号解码器调用OH_VideoDecoder_Destroy接口后,会导致2号解码器的视频播放画面卡住。
438    >
439    > 可以采用以下方案进行更改:
440    > 1. 等1号解码器完全释放后,再调用OH_VideoDecoder_Start接口启动2号解码器;
441    > 2. 1号解码器用surface1,2号解码器先调用OH_ConsumerSurface_Create接口创建临时surface,等1号解码器释放后,再调用OH_VideoDecoder_SetSurface接口将2号解码器绑定至surface1上,详情请参见:[创建视频解码器和NativeWindow初始化并行](../../media/avcodec/parallel-decoding-nativeWindow.md)。
442
443
4447. 调用OH_VideoDecoder_Prepare()解码器就绪。
445
446    该接口将在解码器运行前进行一些数据的准备工作。
447
448    ```c++
449    OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
450    if (ret != AV_ERR_OK) {
451        // 异常处理。
452    }
453    ```
454
4558. 调用OH_VideoDecoder_Start()启动解码器。
456
457    ```c++
458    // 启动解码器,开始解码。
459    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
460    if (ret != AV_ERR_OK) {
461        // 异常处理。
462    }
463    ```
464
4659. (可选)OH_VideoDecoder_SetParameter()动态配置解码器surface参数。
466    详细可配置选项的说明请参考[视频专有键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
467
468    ```c++
469    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
470    if (format == nullptr) {
471        // 异常处理。
472    }
473    // 配置显示旋转角度。
474    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ROTATION, 90);
475    OH_AVErrCode ret = OH_VideoDecoder_SetParameter(videoDec, format.get());
476    if (ret != AV_ERR_OK) {
477        // 异常处理。
478    }
479    ```
480
48110. (可选)调用OH_AVCencInfo_SetAVBuffer(),设置cencInfo。
482
483    若当前播放的节目是DRM加密节目,应用自行实现媒体解封装功能而非使用系统[解封装](audio-video-demuxer.md)功能时,需调用OH_AVCencInfo_SetAVBuffer()将cencInfo设置到AVBuffer,这样AVBuffer携带待解密的数据以及cencInfo,以实现AVBuffer中媒体数据的解密。当应用使用系统[解封装](audio-video-demuxer.md)功能时,则无需调用此接口。
484
485    添加头文件。
486
487    ```c++
488    #include <multimedia/player_framework/native_cencinfo.h>
489    ```
490
491    在 CMake 脚本中链接动态库。
492
493    ``` cmake
494    target_link_libraries(sample PUBLIC libnative_media_avcencinfo.so)
495    ```
496
497    使用示例:
498    - buffer:回调函数OnNeedInputBuffer传入的参数。
499    ```c++
500    uint32_t keyIdLen = DRM_KEY_ID_SIZE;
501    uint8_t keyId[] = {
502        0xd4, 0xb2, 0x01, 0xe4, 0x61, 0xc8, 0x98, 0x96,
503        0xcf, 0x05, 0x22, 0x39, 0x8d, 0x09, 0xe6, 0x28};
504    uint32_t ivLen = DRM_KEY_IV_SIZE;
505    uint8_t iv[] = {
506        0xbf, 0x77, 0xed, 0x51, 0x81, 0xde, 0x36, 0x3e,
507        0x52, 0xf7, 0x20, 0x4f, 0x72, 0x14, 0xa3, 0x95};
508    uint32_t encryptedBlockCount = 0;
509    uint32_t skippedBlockCount = 0;
510    uint32_t firstEncryptedOffset = 0;
511    uint32_t subsampleCount = 1;
512    DrmSubsample subsamples[1] = { {0x10, 0x16} };
513    // 创建CencInfo实例。
514    OH_AVCencInfo *cencInfo = OH_AVCencInfo_Create();
515    if (cencInfo == nullptr) {
516        // 异常处理。
517    }
518    // 设置解密算法。
519    OH_AVErrCode errNo = OH_AVCencInfo_SetAlgorithm(cencInfo, DRM_ALG_CENC_AES_CTR);
520    if (errNo != AV_ERR_OK) {
521        // 异常处理。
522    }
523    // 设置KeyId和Iv。
524    errNo = OH_AVCencInfo_SetKeyIdAndIv(cencInfo, keyId, keyIdLen, iv, ivLen);
525    if (errNo != AV_ERR_OK) {
526        // 异常处理。
527    }
528    // 设置Sample信息。
529    errNo = OH_AVCencInfo_SetSubsampleInfo(cencInfo, encryptedBlockCount, skippedBlockCount, firstEncryptedOffset,
530        subsampleCount, subsamples);
531    if (errNo != AV_ERR_OK) {
532        // 异常处理。
533    }
534    // 设置模式:KeyId、Iv和SubSamples已被设置。
535    errNo = OH_AVCencInfo_SetMode(cencInfo, DRM_CENC_INFO_KEY_IV_SUBSAMPLES_SET);
536    if (errNo != AV_ERR_OK) {
537        // 异常处理。
538    }
539    // 将CencInfo设置到AVBuffer中。
540    errNo = OH_AVCencInfo_SetAVBuffer(cencInfo, buffer);
541    if (errNo != AV_ERR_OK) {
542        // 异常处理。
543    }
544    // 销毁CencInfo实例。
545    errNo = OH_AVCencInfo_Destroy(cencInfo);
546    if (errNo != AV_ERR_OK) {
547        // 异常处理。
548    }
549    ```
550
55111. 调用OH_VideoDecoder_PushInputBuffer()写入解码码流。
552
553    送入输入队列进行解码,以下示例中:
554    - size、offset、pts、frameData:输入尺寸、偏移量、时间戳、帧数据等字段信息,获取方式可以参考[音视频解封装](./audio-video-demuxer.md)“步骤-9:开始解封装,循环获取sample”;
555    - flags:缓冲区标记的类别,请参考[OH_AVCodecBufferFlags](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags)。
556
557    bufferInfo的成员变量:
558    - buffer:回调函数OnNeedInputBuffer传入的参数,可以通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口获取输入码流虚拟地址;
559    - index:回调函数OnNeedInputBuffer传入的参数,与buffer唯一对应的标识;
560    - isValid:bufferInfo中存储的buffer实例是否有效。
561
562    ```c++
563    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
564    std::shared_lock<std::shared_mutex> lock(codecMutex);
565    if (bufferInfo == nullptr || !bufferInfo->isValid) {
566        // 异常处理。
567    }
568    // 写入码流数据。
569    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
570    if (addr == nullptr) {
571       // 异常处理。
572    }
573    int32_t capacity = OH_AVBuffer_GetCapacity(bufferInfo->buffer);
574    if (size > capacity) {
575        // 异常处理。
576    }
577    memcpy(addr, frameData, size);
578    // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。
579    OH_AVCodecBufferAttr info;
580    info.size = size;
581    info.offset = offset;
582    info.pts = pts;
583    info.flags = flags;
584    // info信息写入buffer。
585    OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(bufferInfo->buffer, &info);
586    if (setBufferRet != AV_ERR_OK) {
587        // 异常处理。
588    }
589    // 送入解码输入队列进行解码。
590    OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, bufferInfo->index);
591    if (pushInputRet != AV_ERR_OK) {
592        // 异常处理。
593    }
594    ```
595
59612. 调用OH_VideoDecoder_RenderOutputBuffer()/OH_VideoDecoder_RenderOutputBufferAtTime()显示并释放解码帧,
597    或调用OH_VideoDecoder_FreeOutputBuffer()释放解码帧。
598
599    以下示例中,bufferInfo的成员变量:
600    - index:回调函数OnNewOutputBuffer传入的参数,与buffer唯一对应的标识;
601    - buffer:回调函数OnNewOutputBuffer传入的参数,Surface模式开发者无法通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口获取图像虚拟地址;
602    - isValid:bufferInfo中存储的buffer实例是否有效。
603
604    ```c++
605    std::shared_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
606    std::shared_lock<std::shared_mutex> lock(codecMutex);
607    if (bufferInfo == nullptr || !bufferInfo->isValid) {
608        // 异常处理。
609    }
610    // 获取解码后信息。
611    OH_AVCodecBufferAttr info;
612    OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(bufferInfo->buffer, &info);
613    if (getBufferRet != AV_ERR_OK) {
614        // 异常处理。
615    }
616    // 值由开发者决定。
617    bool isRender;
618    bool isNeedRenderAtTime;
619    OH_AVErrCode ret = AV_ERR_OK;
620    if (isRender) {
621        // 显示并释放已完成处理的信息,index为对应buffer队列的下标。
622        if (isNeedRenderAtTime){
623            // 获取系统绝对时间,renderTimestamp由开发者结合业务指定显示时间。
624            int64_t renderTimestamp =
625                std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
626            ret = OH_VideoDecoder_RenderOutputBufferAtTime(videoDec, bufferInfo->index, renderTimestamp);
627        } else {
628            ret = OH_VideoDecoder_RenderOutputBuffer(videoDec, bufferInfo->index);
629        }
630
631    } else {
632        // 释放已完成处理的信息。
633        ret = OH_VideoDecoder_FreeOutputBuffer(videoDec, bufferInfo->index);
634    }
635    if (ret != AV_ERR_OK) {
636        // 异常处理。
637    }
638
639    ```
640
641    > **注意:**
642    >
643    > 1. 如果要获取buffer的属性,如pixel_format、stride等可通过调用[OH_NativeWindow_NativeWindowHandleOpt](../../reference/apis-arkgraphics2d/capi-external-window-h.md#oh_nativewindow_nativewindowhandleopt)接口获取。
644    > 2. 显示并释放解码帧时,推荐优先调用[OH_VideoDecoder_RenderOutputBufferAtTime](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbufferattime)接口。
645    >
646
64713. (可选)调用OH_VideoDecoder_Flush()刷新解码器。
648
649    调用OH_VideoDecoder_Flush接口后,解码器仍处于运行态,但会清除解码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS650    此时需要调用OH_VideoDecoder_Start接口重新开始解码。
651    以下示例中:
652
653    - xpsData、xpsSize:PPS/SPS信息,获取方式可以参考[音视频解封装](./audio-video-demuxer.md)。
654
655    ```c++
656    std::unique_lock<std::shared_mutex> lock(codecMutex);
657    // 刷新解码器videoDec。
658    OH_AVErrCode flushRet = OH_VideoDecoder_Flush(videoDec);
659    if (flushRet != AV_ERR_OK) {
660        // 异常处理。
661    }
662    inQueue.Flush();
663    outQueue.Flush();
664    // 重新开始解码。
665    OH_AVErrCode startRet = OH_VideoDecoder_Start(videoDec);
666    if (startRet != AV_ERR_OK) {
667        // 异常处理。
668    }
669
670    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
671    if (bufferInfo == nullptr || !bufferInfo->isValid) {
672        // 异常处理。
673    }
674    // 重传PPS/SPS675    // 配置帧数据PPS/SPS信息。
676    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
677    if (addr == nullptr) {
678       // 异常处理
679    }
680    int32_t capacity = OH_AVBuffer_GetCapacity(bufferInfo->buffer);
681    if (xpsSize > capacity) {
682        // 异常处理。
683    }
684    memcpy(addr, xpsData, xpsSize);
685    OH_AVCodecBufferAttr info;
686    info.flags = AVCODEC_BUFFER_FLAG_CODEC_DATA;
687    // info信息写入buffer。
688    OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(bufferInfo->buffer, &info);
689    if (setBufferRet != AV_ERR_OK) {
690        // 异常处理。
691    }
692    // 将帧数据推送到解码器中,index为对应buffer队列的下标。
693    OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, bufferInfo->index);
694    if (pushInputRet != AV_ERR_OK) {
695        // 异常处理。
696    }
697
698    ```
699
700    > **注意:**
701    > Flush之后,重新调用OH_VideoDecoder_Start接口时,需要重新传PPS/SPS702    >
703
70414. (可选)调用OH_VideoDecoder_Reset()重置解码器。
705
706    调用OH_VideoDecoder_Reset接口后,解码器回到初始化的状态,需要调用OH_VideoDecoder_Configure接口、OH_VideoDecoder_SetSurface接口和OH_VideoDecoder_Prepare接口重新配置。
707
708    ```c++
709    std::unique_lock<std::shared_mutex> lock(codecMutex);
710    // 重置解码器videoDec。
711    OH_AVErrCode resetRet = OH_VideoDecoder_Reset(videoDec);
712    if (resetRet != AV_ERR_OK) {
713        // 异常处理。
714    }
715    inQueue.Flush();
716    outQueue.Flush();
717    // 重新配置解码器参数。
718    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
719    if (format == nullptr) {
720        // 异常处理。
721    }
722    OH_AVErrCode configRet = OH_VideoDecoder_Configure(videoDec, format.get());
723    if (configRet != AV_ERR_OK) {
724        // 异常处理。
725    }
726    // Surface模式重新配置surface,而Buffer模式不需要配置surface。
727    OH_AVErrCode setRet = OH_VideoDecoder_SetSurface(videoDec, nativeWindow);
728    if (setRet != AV_ERR_OK) {
729        // 异常处理。
730    }
731    // 解码器重新就绪。
732    OH_AVErrCode prepareRet = OH_VideoDecoder_Prepare(videoDec);
733    if (prepareRet != AV_ERR_OK) {
734        // 异常处理。
735    }
736    ```
737
73815. (可选)调用OH_VideoDecoder_Stop()停止解码器。
739
740    调用OH_VideoDecoder_Stop()后,解码器保留了解码实例,释放输入输出buffer。开发者可以直接调用OH_VideoDecoder_Start接口继续解码,输入的第一个buffer需要携带参数集,从IDR帧开始送入。
741
742    ```c++
743    std::unique_lock<std::shared_mutex> lock(codecMutex);
744    // 终止解码器videoDec。
745    OH_AVErrCode ret = OH_VideoDecoder_Stop(videoDec);
746    if (ret != AV_ERR_OK) {
747        // 异常处理。
748    }
749    inQueue.Flush();
750    outQueue.Flush();
751    ```
752
75316. 调用OH_VideoDecoder_Destroy()销毁解码器实例,释放资源。
754
755    > **说明:**
756    >
757    > 1. 不能在回调函数中调用;
758    > 2. 执行该步骤之后,需要开发者将videoDec指向nullptr,防止野指针导致程序错误。
759    >
760
761    ```c++
762    std::unique_lock<std::shared_mutex> lock(codecMutex);
763    // 释放nativeWindow实例。
764    if(nativeWindow != nullptr){
765        OH_NativeWindow_DestroyNativeWindow(nativeWindow);
766        nativeWindow = nullptr;
767    }
768    // 调用OH_VideoDecoder_Destroy,注销解码器。
769    OH_AVErrCode ret = AV_ERR_OK;
770    if (videoDec != nullptr) {
771        ret = OH_VideoDecoder_Destroy(videoDec);
772        videoDec = nullptr;
773    }
774    if (ret != AV_ERR_OK) {
775        // 异常处理。
776    }
777    inQueue.Flush();
778    outQueue.Flush();
779    ```
780
781### Buffer模式
782
783参考以下示例代码,可以完成Buffer模式下视频解码的全流程,实现异步模式的数据轮转。此处以输入H.264码流文件,解码成YUV文件为例。
784
785
7861. 添加头文件。
787
788    ```c++
789    #include <multimedia/player_framework/native_avcodec_videodecoder.h>
790    #include <multimedia/player_framework/native_avcapability.h>
791    #include <multimedia/player_framework/native_avcodec_base.h>
792    #include <multimedia/player_framework/native_avformat.h>
793    #include <multimedia/player_framework/native_avbuffer.h>
794    #include <native_buffer/native_buffer.h>
795    #include <fstream>
796    ```
797
7982. 创建解码器实例。
799
800    与Surface模式相同,此处不再赘述。
801
802    ```c++
803    // 通过codecname创建解码器,应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。
804    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
805    const char *name = OH_AVCapability_GetName(capability);
806    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
807    ```
808
809    ```c++
810    // 通过MIME TYPE创建解码器,只能创建系统推荐的特定编解码器。
811    // 涉及创建多路编解码器时,优先创建硬件解码器实例,硬件资源不够时再创建软件解码器实例。
812    // 软/硬解:创建H.264解码器。
813    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
814    // 硬解:创建H.265解码器。
815    OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_HEVC);
816    ```
817
8183. 调用OH_VideoDecoder_RegisterCallback()设置回调函数。
819
820    注册回调函数指针集合OH_AVCodecCallback,包括:
821
822    - OH_AVCodecOnError 解码器运行错误,返回的错误码详情请参见:[OH_AVCodecOnError](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodeconerror);
823    - OH_AVCodecOnStreamChanged 码流信息变化,如码流宽、高变化;
824    - OH_AVCodecOnNeedInputBuffer 运行过程中需要新的输入数据,即解码器已准备好,可以输入数据;
825    - OH_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据,即解码完成。
826
827    开发者可以通过处理该回调报告的信息,确保解码器正常运转。
828
829    <!--RP2--><!--RP2End-->
830
831    ```c++
832    int32_t cropTop = 0;
833    int32_t cropBottom = 0;
834    int32_t cropLeft = 0;
835    int32_t cropRight = 0;
836    bool isFirstFrame = true;
837    // 解码异常回调OH_AVCodecOnError实现。
838    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
839    {
840        // 回调的错误码由开发者判断处理。
841        (void)codec;
842        (void)errorCode;
843        (void)userData;
844    }
845
846    // 解码数据流变化回调OH_AVCodecOnStreamChanged实现。
847    static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
848    {
849        // 可选,开发者需要获取视频宽、高、跨距等时可配置。
850        // 可通过format获取到变化后的视频宽、高、跨距等。
851        (void)codec;
852        (void)userData;
853        bool ret = OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
854                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_PIC_HEIGHT, &height) &&
855                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
856                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride) &&
857                   // 获取裁剪矩形信息可选。
858                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_TOP, &cropTop) &&
859                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_BOTTOM, &cropBottom) &&
860                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_LEFT, &cropLeft) &&
861                   OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_CROP_RIGHT, &cropRight);
862        if (!ret) {
863         	// 异常处理。
864        }
865    }
866
867    // 解码输入回调OH_AVCodecOnNeedInputBuffer实现。
868    static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
869    {
870        // 输入帧的数据buffer和对应的index送入inQueue队列。
871        (void)codec;
872        (void)userData;
873        inQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
874    }
875
876    // 解码输出回调OH_AVCodecOnNewOutputBuffer实现。
877    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
878    {
879        // 可选,开发者需要获取视频宽、高、跨距等时可配置。
880        // 获取视频宽、高、跨距。
881        if (isFirstFrame) {
882            auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(codec), OH_AVFormat_Destroy);
883            if (format == nullptr) {
884                // 异常处理。
885            }
886            bool ret = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) &&
887                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height) &&
888                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) &&
889                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride) &&
890                       // 获取裁剪矩形信息可选。
891                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_TOP, &cropTop) &&
892                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_BOTTOM, &cropBottom) &&
893                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_LEFT, &cropLeft) &&
894                       OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_RIGHT, &cropRight);
895            if (!ret) {
896             	// 异常处理。
897            }
898            isFirstFrame = false;
899        }
900        // 完成帧的数据buffer和对应的index送入outQueue队列。
901        (void)userData;
902        outQueue.Enqueue(std::make_shared<CodecBufferInfo>(index, buffer));
903    }
904    // 配置异步回调,调用OH_VideoDecoder_RegisterCallback接口。
905    OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
906    // 配置异步回调。
907    OH_AVErrCode ret = OH_VideoDecoder_RegisterCallback(videoDec, cb, nullptr); // nullptr:开发者执行回调所依赖的数据userData为空。
908    if (ret != AV_ERR_OK) {
909        // 异常处理。
910    }
911    ```
912
913    > **说明:**
914    >
915    > 在回调函数中,对数据队列进行操作时,需要注意多线程同步的问题。
916    >
917
9184. (可选)OH_VideoDecoder_SetDecryptionConfig设置解密配置。在获取到DRM信息(参考[音视频解封装](audio-video-demuxer.md)开发步骤第4步),完成DRM许可证申请后,通过此接口进行解密配置。此接口需在Prepare前调用。在Buffer模式下,DRM解密能力仅支持非安全视频通路。DRM相关接口详见[DRM API文档](../../reference/apis-drm-kit/capi-drm.md)。
919
920    添加头文件。
921
922    ```c++
923    #include <multimedia/drm_framework/native_mediakeysystem.h>
924    #include <multimedia/drm_framework/native_mediakeysession.h>
925    #include <multimedia/drm_framework/native_drm_err.h>
926    #include <multimedia/drm_framework/native_drm_common.h>
927    ```
928
929    在 CMake 脚本中链接动态库。
930
931    ``` cmake
932    target_link_libraries(sample PUBLIC libnative_drm.so)
933    ```
934
935    使用示例:
936
937    ```c++
938    // 根据DRM信息创建指定的DRM系统,以创建"com.clearplay.drm"为例。
939    MediaKeySystem *system = nullptr;
940    int32_t ret = OH_MediaKeySystem_Create("com.clearplay.drm", &system);
941    if (system == nullptr) {
942        printf("create media key system failed");
943        return;
944    }
945
946    // 创建解密会话。
947    // 使用非安全视频通路,应创建CONTENT_PROTECTION_LEVEL_SW_CRYPTO及以上内容保护级别的MediaKeySession。
948    MediaKeySession *session = nullptr;
949    DRM_ContentProtectionLevel contentProtectionLevel = CONTENT_PROTECTION_LEVEL_SW_CRYPTO;
950    ret = OH_MediaKeySystem_CreateMediaKeySession(system, &contentProtectionLevel, &session);
951    if (ret != DRM_OK) {
952        // 如创建失败,请查看DRM接口文档及日志信息。
953        printf("create media key session failed.");
954        return;
955    }
956    if (session == nullptr) {
957        printf("media key session is nullptr.");
958        return;
959    }
960    // 获取许可证请求、设置许可证响应等。
961    // 设置解密配置,即将解密会话、安全视频通路标志设置到解码器中。
962    bool secureVideoPath = false;
963    ret = OH_VideoDecoder_SetDecryptionConfig(videoDec, session, secureVideoPath);
964    ```
965
9665. 调用OH_VideoDecoder_Configure()配置解码器。
967
968    与Surface模式相同,此处不再赘述。
969
970    ```c++
971    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
972    if (format == nullptr) {
973        // 异常处理。
974    }
975    // 写入format。
976    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。
977    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。
978    OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);
979    // 配置解码器。
980    OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get());
981    if (ret != AV_ERR_OK) {
982        // 异常处理。
983    }
984    ```
985
9866. 调用OH_VideoDecoder_Prepare()解码器就绪。
987
988    该接口将在解码器运行前进行一些数据的准备工作。
989
990    ```c++
991    OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec);
992    if (ret != AV_ERR_OK) {
993        // 异常处理。
994    }
995    ```
996
9977. 调用OH_VideoDecoder_Start()启动解码器。
998
999    ```c++
1000    std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>();
1001    if (outputFile != nullptr) {
1002        outputFile->open("/*yourpath*.yuv", std::ios::out | std::ios::binary | std::ios::ate);
1003    }
1004    // 启动解码器,开始解码。
1005    OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec);
1006    if (ret != AV_ERR_OK) {
1007        // 异常处理。
1008    }
1009    ```
1010
10118. (可选)OH_VideoDecoder_SetParameter()动态配置解码器参数。
1012    详细可配置选项的说明请参考[视频专有键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。
1013
1014    ```c++
1015    auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
1016    if (format == nullptr) {
1017        // 异常处理。
1018    }
1019    // 配置帧率。
1020    OH_AVFormat_SetDoubleValue(format.get(), OH_MD_KEY_FRAME_RATE, 30.0);
1021    OH_AVErrCode ret = OH_VideoDecoder_SetParameter(videoDec, format.get());
1022    if (ret != AV_ERR_OK) {
1023        // 异常处理。
1024    }
1025    ```
1026
10279. (可选)调用OH_AVCencInfo_SetAVBuffer(),设置cencInfo。
1028
1029    与Surface模式相同,此处不再赘述。
1030
1031    使用示例:
1032
1033    ```c++
1034    uint32_t keyIdLen = DRM_KEY_ID_SIZE;
1035    uint8_t keyId[] = {
1036        0xd4, 0xb2, 0x01, 0xe4, 0x61, 0xc8, 0x98, 0x96,
1037        0xcf, 0x05, 0x22, 0x39, 0x8d, 0x09, 0xe6, 0x28};
1038    uint32_t ivLen = DRM_KEY_IV_SIZE;
1039    uint8_t iv[] = {
1040        0xbf, 0x77, 0xed, 0x51, 0x81, 0xde, 0x36, 0x3e,
1041        0x52, 0xf7, 0x20, 0x4f, 0x72, 0x14, 0xa3, 0x95};
1042    uint32_t encryptedBlockCount = 0;
1043    uint32_t skippedBlockCount = 0;
1044    uint32_t firstEncryptedOffset = 0;
1045    uint32_t subsampleCount = 1;
1046    DrmSubsample subsamples[1] = { {0x10, 0x16} };
1047    // 创建CencInfo实例。
1048    OH_AVCencInfo *cencInfo = OH_AVCencInfo_Create();
1049    if (cencInfo == nullptr) {
1050        // 异常处理。
1051    }
1052    // 设置解密算法。
1053    OH_AVErrCode errNo = OH_AVCencInfo_SetAlgorithm(cencInfo, DRM_ALG_CENC_AES_CTR);
1054    if (errNo != AV_ERR_OK) {
1055        // 异常处理。
1056    }
1057    // 设置KeyId和Iv。
1058    errNo = OH_AVCencInfo_SetKeyIdAndIv(cencInfo, keyId, keyIdLen, iv, ivLen);
1059    if (errNo != AV_ERR_OK) {
1060        // 异常处理。
1061    }
1062    // 设置Sample信息。
1063    errNo = OH_AVCencInfo_SetSubsampleInfo(cencInfo, encryptedBlockCount, skippedBlockCount, firstEncryptedOffset,
1064        subsampleCount, subsamples);
1065    if (errNo != AV_ERR_OK) {
1066        // 异常处理。
1067    }
1068    // 设置模式:KeyId、Iv和SubSamples已被设置。
1069    errNo = OH_AVCencInfo_SetMode(cencInfo, DRM_CENC_INFO_KEY_IV_SUBSAMPLES_SET);
1070    if (errNo != AV_ERR_OK) {
1071        // 异常处理。
1072    }
1073    // 将CencInfo设置到AVBuffer中。
1074    errNo = OH_AVCencInfo_SetAVBuffer(cencInfo, buffer);
1075    if (errNo != AV_ERR_OK) {
1076        // 异常处理。
1077    }
1078    // 销毁CencInfo实例。
1079    errNo = OH_AVCencInfo_Destroy(cencInfo);
1080    if (errNo != AV_ERR_OK) {
1081        // 异常处理。
1082    }
1083    ```
1084
108510. 调用OH_VideoDecoder_PushInputBuffer()写入解码码流。
1086
1087    与Surface模式相同,此处不再赘述。
1088
1089    ```c++
1090    std::shared_ptr<CodecBufferInfo> bufferInfo = inQueue.Dequeue();
1091    std::shared_lock<std::shared_mutex> lock(codecMutex);
1092    if (bufferInfo == nullptr || !bufferInfo->isValid) {
1093        // 异常处理。
1094    }
1095    // 写入码流数据。
1096    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
1097    if (addr == nullptr) {
1098       // 异常处理。
1099    }
1100    int32_t capacity = OH_AVBuffer_GetCapacity(bufferInfo->buffer);
1101    if (size > capacity) {
1102        // 异常处理。
1103    }
1104    memcpy(addr, frameData, size);
1105    // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。
1106    OH_AVCodecBufferAttr info;
1107    info.size = size;
1108    info.offset = offset;
1109    info.pts = pts;
1110    info.flags = flags;
1111    // info信息写入buffer。
1112    OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(bufferInfo->buffer, &info);
1113    if (setBufferRet != AV_ERR_OK) {
1114        // 异常处理。
1115    }
1116    // 送入解码输入队列进行解码,index为对应buffer队列的下标。
1117    OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, bufferInfo->index);
1118    if (pushInputRet != AV_ERR_OK) {
1119        // 异常处理。
1120    }
1121    ```
1122
112311. 调用OH_VideoDecoder_FreeOutputBuffer()释放解码帧。
1124
1125    以下示例中,bufferInfo的成员变量:
1126
1127    - index:回调函数OnNewOutputBuffer传入的参数,与buffer唯一对应的标识;
1128    - buffer: 回调函数OnNewOutputBuffer传入的参数,可以通过[OH_AVBuffer_GetAddr](../../reference/apis-avcodec-kit/_core.md#oh_avbuffer_getaddr)接口获取图像虚拟地址;
1129    - isValid:bufferInfo中存储的buffer实例是否有效。
1130
1131    ```c++
1132    std::shared_ptr<CodecBufferInfo> bufferInfo = outQueue.Dequeue();
1133    std::shared_lock<std::shared_mutex> lock(codecMutex);
1134    if (bufferInfo == nullptr || !bufferInfo->isValid) {
1135        // 异常处理。
1136    }
1137    // 获取解码后信息。
1138    OH_AVCodecBufferAttr info;
1139    OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(bufferInfo->buffer, &info);
1140    if (getBufferRet != AV_ERR_OK) {
1141        // 异常处理。
1142    }
1143    // 将解码完成数据data写入到对应输出文件中。
1144    uint8_t *addr = OH_AVBuffer_GetAddr(bufferInfo->buffer);
1145    if (addr == nullptr) {
1146       // 异常处理。
1147    }
1148    if (outputFile != nullptr && outputFile->is_open()) {
1149        outputFile->write(reinterpret_cast<char *>(addr), info.size);
1150    }
1151    // Buffer模式,释放已完成写入的数据,index为对应buffer队列的下标。
1152    OH_AVErrCode freeOutputRet = OH_VideoDecoder_FreeOutputBuffer(videoDec, bufferInfo->index);
1153    if (freeOutputRet != AV_ERR_OK) {
1154        // 异常处理。
1155    }
1156    ```
1157
1158    NV12/NV21图像如果需要依次将Y、U、V三个分量拷贝至另一块buffer中,以NV12图像为例,按行拷贝示例如下:
1159
1160    以NV12图像为例,width、height、wStride、hStride图像排布参考下图:
1161
1162    - OH_MD_KEY_VIDEO_PIC_WIDTH表示width;
1163    - OH_MD_KEY_VIDEO_PIC_HEIGHT表示height;
1164    - OH_MD_KEY_VIDEO_STRIDE表示wStride;
1165    - OH_MD_KEY_VIDEO_SLICE_HEIGHT表示hStride。
1166
1167    ![copy by line](figures/copy-by-line-decoder.png)
1168
1169    添加头文件。
1170
1171    ```c++
1172    #include <string.h>
1173    ```
1174
1175    使用示例:
1176
1177    ```c++
1178    // 源内存区域的宽、高,通过回调函数OnStreamChanged或接口OH_VideoDecoder_GetOutputDescription获取。
1179    struct Rect
1180    {
1181        int32_t width;
1182        int32_t height;
1183    };
1184
1185    struct DstRect // 目标内存区域的宽跨距、高跨距,由开发者自行设置。
1186    {
1187        int32_t wStride;
1188        int32_t hStride;
1189    };
1190    // 源内存区域的宽跨距、高跨距,通过回调函数OnStreamChanged或接口OH_VideoDecoder_GetOutputDescription获取。
1191    struct SrcRect
1192    {
1193        int32_t wStride;
1194        int32_t hStride;
1195    };
1196
1197    Rect rect = {320, 240};
1198    DstRect dstRect = {320, 240};
1199    SrcRect srcRect = {320, 256};
1200    uint8_t* dst = new uint8_t[dstRect.hStride * dstRect.wStride * 3 / 2]; // 目标内存区域的指针。
1201    uint8_t* src = new uint8_t[srcRect.hStride * srcRect.wStride * 3 / 2]; // 源内存区域的指针。
1202    uint8_t* dstTemp = dst;
1203    uint8_t* srcTemp = src;
1204    rect.height = ((rect.height + 1) / 2)  * 2 // 避免height为奇数;
1205    rect.width = ((rect.width + 1) / 2)  * 2 // 避免width为奇数;
1206
1207    // Y 将Y区域的源数据复制到另一个区域的目标数据中。
1208    for (int32_t i = 0; i < rect.height; ++i) {
1209        //将源数据的一行数据复制到目标数据的一行中。
1210        memcpy(dstTemp, srcTemp, rect.width);
1211        // 更新源数据和目标数据的指针,进行下一行的复制。每更新一次源数据和目标数据的指针都向下移动一个wStride。
1212        dstTemp += dstRect.wStride;
1213        srcTemp += srcRect.wStride;
1214    }
1215    // padding。
1216    // 更新源数据和目标数据的指针,指针都向下移动一个padding。
1217    dstTemp += (dstRect.hStride - rect.height) * dstRect.wStride;
1218    srcTemp += (srcRect.hStride - rect.height) * srcRect.wStride;
1219    rect.height >>= 1;
1220    // UV 将UV区域的源数据复制到另一个区域的目标数据中。
1221    for (int32_t i = 0; i < rect.height; ++i) {
1222        memcpy(dstTemp, srcTemp, rect.width);
1223        dstTemp += dstRect.wStride;
1224        srcTemp += srcRect.wStride;
1225    }
1226
1227    delete[] dst;
1228    dst = nullptr;
1229    delete[] src;
1230    src = nullptr;
1231    ```
1232
1233    硬件解码在处理buffer数据时(释放数据前),输出回调开发者收到的AVbuffer是宽、高对齐后的图像数据。
1234    一般需要获取数据的宽、高、跨距、像素格式来保证解码输出数据被正确的处理。
1235
1236    具体实现请参考:[Buffer模式](#buffer模式)的步骤3-调用OH_VideoDecoder_RegisterCallback()设置回调函数来获取数据的宽、高、跨距、像素格式。
1237
1238后续流程(包括刷新、重置、停止和销毁解码器)与Surface模式基本一致,请参考[Surface模式](#surface模式)的步骤13-16。
1239
1240<!--RP5-->
1241<!--RP5End-->