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-encoding.md)。 17 18## 适用场景 19 20通常情况下,推荐使用异步模式。若需要主动请求buffer去送帧,则可以采用同步模式。 21 22 23## 开发指导 24 25详细的API说明请参考[VideoEncoder](../../reference/apis-avcodec-kit/_video_encoder.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_venc.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_videoencoder.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 ``` 65 662. 全局变量(仅作参考,可以根据实际情况将其封装到对象中)。 67 68 ```c++ 69 // 视频帧宽度。 70 int32_t width = 320; 71 // 视频帧高度。 72 int32_t height = 240; 73 // 视频宽跨距。 74 int32_t widthStride = 0; 75 // 视频高跨距。 76 int32_t heightStride = 0; 77 // 视频像素格式。 78 OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12; 79 // 编码器同步锁。 80 std::shared_mutex codecMutex; 81 // 编码器实例指针。 82 OH_AVCodec *videoEnc = nullptr; 83 // 编码输出。 84 bool outputDone = false; 85 // 编码输入。 86 bool inputDone = false; 87 std::unique_ptr<std::ifstream> inFile_; 88 ``` 89 90### Surface模式 91 92参考以下示例代码,可以完成Surface模式下视频编码的全流程,实现同步模式的数据轮转。此处以输入surface数据,编码成H.264格式为例。 93 94 951. 创建编码器实例。 96 97 通过名称创建编码器。示例中的变量说明如下: 98 99 - videoEnc:视频编码器实例的指针。 100 - capability:编码器能力查询实例的指针。 101 - [OH_AVCODEC_MIMETYPE_VIDEO_AVC](../../reference/apis-avcodec-kit/_codec_base.md#oh_avcodec_mimetype_video_avc):AVC格式视频编解码器。 102 103 ```c++ 104 // 创建硬件编码器实例。 105 OH_AVCapability *capability= OH_AVCodec_GetCapabilityByCategory(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true, HARDWARE); 106 const char *name = OH_AVCapability_GetName(capability); 107 OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name); 108 if (videoEnc == nullptr) { 109 printf("create videoEnc failed"); 110 return; 111 } 112 ``` 113 1142. 调用OH_VideoEncoder_Configure()配置编码器。 115 116 - 详细可配置选项的说明请参考[媒体数据键值对](../../reference/apis-avcodec-kit/_codec_base.md#媒体数据键值对)。 117 - 参数校验规则请参考[OH_VideoEncoder_Configure()](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)。 118 - 参数取值范围可以通过能力查询接口获取,具体示例请参考[获取支持的编解码能力](obtain-supported-codecs.md)。 119 120 目前支持的所有格式都必须配置以下选项:视频帧宽度、视频帧高度、视频像素格式。 121 122 ```c++ 123 124 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 125 if (format == nullptr) { 126 // 异常处理。 127 } 128 // 写入format。 129 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。 130 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。 131 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);// 必须配置。 132 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 133 // 配置编码器。 134 OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get()); 135 if (ret != AV_ERR_OK) { 136 // 异常处理。 137 } 138 ``` 139 140 > **注意:** 141 > 142 > 1. 要使能视频编码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。 143 > 2. 同步模式在调用OH_VideoEncoder_Configure接口前不能调用OH_VideoEncoder_RegisterCallback或OH_VideoEncoder_RegisterParameterCallback接口,否则为异步模式。 144 > 3. 不支持Surface模式的随帧通路的同步模式。 145 > 146 1473. 设置surface。 148 149 示例中的变量说明如下: 150 - nativeWindow:获取方式请参考[视频编码Surface模式](video-encoding.md#surface模式)的“步骤-6:设置surface”。 151 152 ```c++ 153 // 获取需要输入的surface,以进行编码。 154 OH_AVErrCode ret = OH_VideoEncoder_GetSurface(videoEnc, &nativeWindow); 155 if (ret != AV_ERR_OK) { 156 // 异常处理。 157 } 158 ``` 159 1604. 调用OH_VideoEncoder_Prepare()编码器就绪。 161 162 该接口将在编码器运行前进行一些数据的准备工作。 163 164 ```c++ 165 OH_AVErrCode ret = OH_VideoEncoder_Prepare(videoEnc); 166 if (ret != AV_ERR_OK) { 167 // 异常处理。 168 } 169 ``` 170 1715. 调用调用OH_VideoEncoder_Start()启动编码器。 172 173 ```c++ 174 // 配置待编码文件路径。 175 std::string_view outputFilePath = "/*yourpath*.h264"; 176 std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>(); 177 if (outputFile != nullptr) { 178 outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate); 179 } 180 // 启动编码器,开始编码。 181 OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc); 182 if (ret != AV_ERR_OK) { 183 // 异常处理。 184 } 185 ``` 186 1876. 获取可用buffer并释放编码帧。 188 189 - 调用[OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。 190 - 根据获取的索引(index),调用[OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。 191 - 调用[OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer)接口释放编码帧。 192 193 ```c++ 194 bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs) 195 { 196 uint32_t index; 197 std::shared_lock<std::shared_mutex> lock(codecMutex); 198 199 OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs); 200 switch (ret) { 201 case AV_ERR_OK: { 202 OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index); 203 if (buffer == nullptr) { 204 // 异常处理。 205 return false; 206 } 207 208 // 获取编码后信息。 209 OH_AVCodecBufferAttr info; 210 OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info); 211 if (getBufferRet != AV_ERR_OK) { 212 // 异常处理。 213 return false; 214 } 215 if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) { 216 outputDone = 1; 217 } 218 219 // 将编码完成帧数据buffer写入到对应输出文件中。 220 uint8_t *addr = OH_AVBuffer_GetAddr(buffer); 221 if (addr == nullptr) { 222 // 异常处理 223 return false; 224 } 225 if (outputFile != nullptr && outputFile->is_open()) { 226 outputFile->write(reinterpret_cast<char *>(addr), info.size); 227 } 228 // 释放已完成写入的数据,index为对应输出队列下标。 229 OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index); 230 if (freeOutputRet != AV_ERR_OK) { 231 // 异常处理。 232 return false; 233 } 234 break; 235 } 236 case AV_ERR_TRY_AGAIN_LATER: { 237 break; 238 } 239 case AV_ERR_STREAM_CHANGED: { 240 auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetOutputDescription(videoEnc), OH_AVFormat_Destroy); 241 if (format == nullptr) { 242 // 异常处理。 243 } 244 // 获取新宽高。 245 bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_WIDTH, &width) && 246 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_HEIGHT, &height); 247 if (!getIntRet) { 248 // 异常处理。 249 } 250 break; 251 } 252 default: { 253 // 异常处理。 254 return false; 255 } 256 } 257 return true; 258 } 259 ``` 260 2617. 编码器出帧处理循环。 262 263 ```c++ 264 bool result = true; 265 int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。 266 267 while (!outputDone && result) { 268 if (!outputDone ) { 269 result = EncoderOutput(videoEnc, timeoutUs); 270 } 271 } 272 ``` 273 2748. 调用OH_VideoEncoder_NotifyEndOfStream()通知编码器结束。 275 276 ```c++ 277 // Surface模式:通知视频编码器输入流已结束,只能使用此接口进行通知。 278 OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(videoEnc); 279 if (ret != AV_ERR_OK) { 280 // 异常处理。 281 } 282 ``` 283 2849. (可选)调用OH_VideoEncoder_Flush()刷新编码器。 285 286 调用OH_VideoEncoder_Flush接口后,编码器仍处于运行态,但会清除编码器中缓存的输入和输出数据及参数集如H.264格式的PPS/SPS。 287 此时需要调用[OH_VideoEncoder_Start](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_start)接口重新开始编码。 288 289 ```c++ 290 // 通过codecMutex来避免调用Flush接口,状态切换后,编码线程还在跑会退出循环的问题。 291 std::unique_lock<std::shared_mutex> lock(codecMutex); 292 // 刷新编码器videoEnc。 293 OH_AVErrCode flushRet = OH_VideoEncoder_Flush(videoEnc); 294 if (flushRet != AV_ERR_OK) { 295 // 异常处理。 296 } 297 298 // 重新开始编码。 299 OH_AVErrCode startRet = OH_VideoEncoder_Start(videoEnc); 300 if (startRet != AV_ERR_OK) { 301 // 异常处理。 302 } 303 ``` 304 30510. (可选)调用OH_VideoEncoder_Reset()重置编码器。 306 307 调用OH_VideoEncoder_Reset接口后,编码器回到初始化的状态,需要调用接口[OH_VideoEncoder_Configure](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_configure)和[OH_VideoEncoder_Prepare](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_prepare)重新配置。 308 309 ```c++ 310 // 重置编码器videoEnc。 311 std::unique_lock<std::shared_mutex> lock(codecMutex); 312 OH_AVErrCode resetRet = OH_VideoEncoder_Reset(videoEnc); 313 if (resetRet != AV_ERR_OK) { 314 // 异常处理。 315 } 316 317 // 重新配置编码器参数。 318 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 319 if (format == nullptr) { 320 // 异常处理。 321 } 322 OH_AVErrCode configRet = OH_VideoEncoder_Configure(videoEnc, format.get()); 323 if (configRet != AV_ERR_OK) { 324 // 异常处理。 325 } 326 327 // 编码器重新就绪。 328 OH_AVErrCode prepareRet = OH_VideoEncoder_Prepare(videoEnc); 329 if (prepareRet != AV_ERR_OK) { 330 // 异常处理。 331 } 332 ``` 333 334 > **注意:** 335 > 336 > 编码器回到初始化的状态,调用OH_VideoEncoder_Configure接口重新配置编码器参数时,同步模式需要重新配置OH_MD_KEY_ENABLE_SYNC_MODE为1,否则为异步模式。 337 > 338 33911. (可选)调用OH_VideoEncoder_Stop()停止编码器。 340 341 调用OH_VideoEncoder_Stop接口后,编码器保留了编码实例,释放输入输出buffer。开发者可以直接调用OH_VideoEncoder_Start接口继续编码,输入的第一个buffer需要携带参数集,从IDR帧开始送入。 342 343 ```c++ 344 // 终止编码器videoEnc。 345 std::unique_lock<std::shared_mutex> lock(codecMutex); 346 OH_AVErrCode ret = OH_VideoEncoder_Stop(videoEnc); 347 if (ret != AV_ERR_OK) { 348 // 异常处理。 349 } 350 ``` 351 35212. 调用OH_VideoEncoder_Destroy()销毁编码器实例,释放资源。 353 354 ```c++ 355 // 注销编码器。 356 std::unique_lock<std::shared_mutex> lock(codecMutex); 357 OH_AVErrCode ret = AV_ERR_OK; 358 if (videoEnc != nullptr) { 359 ret = OH_VideoEncoder_Destroy(videoEnc); 360 videoEnc = nullptr; 361 } 362 if (ret != AV_ERR_OK) { 363 // 异常处理。 364 } 365 ``` 366 367 > **说明:** 368 > 369 > 执行该步骤之后,需要开发者将videoEnc指向nullptr,防止野指针导致程序错误。 370 > 371 372### Buffer模式 373 374参考以下示例代码,可以完成Buffer模式下视频编码的全流程,实现同步模式的数据轮转。此处以输入YUV文件,编码成H.264格式为例。 375 3761. 创建编码器实例。 377 378 与Surface模式相同,此处不再赘述。 379 380 ```c++ 381 // 通过codecname创建编码器,应用有特殊需求,比如选择支持某种分辨率规格的编码器,可先查询capability,再根据codec name创建编码器。 382 OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true); 383 const char *name = OH_AVCapability_GetName(capability); 384 OH_AVCodec *videoEnc = OH_VideoEncoder_CreateByName(name); 385 if (videoEnc == nullptr) { 386 printf("create videoEnc failed"); 387 return; 388 } 389 ``` 390 3912. 调用OH_VideoEncoder_Configure()配置编码器。 392 393 与Surface模式相同,此处不再赘述。 394 395 ```c++ 396 auto format = std::shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy); 397 if (format == nullptr) { 398 // 异常处理。 399 } 400 // 写入format。 401 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_WIDTH, width); // 必须配置。 402 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_HEIGHT, height); // 必须配置。 403 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_PIXEL_FORMAT, pixelFormat);// 必须配置。 404 OH_AVFormat_SetIntValue(format.get(), OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 405 // 配置编码器。 406 OH_AVErrCode ret = OH_VideoEncoder_Configure(videoEnc, format.get()); 407 if (ret != AV_ERR_OK) { 408 // 异常处理。 409 } 410 ``` 411 412 > **注意:** 413 > 414 > 1. 要使能视频编码同步模式,必须将OH_MD_KEY_ENABLE_SYNC_MODE配置为1。 415 > 2. 同步模式在调用OH_VideoEncoder_Configure接口前不能调用OH_VideoEncoder_RegisterCallback或OH_VideoEncoder_RegisterParameterCallback接口,否则为异步模式。 416 > 417 4183. 调用OH_VideoEncoder_Prepare()编码器就绪。 419 420 该接口将在编码器运行前进行一些数据的准备工作。 421 422 ```c++ 423 ret = OH_VideoEncoder_Prepare(videoEnc); 424 if (ret != AV_ERR_OK) { 425 // 异常处理。 426 } 427 ``` 428 4294. 调用OH_VideoEncoder_Start()启动编码器。 430 431 配置输入文件、输出文件。 432 433 ```c++ 434 // 配置待编码文件路径。 435 std::string_view inputFilePath = "/*yourpath*.yuv"; 436 std::string_view outputFilePath = "/*yourpath*.h264"; 437 std::unique_ptr<std::ifstream> inputFile = std::make_unique<std::ifstream>(); 438 std::unique_ptr<std::ofstream> outputFile = std::make_unique<std::ofstream>(); 439 if (inputFile != nullptr) { 440 inputFile->open(inputFilePath.data(), std::ios::in | std::ios::binary); 441 } 442 if (outputFile != nullptr) { 443 outputFile->open(outputFilePath.data(), std::ios::out | std::ios::binary | std::ios::ate); 444 } 445 // 启动编码器,开始编码。 446 OH_AVErrCode ret = OH_VideoEncoder_Start(videoEnc); 447 if (ret != AV_ERR_OK) { 448 // 异常处理。 449 } 450 ``` 451 4525. 获取可用buffer并写入码流至编码器。 453 454 - 调用[OH_VideoEncoder_QueryInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryinputbuffer)接口获取下一个可用的输入缓冲区(buffer)的索引(index)。 455 - 根据获取的索引(index),调用[OH_VideoEncoder_GetInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getinputbuffer)接口获取对应的缓冲区(buffer)实例。 456 - 将需要编码的数据写入该缓冲区(buffer)后,调用[OH_VideoEncoder_PushInputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_pushinputbuffer)接口将其送入编码输入队列进行编码。当所有待处理数据全部传递给编码器后,需要将flag标识成AVCODEC_BUFFER_FLAGS_EOS,通知编码器输入结束。 457 458 459 示例中的变量size、offset、pts、frameData、flags说明与Surface模式相同,此处不再赘述。 460 461 ```c++ 462 bool EncoderInput(OH_AVCodec *videoEnc, int64_t timeoutUs) 463 { 464 uint32_t index; 465 std::shared_lock<std::shared_mutex> lock(codecMutex); 466 467 OH_AVErrCode ret = OH_VideoEncoder_QueryInputBuffer(videoEnc, &index, timeoutUs); 468 switch (ret) { 469 case AV_ERR_OK: { 470 OH_AVBuffer *buffer = OH_VideoEncoder_GetInputBuffer(videoEnc, index); 471 if (buffer == nullptr) { 472 // 异常处理。 473 return false; 474 } 475 476 // 写入图像数据。 477 int32_t frameSize = 0; 478 bool isFirstFrame = true; 479 // 获取视频宽跨距和高跨距。 480 if (isFirstFrame) { 481 auto format = std::shared_ptr<OH_AVFormat>(OH_VideoEncoder_GetInputDescription(videoEnc), OH_AVFormat_Destroy); 482 if (format == nullptr) { 483 // 异常处理。 484 } 485 bool getIntRet = OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_STRIDE, &widthStride) && 486 OH_AVFormat_GetIntValue(format.get(), OH_MD_KEY_VIDEO_SLICE_HEIGHT, &heightStride); 487 if (!getIntRet) { 488 // 异常处理。 489 } 490 isFirstFrame = false; 491 } 492 if (widthStride == width && heightStride == height) { 493 frameSize = width * height * 3 / 2; // NV12像素格式下,每帧数据大小的计算公式。 494 // 处理文件流得到帧的长度,再将需要编码的数据写入到对应index的buffer中。 495 uint8_t *addr = OH_AVBuffer_GetAddr(buffer); 496 if (addr == nullptr) { 497 // 异常处理 498 return false; 499 } 500 if (inputFile != nullptr && inputFile->is_open()) { 501 inputFile->read(reinterpret_cast<char *>(addr), frameSize); 502 } 503 } else { 504 // 如果跨距不等于宽,开发者需要按照跨距进行偏移,详情请参考视频编码Buffer模式“步骤-8. 写入编码图像”。 505 } 506 507 // 配置buffer info信息。 508 OH_AVCodecBufferAttr info; 509 info.size = frameSize; 510 info.offset = 0; 511 info.pts = 0; 512 OH_AVErrCode setBufferRet = OH_AVBuffer_SetBufferAttr(buffer, &info); 513 if (setBufferRet != AV_ERR_OK) { 514 // 异常处理。 515 return false; 516 } 517 // 送入编码输入队列进行编码,index为对应输入队列的下标。 518 OH_AVErrCode pushInputRet = OH_VideoEncoder_PushInputBuffer(videoEnc, index); 519 if (pushInputRet != AV_ERR_OK) { 520 // 异常处理。 521 return false; 522 } 523 if (inFile_->eof()) { 524 inputDone = 1; 525 } 526 break; 527 } 528 case AV_ERR_TRY_AGAIN_LATER: { 529 break; 530 } 531 default: { 532 // 异常处理。 533 return false; 534 } 535 } 536 return true; 537 } 538 ``` 539 540 5416. 获取可用buffer并释放编码帧。 542 543 - 调用[OH_VideoEncoder_QueryOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_queryoutputbuffer)接口获取下一个可用的输出缓冲区(buffer)的索引(index)。 544 - 根据获取的索引(index),调用[OH_VideoEncoder_GetOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_getoutputbuffer)接口获取对应的缓冲区(buffer)实例。 545 - 调用[OH_VideoEncoder_FreeOutputBuffer](../../reference/apis-avcodec-kit/_video_encoder.md#oh_videoencoder_freeoutputbuffer)接口释放编码帧。 546 547 548 ```c++ 549 bool EncoderOutput(OH_AVCodec *videoEnc, int64_t timeoutUs) 550 { 551 uint32_t index; 552 std::shared_lock<std::shared_mutex> lock(codecMutex); 553 554 OH_AVErrCode ret = OH_VideoEncoder_QueryOutputBuffer(videoEnc, &index, timeoutUs); 555 switch (ret) { 556 case AV_ERR_OK: { 557 OH_AVBuffer *buffer = OH_VideoEncoder_GetOutputBuffer(videoEnc, index); 558 if (buffer == nullptr) { 559 // 异常处理。 560 return false; 561 } 562 563 // 获取编码后信息。 564 OH_AVCodecBufferAttr info; 565 OH_AVErrCode getBufferRet = OH_AVBuffer_GetBufferAttr(buffer, &info); 566 if (getBufferRet != AV_ERR_OK) { 567 // 异常处理。 568 return false; 569 } 570 // 将编码完成帧数据buffer写入到对应输出文件中。 571 uint8_t *addr = OH_AVBuffer_GetAddr(buffer); 572 if (addr == nullptr) { 573 // 异常处理 574 return false; 575 } 576 if (outputFile != nullptr && outputFile->is_open()) { 577 outputFile->write(reinterpret_cast<char *>(addr), info.size); 578 } 579 if (info.flags & AVCODEC_BUFFER_FLAGS_EOS) { 580 outputDone = 1; 581 } 582 // 释放已完成处理的信息,index为对应buffer队列的下标。 583 OH_AVErrCode freeOutputRet = OH_VideoEncoder_FreeOutputBuffer(videoEnc, index); 584 if (freeOutputRet != AV_ERR_OK) { 585 // 异常处理。 586 return false; 587 } 588 break; 589 } 590 case AV_ERR_TRY_AGAIN_LATER: { 591 break; 592 } 593 case AV_ERR_STREAM_CHANGED: { 594 break; 595 } 596 default: { 597 // 异常处理。 598 return false; 599 } 600 } 601 return true; 602 } 603 ``` 604 6057. 编码器送帧/出帧处理循环。 606 607 ```c++ 608 bool result = true; 609 int64_t timeoutUs = 0; // 单位:微秒(us),负值:无限等待;0:立即退出;正值:等待指定时长后退出。 610 611 while (!outputDone && result) { 612 if (!inputDone) { 613 result = EncoderInput(videoEnc, timeoutUs); 614 } 615 if (!outputDone) { 616 result = EncoderOutput(videoEnc, timeoutUs); 617 } 618 } 619 ``` 620 621后续流程(包括刷新、重置、停止和销毁编码器)与Surface模式基本一致,请参考[Surface模式](#surface模式)的步骤9-12。