开发者可以调用本模块的Native API接口,完成视频解码,即将媒体数据解码成YUV文件或送显。
当前支持的解码能力如下:
视频解码软/硬件解码存在差异,基于MimeType创建解码器时,软解当前仅支持 H264 (OH_AVCODEC_MIMETYPE_VIDEO_AVC),硬解则支持 H264 (OH_AVCODEC_MIMETYPE_VIDEO_AVC) 和 H265 (OH_AVCODEC_MIMETYPE_VIDEO_HEVC)。
如果需要对HDRVivid视频进行解码,需要配置MimeType为H265 (OH_AVCODEC_MIMETYPE_VIDEO_HEVC),本功能从API version 11开始支持。
通过视频解码,应用可以实现以下重点能力,包括:
限制约束
- buffer模式不支持10bit的图像数据。
- Flush,Reset,Stop之后,重新Start时,需要重新传XPS。具体示例请参考Surface模式步骤14调用OH_VideoDecoder_Flush()。
- 由于硬件解码器资源有限,每个解码器在使用完毕后都必须调用OH_VideoDecoder_Destroy()函数来销毁实例并释放资源。
- 视频解码输入码流仅支持AnnexB格式,且支持的AnnexB格式不支持多层次切片。
- 在调用Flush,Reset,Stop的过程中,开发者不应对之前回调函数获取到的OH_AVBuffer继续进行操作。
Surface输出与Buffer输出
- 两者数据的输出方式不同。
- 两者的适用场景不同。
- Surface输出是指用OHNativeWindow来传递输出数据,可以与其他模块对接,例如XComponent。
- Buffer输出是指经过解码的数据会以共享内存的方式输出。
- 在接口调用的过程中,两种方式的接口调用方式基本一致,但存在以下差异点:
- 在Surface模式下,可选择调用OH_VideoDecoder_FreeOutputBuffer()接口丢弃输出帧(不送显);在Buffer模式下,应用必须调用OH_VideoDecoder_FreeOutputBuffer()释放数据。
- Surface模式下,应用在解码器就绪前,必须调用OH_VideoDecoder_SetSurface()设置OHNativeWindow,启动后,调用OH_VideoDecoder_RenderOutputBuffer()将解码数据送显;
- 输出回调传出的buffer,在Buffer模式下,可以获取共享内存的地址和数据信息;在Surface模式下,只能获取buffer的数据信息。
两种模式的开发步骤详细说明请参考:Surface模式和Buffer模式。
状态机调用关系
如下为状态机调用关系图:
-
有两种方式可以使解码器进入Initialized状态:
- 初始创建解码器实例时,解码器处于Initialized状态
- 任何状态下调用OH_VideoDecoder_Reset()方法,解码器将会移回Initialized状态
-
Initialized状态下,调用OH_VideoDecoder_Configure()方法配置解码器,配置成功后解码器进入Configured状态
-
Configured状态下调用OH_VideoDecoder_Prepare()进入Prepared状态。
-
Prepared状态调用OH_VideoDecoder_Start()方法使解码器进入Executing状态。
- 处于Executing状态时,调用OH_VideoDecoder_Stop()方法可以使解码器返回到Prepared状态
-
在极少数情况下,解码器可能会遇到错误并进入Error状态。解码器的错误传递,可以通过队列操作返回无效值或者抛出异常。
- Error状态下可以调用解码器OH_VideoDecoder_Reset()方法将解码器移到Initialized状态;或者调用OH_VideoDecoder_Destroy()方法移动到最后的Released状态
-
Executing状态具有三个子状态:Flushed、Running和End-of-Stream:
- 在调用了OH_VideoDecoder_Start()之后,解码器立即进入Running子状态。
- 对于处于Executing状态的解码器,可以调用OH_VideoDecoder_Flush()方法返回到Flushed子状态。
- 当待处理数据全部传递给解码器后,在input buffers队列中为最后一个入队的input buffer中添加AVCODEC_BUFFER_FLAGS_EOS标记,遇到这个标记时,解码器会转换为End-of-Stream子状态。在此状态下,解码器不再接受新的输入,但是仍然会继续生成输出,直到输出到达尾帧。
-
使用完解码器后,必须调用OH_VideoDecoder_Destroy()方法销毁解码器实例。使解码器进入Released状态。
开发指导
详细的API说明请参考API文档。
如下为视频解码调用关系图:
在 CMake 脚本中链接动态库
1. target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
2. target_link_libraries(sample PUBLIC libnative_media_core.so)
3. target_link_libraries(sample PUBLIC libnative_media_vdec.so)
Surface模式
参考以下示例代码,开发者可以完成Surface模式下视频解码的全流程。此处以H.264码流文件输入,解码送显输出为例。
本模块目前仅支持异步模式的数据轮转。
- 添加头文件。
target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
target_link_libraries(sample PUBLIC libnative_media_core.so)
target_link_libraries(sample PUBLIC libnative_media_vdec.so)
- 全局变量。
#include <multimedia/player_framework/native_avcodec_videodecoder.h>
#include <multimedia/player_framework/native_avcapability.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avformat.h>
#include <multimedia/player_framework/native_avbuffer.h>
#include <fstream>
-
创建解码器实例对象。
应用可以通过名称或媒体类型创建解码器。示例中的变量说明如下:
- videoDec:视频解码器实例的指针。
- capability:解码器能力查询实例的指针。
- OH_AVCODEC_MIMETYPE_VIDEO_AVC:AVC格式视频码流的名称。
// 通过codecname创建解码器,应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。
OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
const char *name = OH_AVCapability_GetName(capability);
OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name);
// 通过MIME TYPE创建解码器,只能创建系统推荐的特定编解码器
// 涉及创建多路编解码器时,优先创建硬件解码器实例,硬件资源不够时再创建软件解码器实例
// 软/硬解: 创建H264解码器
OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC);
// 硬解: 创建H265解码器
OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_HEVC);
-
调用OH_VideoDecoder_RegisterCallback()设置回调函数。
注册回调函数指针集合OH_AVCodecCallback,包括:
- OH_AVCodecOnError 解码器运行错误;
- OH_AVCodecOnStreamChanged 码流信息变化,如码流宽、高变化;
- OH_AVCodecOnNeedInputBuffer 运行过程中需要新的输入数据,即解码器已准备好,可以输入数据;
- OH_AVCodecOnNewOutputBuffer 运行过程中产生了新的输出数据,即解码完成(注:Surface模式buffer参数为空)。
开发者可以通过处理该回调报告的信息,确保解码器正常运转。
回调函数的具体实现可参考示例工程。
// 解码异常回调OH_AVCodecOnError实现
static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
{
// 回调的错误码由用户判断处理
(void)codec;
(void)errorCode;
(void)userData;
}
// 解码数据流变化回调OH_AVCodecOnStreamChanged实现
static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
{
// 可通过format获取到变化后的视频宽、高、跨距等
(void)codec;
(void)format;
(void)userData;
}
// 解码输入回调OH_AVCodecOnNeedInputBuffer实现
static void