1# 视频解码同步模式 2 3<!--Kit: AVCodec Kit--> 4<!--Subsystem: Multimedia--> 5<!--Owner: @zhanghongran--> 6<!--Designer: @dpy2650---> 7<!--Tester: @cyakee--> 8<!--Adviser: @zengyawen--> 9 10从API 20开始,支持视频解码同步模式。 11 12开发者可以调用本模块的Native API接口,完成同步模式的视频解码。 13 14当前支持的解码能力,请参考[AVCodec支持的格式](avcodec-support-formats.md#视频解码)。 15 16视频解码的限制约束、支持的能力、状态机调用关系请参考[视频解码](video-decoding.md)。 17 18## 适用场景 19 20通常情况下,推荐使用异步模式。若需要主动请求buffer去送帧,则可以采用同步模式。 21 22 23## 开发指导 24 25详细的API说明请参考[VideoDecoder](../../reference/apis-avcodec-kit/_video_decoder.md)。 26 27- 虚线表示可选。 28 29- 实线表示必选。 30 31 32 33### 在CMake脚本中链接动态库 34 35``` cmake 36target_link_libraries(sample PUBLIC libnative_media_codecbase.so) 37target_link_libraries(sample PUBLIC libnative_media_core.so) 38target_link_libraries(sample PUBLIC libnative_media_vdec.so) 39``` 40 41> **说明:** 42> 43> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。 44> 45 46### 定义基础结构 47 48本部分示例代码按照C++17标准编写,仅作参考。 49 501. 添加头文件。 51 52 ```c++ 53 #include <multimedia/player_framework/native_avcodec_videodecoder.h> 54 #include <multimedia/player_framework/native_avcapability.h> 55 #include <multimedia/player_framework/native_avcodec_base.h> 56 #include <multimedia/player_framework/native_avformat.h> 57 #include <multimedia/player_framework/native_avbuffer.h> 58 #include <multimedia/player_framework/native_averrors.h> 59 #include <native_buffer/native_buffer.h> 60 #include <memory> 61 #include <fstream> 62 #include <mutex> 63 #include <shared_mutex> 64 #include <string.h> 65 ``` 66 672. 全局变量(仅作参考,可以根据实际情况将其封装到对象中)。 68 69 ```c++ 70 // 视频帧宽度。 71 int32_t width = 320; 72 // 视频帧高度。 73 int32_t height = 240; 74 // 视频像素格式。 75 OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12; 76 // 解码器同步锁。 77 std::shared_mutex codecMutex; 78 // 解码器实例指针。 79 OH_AVCodec *videoDec = nullptr; 80 // 解码输出。 81 bool outputDone = false; 82 // 解码输入。 83 bool inputDone = false; 84 std::unique_ptr<std::ifstream> inFile_; 85 ``` 86 87### Surface模式 88 89参考以下示例代码,可以完成Surface模式下视频解码的全流程,实现同步模式的数据轮转。此处以输入H.264码流文件,解码送显输出为例。 90 91 921. 创建解码器实例。 93 94 通过名称创建解码器。示例中的变量说明如下: 95 96 - videoDec:视频解码器实例的指针。 97 - capability:解码器能力查询实例的指针。 98 - OH_AVCODEC_MIMETYPE_VIDEO_AVC:AVC格式视频编解码器。 99 100 ```c++ 101 // 创建硬件解码器实例。 102 OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false, HARDWARE); 103 const char *name = OH_AVCapability_GetName(capability); 104 OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name); 105 if (videoDec == nullptr) { 106 printf("create videoDec failed"); 107 return; 108 } 109 ``` 1102. 调用OH_VideoDecoder_Configure()配置解码器。 111 112 - 详细可配置选项的说明请参考[媒体数据键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。 113 - 参数校验规则请参考[OH_VideoDecoder_Configure()](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure)。 114 - 参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力](obtain-supported-codecs.md)。 115 116 目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度。 117 118 ```c++ 119 120 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 121 if (format == nullptr) { 122 // 异常处理。 123 } 124 // 写入format。 125 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。 126 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。 127 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); 128 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 129 // 配置解码器。 130 OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get()); 131 if (ret != AV_ERR_OK) { 132 // 异常处理。 133 } 134 ``` 135 136 > **注意:** 137 > 138 > 1. 要使能视频解码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。 139 > 2. 同步模式在调用OH_VideoDecoder_Configure接口前不能调用OH_VideoDecoder_RegisterCallback接口,否则为异步模式。 140 > 141 1423. 设置surface。 143 144 示例中的变量说明如下: 145 - nativeWindow:获取方式请参考[视频解码Surface模式](video-decoding.md#surface模式)的“步骤-6:设置surface”。 146 147 ```c++ 148 // 设置surface。 149 // 配置送显窗口参数。 150 OH_AVErrCode ret = OH_VideoDecoder_SetSurface(videoDec, nativeWindow); 151 if (ret != AV_ERR_OK) { 152 // 异常处理。 153 } 154 ``` 155 156 1574. 调用OH_VideoDecoder_Prepare()解码器就绪。 158 159 该接口将在解码器运行前进行一些数据的准备工作。 160 161 ```c++ 162 OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec); 163 if (ret != AV_ERR_OK) { 164 // 异常处理。 165 } 166 ``` 167 1685. 调用OH_VideoDecoder_Start()启动解码器。 169 170 ```c++ 171 // 启动解码器,开始解码。 172 OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec); 173 if (ret != AV_ERR_OK) { 174 // 异常处理。 175 } 176 ``` 177 1786. 获取可用buffer并写入码流至解码器。 179 180 - 调用[OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。 181 - 根据获取的索引(index),调用[OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。 182 - 将待解码数据写入该缓冲区(buffer)后,调用[OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer)接口提交至解码器进行解码。当所有待处理数据全部传递给解码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知解码器输入结束。 183 184 185 送入输入队列进行解码,示例中的变量说明如下: 186 - size、offset、pts、frameData:输入尺寸、偏移量、时间戳、帧数据等字段信息,获取方式可以参考[媒体数据解析](./audio-video-demuxer.md#开发步骤)“步骤-9:开始解封装,循环获取sample”。 187 - flags:缓冲区标记的类别,请参考[OH_AVCodecBufferFlags](../../reference/apis-avcodec-kit/_core.md#oh_avcodecbufferflags)。 188 189 ```c++ 190 bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs) 191 { 192 uint32_t index; 193 std::shared_lock<std::shared_mutex> lock(codecMutex); 194 195 OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs); 196 switch (ret) { 197 case AV_ERR_OK: { 198 OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index); 199 if (buffer == nullptr) { 200 // 异常处理。 201 return false; 202 } 203 // 写入码流数据。 204 uint8_t *addr = OH_AVBuffer_GetAddr(buffer); 205 if (addr == nullptr) { 206 // 异常处理。 207 return false; 208 } 209 // buffer数据填充。 210 int32_t capacity = OH_AVBuffer_GetCapacity(buffer); 211 if (size > capacity) { 212 // 异常处理。 213 } 214 memcpy(addr, frameData, size); 215 216 OH_AVCodecBufferAttr info; 217 // buffer属性配置。 218 // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。 219 info.size = size; 220 info.offset = offset; 221 info.pts = pts; 222 if (inFile_->eof()) { 223 info.flags = AVCODEC_BUFFER_FLAGS_EOS; 224 } else { 225 info.flags = flags; 226 } 227 OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info); 228 if (setBufferRet != AV_ERR_OK) { 229 // 异常处理。 230 return false; 231 } 232 OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index); 233 if (pushInputRet != AV_ERR_OK) { 234 // 异常处理。 235 return false; 236 } 237 if (inFile_->eof()) { 238 inputDone = 1; 239 } 240 break; 241 } 242 case AV_ERR_TRY_AGAIN_LATER: { 243 break; 244 } 245 default: { 246 // 异常处理。 247 return false; 248 } 249 } 250 return true; 251 } 252 ``` 253 2547. 获取可用buffer显示并释放解码帧。 255 256 - 调用[OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。 257 - 根据获取的索引(index),调用[OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。 258 - 根据开发者设置的isRender标志决定后续操作:若无需送显,则调用[OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer)接口释放解码帧。若需送显,则可调用[OH_VideoDecoder_RenderOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbuffer)接口显示并自动释放解码帧,或调用[OH_VideoDecoder_RenderOutputBufferAtTime](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_renderoutputbufferattime)接口在指定时间点显示并释放解码帧。 259 260 261 ```c++ 262 bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs) 263 { 264 uint32_t index; 265 std::shared_lock<std::shared_mutex> lock(codecMutex); 266 267 OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs); 268 switch (ret) { 269 case AV_ERR_OK: { 270 OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index); 271 if (buffer == nullptr) { 272 // 异常处理。 273 return false; 274 } 275 276 // 获取解码后信息。 277 OH_AVCodecBufferAttr info; 278 OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info); 279 if (getBufferRet != AV_ERR_OK) { 280 // 异常处理。 281 return false; 282 } 283 if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) { 284 outputDone = 1; 285 } 286 287 // 解码输出数据处理。 288 // 值由开发者决定。 289 bool isRender; 290 bool isNeedRenderAtTime; 291 OH_AVErrCode result = AV_ERR_OK; 292 if (isRender) { 293 // 显示并释放已完成处理的信息,index为对应buffer队列的下标。 294 if (isNeedRenderAtTime){ 295 // 获取系统绝对时间,renderTimestamp由开发者结合业务指定显示时间。 296 int64_t renderTimestamp = 297 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 298 result = OH_VideoDecoder_RenderOutputBufferAtTime(videoDec, index, renderTimestamp); 299 } else { 300 result = OH_VideoDecoder_RenderOutputBuffer(videoDec, index); 301 } 302 } else { 303 // 释放已完成处理的信息。 304 result = OH_VideoDecoder_FreeOutputBuffer(videoDec, index); 305 } 306 if (result != AV_ERR_OK) { 307 // 异常处理。 308 return false; 309 } 310 break; 311 } 312 case AV_ERR_TRY_AGAIN_LATER: { 313 break; 314 } 315 case AV_ERR_STREAM_CHANGED: { 316 auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy); 317 if (format == nullptr) { 318 // 异常处理。 319 } 320 // 获取新宽高。 321 bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) && 322 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height); 323 if (!getIntRet) { 324 // 异常处理。 325 } 326 break; 327 } 328 default: { 329 // 异常处理。 330 return false; 331 } 332 } 333 return true; 334 } 335 ``` 336 3378. 解码器送帧/出帧处理循环。 338 339 ```c++ 340 bool result = true; 341 int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:指定时间后结束后退出。 342 343 while (!outputDone && result) { 344 if (!inputDone) { 345 result = DecoderInput(videoDec, timeoutUs); 346 } 347 if (!outputDone) { 348 result = DecoderOutput(videoDec, timeoutUs); 349 } 350 } 351 ``` 352 3539. (可选)调用OH_VideoDecoder_Flush()刷新解码器。 354 355 调用OH_VideoDecoder_Flush接口后,解码器仍处于运行态,但会清除解码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS。 356 此时需要调用[OH_VideoDecoder_Start](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_start)接口重新开始解码。 357 358 ```c++ 359 // 通过codecMutex来避免调用Flush接口,状态切换后,解码线程还在跑会退出循环的问题。 360 std::unique_lock<std::shared_mutex> lock(codecMutex); 361 // 刷新解码器videoDec。 362 OH_AVErrCode ret = OH_VideoDecoder_Flush(videoDec); 363 if (ret != AV_ERR_OK) { 364 // 异常处理。 365 } 366 367 // 重新开始解码。 368 ret = OH_VideoDecoder_Start(videoDec); 369 if (ret != AV_ERR_OK) { 370 // 异常处理。 371 } 372 ``` 373 37410. (可选)调用OH_VideoDecoder_Reset()重置解码器。 375 376 调用OH_VideoDecoder_Reset接口后,解码器回到初始化的状态,需要调用接口[OH_VideoDecoder_Configure](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_configure)、[OH_VideoDecoder_SetSurface](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_setsurface)和[OH_VideoDecoder_Prepare](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_prepare)重新配置。 377 378 ```c++ 379 // 重置解码器videoDec。 380 std::unique_lock<std::shared_mutex> lock(codecMutex); 381 OH_AVErrCode resetRet = OH_VideoDecoder_Reset(videoDec); 382 if (resetRet != AV_ERR_OK) { 383 // 异常处理。 384 } 385 386 // 重新配置解码器参数。 387 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 388 if (format == nullptr) { 389 // 异常处理。 390 } 391 OH_AVErrCode configRet = OH_VideoDecoder_Configure(videoDec, format.get()); 392 if (configRet != AV_ERR_OK) { 393 // 异常处理。 394 } 395 396 // Surface模式需要重新配置surface,而Buffer模式不需要配置surface。 397 OH_AVErrCode setRet = OH_VideoDecoder_SetSurface(videoDec, nativeWindow); 398 if (setRet != AV_ERR_OK) { 399 // 异常处理。 400 } 401 // 解码器重新就绪。 402 OH_AVErrCode prepareRet = OH_VideoDecoder_Prepare(videoDec); 403 if (prepareRet != AV_ERR_OK) { 404 // 异常处理。 405 } 406 ``` 407 408 > **注意:** 409 > 410 > 解码器回到初始化的状态,调用OH_VideoDecoder_Configure接口重新配置解码器参数时,同步模式需要重新配置OH_MD_KEY_ENABLE_SYNC_MODE为1,否则为异步模式。 411 > 412 41311. (可选)调用OH_VideoDecoder_Stop()停止解码器。 414 415 调用OH_VideoDecoder_Stop()后,解码器保留解码实例,释放输入输出buffer。 416 417 ```c++ 418 // 终止解码器videoDec。 419 std::unique_lock<std::shared_mutex> lock(codecMutex); 420 OH_AVErrCode ret = OH_VideoDecoder_Stop(videoDec); 421 if (ret != AV_ERR_OK) { 422 // 异常处理。 423 } 424 ``` 425 42612. 调用OH_VideoDecoder_Destroy()销毁解码器实例,释放资源。 427 428 ```c++ 429 // 调用OH_VideoDecoder_Destroy,注销解码器。 430 std::unique_lock<std::shared_mutex> lock(codecMutex); 431 OH_AVErrCode ret = AV_ERR_OK; 432 if (videoDec != nullptr) { 433 ret = OH_VideoDecoder_Destroy(videoDec); 434 videoDec = nullptr; 435 } 436 if (ret != AV_ERR_OK) { 437 // 异常处理。 438 } 439 ``` 440 441 > **说明:** 442 > 443 > 执行该步骤之后,需要开发者将videoDec指向nullptr,防止野指针导致程序错误。 444 > 445 446### Buffer模式 447 448参考以下示例代码,可以完成Buffer模式下视频解码的全流程,实现同步模式的数据轮转。此处以输入H.264码流文件,解码成YUV文件为例。 449 4501. 创建解码器实例。 451 452 与Surface模式相同,此处不再赘述。 453 454 ```c++ 455 // 通过codecname创建解码器,应用有特殊需求,比如选择支持某种分辨率规格的解码器,可先查询capability,再根据codec name创建解码器。 456 OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false); 457 const char *name = OH_AVCapability_GetName(capability); 458 OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name); 459 if (videoDec == nullptr) { 460 printf("create videoDec failed"); 461 return; 462 } 463 ``` 464 4652. 调用OH_VideoDecoder_Configure()配置解码器。 466 467 与Surface模式相同,此处不再赘述。 468 469 ```c++ 470 471 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 472 if (format == nullptr) { 473 // 异常处理。 474 } 475 // 写入format。 476 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。 477 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。 478 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat); 479 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 480 // 配置解码器。 481 OH_AVErrCode ret = OH_VideoDecoder_Configure(videoDec, format.get()); 482 if (ret != AV_ERR_OK) { 483 // 异常处理。 484 } 485 ``` 486 487 > **注意:** 488 > 489 > 1. 要使能视频解码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。 490 > 2. 同步模式在调用OH_VideoDecoder_Configure接口前不能调用OH_VideoDecoder_RegisterCallback接口,否则为异步模式。 491 > 492 4933. 调用OH_VideoDecoder_Prepare()解码器就绪。 494 495 该接口将在解码器运行前进行一些数据的准备工作。 496 497 ```c++ 498 OH_AVErrCode ret = OH_VideoDecoder_Prepare(videoDec); 499 if (ret != AV_ERR_OK) { 500 // 异常处理。 501 } 502 ``` 503 5044. 调用OH_VideoDecoder_Start()启动解码器。 505 506 ```c++ 507 std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>(); 508 if (outputFile != nullptr) { 509 outputFile->open("/*yourpath*.yuv", std::ios::out | std::ios::binary | std::ios::ate); 510 } 511 // 启动解码器,开始解码。 512 OH_AVErrCode ret = OH_VideoDecoder_Start(videoDec); 513 if (ret != AV_ERR_OK) { 514 // 异常处理。 515 } 516 ``` 517 5185. 获取可用buffer并写入码流至解码器。 519 520 - 调用[OH_VideoDecoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。 521 - 根据获取的索引(index),调用[OH_VideoDecoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。 522 - 将待解码数据写入该缓冲区(buffer)后,调用[OH_VideoDecoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_pushinputbuffer)接口提交至解码器进行解码。当所有待处理数据全部传递给解码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知解码器输入结束。 523 524 示例中的变量size、offset、pts、frameData、flags说明与Surface模式相同,此处不再赘述。 525 526 ```c++ 527 bool DecoderInput(OH_AVCodec *videoDec, int64_t timeoutUs) 528 { 529 uint32_t index; 530 std::shared_lock<std::shared_mutex> lock(codecMutex); 531 532 OH_AVErrCode ret = OH_VideoDecoder_QueryInputBuffer(videoDec, &index, timeoutUs); 533 switch (ret) { 534 case AV_ERR_OK: { 535 OH_AVBuffer *buffer = OH_VideoDecoder_GetInputBuffer(videoDec, index); 536 if (buffer == nullptr) { 537 // 异常处理。 538 return false; 539 } 540 // 写入码流数据。 541 uint8_t *addr = OH_AVBuffer_GetAddr(buffer); 542 if (addr == nullptr) { 543 // 异常处理。 544 return false; 545 } 546 // buffer数据填充。 547 int32_t capacity = OH_AVBuffer_GetCapacity(buffer); 548 if (size > capacity) { 549 // 异常处理。 550 } 551 memcpy(addr, frameData, size); 552 553 OH_AVCodecBufferAttr info; 554 // buffer属性配置。 555 // 配置帧数据的输入尺寸、偏移量、时间戳等字段信息。 556 info.size = size; 557 info.offset = offset; 558 info.pts = pts; 559 if (inFile_->eof()) { 560 info.flags = AVCODEC_BUFFER_FLAGS_EOS; 561 } else { 562 info.flags = flags; 563 } 564 OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info); 565 if (setBufferRet != AV_ERR_OK) { 566 // 异常处理。 567 return false; 568 } 569 OH_AVErrCode pushInputRet = OH_VideoDecoder_PushInputBuffer(videoDec, index); 570 if (pushInputRet != AV_ERR_OK) { 571 // 异常处理。 572 return false; 573 } 574 if (inFile_->eof()) { 575 inputDone = 1; 576 } 577 break; 578 } 579 case AV_ERR_TRY_AGAIN_LATER: { 580 break; 581 } 582 default: { 583 // 异常处理。 584 return false; 585 } 586 } 587 return true; 588 } 589 ``` 590 5916. 获取可用buffer并释放解码帧。 592 593 - 调用[OH_VideoDecoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。 594 - 根据获取的索引(index),调用[OH_VideoDecoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。 595 - 调用[OH_VideoDecoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_decoder.md#oh_videodecoder_freeoutputbuffer)接口释放解码帧。 596 597 ```c++ 598 bool DecoderOutput(OH_AVCodec *videoDec, int64_t timeoutUs) 599 { 600 uint32_t index; 601 int32_t cropTop = 0; 602 int32_t cropBottom = 0; 603 int32_t cropLeft = 0; 604 int32_t cropRight = 0; 605 int32_t widthStride = 0; 606 int32_t heightStride = 0; 607 std::shared_lock<std::shared_mutex> lock(codecMutex); 608 609 OH_AVErrCode ret = OH_VideoDecoder_QueryOutputBuffer(videoDec, &index, timeoutUs); 610 switch (ret) { 611 case AV_ERR_OK: { 612 OH_AVBuffer *buffer = OH_VideoDecoder_GetOutputBuffer(videoDec, index); 613 if (buffer == nullptr) { 614 // 异常处理。 615 return false; 616 } 617 618 // 获取解码后信息。 619 OH_AVCodecBufferAttr info; 620 OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info); 621 if (getBufferRet != AV_ERR_OK) { 622 // 异常处理。 623 return false; 624 } 625 if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) { 626 outputDone = 1; 627 } 628 629 // 释放已完成处理的信息,index为对应buffer队列的下标。 630 OH_AVErrCode freeOutputRet = OH_VideoDecoder_FreeOutputBuffer(videoDec, index); 631 if (freeOutputRet != AV_ERR_OK) { 632 // 异常处理。 633 return false; 634 } 635 break; 636 } 637 case AV_ERR_TRY_AGAIN_LATER: { 638 break; 639 } 640 case AV_ERR_STREAM_CHANGED: { 641 auto format = std::shared_ptr<OH_AVFormat>(OH_VideoDecoder_GetOutputDescription(videoDec), OH_AVFormat_Destroy); 642 if (format == nullptr) { 643 // 异常处理。 644 } 645 // 获取到变化后的视频宽、高、跨距。 646 bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_WIDTH, &width) && 647 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_PIC_HEIGHT, &height) && 648 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) && 649 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride) && 650 // 获取裁剪矩形信息可选。 651 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_TOP, &cropTop) && 652 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_BOTTOM, &cropBottom) && 653 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_LEFT, &cropLeft) && 654 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_CROP_RIGHT, &cropRight); 655 if (!getIntRet) { 656 // 异常处理。 657 } 658 break; 659 } 660 default: { 661 // 异常处理。 662 return false; 663 } 664 } 665 return true; 666 } 667 ``` 668 6697. 解码器送帧/出帧处理循环。 670 671 ```c++ 672 bool result = true; 673 int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。 674 675 while (!outputDone && result) { 676 if (!inputDone) { 677 result = DecoderInput(videoDec, timeoutUs); 678 } 679 if (!outputDone) { 680 result = DecoderOutput(videoDec, timeoutUs); 681 } 682 } 683 ``` 684 685后续流程(包括刷新、重置停止和销毁解码器)与Surface模式基本一致,请参考[Surface模式](#surface模式)的步骤9-12。