FFmpegDataEncoder.cpp (19702B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FFmpegDataEncoder.h" 8 9 #include <utility> 10 11 #include "FFmpegLog.h" 12 #include "FFmpegUtils.h" 13 #include "PlatformEncoderModule.h" 14 #include "libavutil/error.h" 15 #include "mozilla/StaticMutex.h" 16 #include "mozilla/StaticPrefs_media.h" 17 18 namespace mozilla { 19 20 template <> 21 AVCodecID GetFFmpegEncoderCodecId<LIBAV_VER>(CodecType aCodec) { 22 #if LIBAVCODEC_VERSION_MAJOR >= 58 23 if (XRE_IsParentProcess() || XRE_IsContentProcess() || 24 StaticPrefs::media_use_remote_encoder_video()) { 25 if (aCodec == CodecType::VP8) { 26 return AV_CODEC_ID_VP8; 27 } 28 29 if (aCodec == CodecType::VP9) { 30 return AV_CODEC_ID_VP9; 31 } 32 33 if (aCodec == CodecType::H264) { 34 return AV_CODEC_ID_H264; 35 } 36 37 if (aCodec == CodecType::H265) { 38 return AV_CODEC_ID_HEVC; 39 } 40 41 if (aCodec == CodecType::AV1) { 42 return AV_CODEC_ID_AV1; 43 } 44 } 45 46 if (XRE_IsParentProcess() || XRE_IsContentProcess() || 47 StaticPrefs::media_use_remote_encoder_audio()) { 48 if (aCodec == CodecType::Opus) { 49 return AV_CODEC_ID_OPUS; 50 } 51 52 if (aCodec == CodecType::Vorbis) { 53 return AV_CODEC_ID_VORBIS; 54 } 55 } 56 #endif 57 return AV_CODEC_ID_NONE; 58 } 59 60 /* static */ 61 AVCodec* FFmpegDataEncoder<LIBAV_VER>::FindSoftwareEncoder( 62 const FFmpegLibWrapper* aLib, AVCodecID aCodecId) { 63 MOZ_ASSERT(aLib); 64 65 // We use this instead of MOZ_USE_HWDECODE because it is possible to disable 66 // support for hardware encoding in Firefox, while the system ffmpeg library 67 // still exposes the hardware codecs. 68 #if LIBAVCODEC_VERSION_MAJOR >= 58 69 AVCodec* fallbackCodec = nullptr; 70 void* opaque = nullptr; 71 while (AVCodec* codec = aLib->av_codec_iterate(&opaque)) { 72 if (codec->id != aCodecId || !aLib->av_codec_is_encoder(codec)) { 73 continue; 74 } 75 76 if (codec->capabilities & AV_CODEC_CAP_HARDWARE) { 77 continue; 78 } 79 80 // Prioritize libx264 for now since it's the only h264 codec we tested. 81 // Once libopenh264 is supported, we can simply use the first one we find. 82 if (aCodecId == AV_CODEC_ID_H264 && strcmp(codec->name, "libx264") != 0) { 83 if (!fallbackCodec) { 84 fallbackCodec = codec; 85 } 86 continue; 87 } 88 89 if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) { 90 if (!fallbackCodec) { 91 fallbackCodec = codec; 92 } 93 continue; 94 } 95 96 FFMPEGV_LOG("Using preferred software codec %s", codec->name); 97 return codec; 98 } 99 100 if (fallbackCodec) { 101 FFMPEGV_LOG("Using fallback software codec %s", fallbackCodec->name); 102 } 103 return fallbackCodec; 104 #else 105 // Prioritize libx264 for now since it's the only h264 codec we tested. Once 106 // libopenh264 is supported, we can simply use `avcodec_find_encoder` and 107 // rename this function. 108 if (aCodecId == AV_CODEC_ID_H264) { 109 AVCodec* codec = aLib->avcodec_find_encoder_by_name("libx264"); 110 if (codec) { 111 FFMPEGV_LOG("Prefer libx264 for h264 codec"); 112 return codec; 113 } 114 FFMPEGV_LOG("Fallback to other h264 library. Fingers crossed"); 115 } 116 117 return aLib->avcodec_find_encoder(aCodecId); 118 #endif 119 } 120 121 #ifdef MOZ_USE_HWDECODE 122 /* static */ 123 AVCodec* FFmpegDataEncoder<LIBAV_VER>::FindHardwareEncoder( 124 const FFmpegLibWrapper* aLib, AVCodecID aCodecId) { 125 MOZ_ASSERT(aLib); 126 127 AVCodec* fallbackCodec = nullptr; 128 void* opaque = nullptr; 129 while (AVCodec* codec = aLib->av_codec_iterate(&opaque)) { 130 if (codec->id != aCodecId || !aLib->av_codec_is_encoder(codec)) { 131 continue; 132 } 133 134 bool hasHwConfig = codec->capabilities & AV_CODEC_CAP_HARDWARE; 135 if (!hasHwConfig) { 136 for (int i = 0; const AVCodecHWConfig* config = 137 aLib->avcodec_get_hw_config(codec, i); 138 ++i) { 139 if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) { 140 hasHwConfig = true; 141 break; 142 } 143 } 144 } 145 146 if (!hasHwConfig) { 147 continue; 148 } 149 150 if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) { 151 if (!fallbackCodec) { 152 fallbackCodec = codec; 153 } 154 continue; 155 } 156 157 FFMPEGV_LOG("Using preferred hardware codec %s", codec->name); 158 return codec; 159 } 160 161 if (fallbackCodec) { 162 FFMPEGV_LOG("Using fallback hardware codec %s", fallbackCodec->name); 163 } 164 return fallbackCodec; 165 } 166 #endif 167 168 /* static */ 169 Result<RefPtr<MediaRawData>, MediaResult> 170 FFmpegDataEncoder<LIBAV_VER>::CreateMediaRawData(AVPacket* aPacket) { 171 MOZ_ASSERT(aPacket); 172 173 // Copy frame data from AVPacket. 174 auto data = MakeRefPtr<MediaRawData>(); 175 UniquePtr<MediaRawDataWriter> writer(data->CreateWriter()); 176 if (!writer->Append(aPacket->data, static_cast<size_t>(aPacket->size))) { 177 return Err(MediaResult(NS_ERROR_OUT_OF_MEMORY, 178 "fail to allocate MediaRawData buffer"_ns)); 179 } 180 return data; 181 } 182 183 StaticMutex FFmpegDataEncoder<LIBAV_VER>::sMutex; 184 185 FFmpegDataEncoder<LIBAV_VER>::FFmpegDataEncoder( 186 const FFmpegLibWrapper* aLib, AVCodecID aCodecID, 187 const RefPtr<TaskQueue>& aTaskQueue, const EncoderConfig& aConfig) 188 : mLib(aLib), 189 mCodecID(aCodecID), 190 mTaskQueue(aTaskQueue), 191 mConfig(aConfig), 192 mCodecName(EmptyCString()), 193 mCodecContext(nullptr), 194 mFrame(nullptr), 195 mVideoCodec(IsVideoCodec(aCodecID)) { 196 MOZ_ASSERT(mLib); 197 MOZ_ASSERT(mTaskQueue); 198 #if LIBAVCODEC_VERSION_MAJOR < 58 199 MOZ_CRASH("FFmpegDataEncoder needs ffmpeg 58 at least."); 200 #endif 201 }; 202 203 RefPtr<MediaDataEncoder::EncodePromise> FFmpegDataEncoder<LIBAV_VER>::Encode( 204 const MediaData* aSample) { 205 MOZ_ASSERT(aSample != nullptr); 206 207 FFMPEG_LOG("Encode"); 208 return InvokeAsync( 209 mTaskQueue, __func__, 210 [self = RefPtr<FFmpegDataEncoder<LIBAV_VER>>(this), 211 sample = RefPtr<MediaData>(const_cast<MediaData*>(aSample))]() { 212 return self->ProcessEncode({sample}); 213 }); 214 } 215 216 RefPtr<MediaDataEncoder::EncodePromise> FFmpegDataEncoder<LIBAV_VER>::Encode( 217 nsTArray<RefPtr<MediaData>>&& aSamples) { 218 MOZ_ASSERT(!aSamples.IsEmpty()); 219 220 FFMPEG_LOG("Encode: %zu samples", aSamples.Length()); 221 return InvokeAsync(mTaskQueue, __func__, 222 [self = RefPtr<FFmpegDataEncoder<LIBAV_VER>>(this), 223 samples = std::move(aSamples)]() mutable { 224 return self->ProcessEncode(std::move(samples)); 225 }); 226 } 227 228 RefPtr<MediaDataEncoder::ReconfigurationPromise> 229 FFmpegDataEncoder<LIBAV_VER>::Reconfigure( 230 const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges) { 231 return InvokeAsync(mTaskQueue, this, __func__, 232 &FFmpegDataEncoder<LIBAV_VER>::ProcessReconfigure, 233 aConfigurationChanges); 234 } 235 236 RefPtr<MediaDataEncoder::EncodePromise> FFmpegDataEncoder<LIBAV_VER>::Drain() { 237 FFMPEG_LOG("Drain"); 238 return InvokeAsync(mTaskQueue, this, __func__, 239 &FFmpegDataEncoder::ProcessDrain); 240 } 241 242 RefPtr<ShutdownPromise> FFmpegDataEncoder<LIBAV_VER>::Shutdown() { 243 FFMPEG_LOG("Shutdown"); 244 return InvokeAsync(mTaskQueue, this, __func__, 245 &FFmpegDataEncoder::ProcessShutdown); 246 } 247 248 RefPtr<GenericPromise> FFmpegDataEncoder<LIBAV_VER>::SetBitrate( 249 uint32_t aBitrate) { 250 FFMPEG_LOG("SetBitrate"); 251 return GenericPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); 252 } 253 254 RefPtr<MediaDataEncoder::EncodePromise> FFmpegDataEncoder< 255 LIBAV_VER>::ProcessEncode(nsTArray<RefPtr<MediaData>>&& aSamples) { 256 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 257 258 FFMPEG_LOG("ProcessEncode: %zu samples", aSamples.Length()); 259 260 #if LIBAVCODEC_VERSION_MAJOR < 58 261 // TODO(Bug 1868253): implement encode with avcodec_encode_video2(). 262 MOZ_CRASH("FFmpegDataEncoder needs ffmpeg 58 at least."); 263 return EncodePromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); 264 #else 265 EncodedData output; 266 for (auto& sample : aSamples) { 267 auto rv = EncodeInputWithModernAPIs(sample); 268 if (rv.isErr()) { 269 MediaResult e = rv.unwrapErr(); 270 FFMPEG_LOG("%s", e.Description().get()); 271 return EncodePromise::CreateAndReject(e, __func__); 272 } 273 output.AppendElements(rv.unwrap()); 274 } 275 return EncodePromise::CreateAndResolve(std::move(output), __func__); 276 #endif 277 } 278 279 RefPtr<MediaDataEncoder::ReconfigurationPromise> 280 FFmpegDataEncoder<LIBAV_VER>::ProcessReconfigure( 281 const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges) { 282 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 283 284 FFMPEG_LOG("ProcessReconfigure"); 285 286 bool ok = false; 287 for (const auto& confChange : aConfigurationChanges->mChanges) { 288 // A reconfiguration on the fly succeeds if all changes can be applied 289 // successfuly. In case of failure, the encoder will be drained and 290 // recreated. 291 ok &= confChange.match( 292 // Not supported yet 293 [&](const DimensionsChange& aChange) -> bool { return false; }, 294 [&](const DisplayDimensionsChange& aChange) -> bool { return false; }, 295 [&](const BitrateModeChange& aChange) -> bool { return false; }, 296 [&](const BitrateChange& aChange) -> bool { 297 // Verified on x264 298 if (!strcmp(mCodecContext->codec->name, "libx264")) { 299 MOZ_ASSERT(aChange.get().ref() != 0); 300 mConfig.mBitrate = aChange.get().ref(); 301 mCodecContext->bit_rate = 302 static_cast<FFmpegBitRate>(mConfig.mBitrate); 303 return true; 304 } 305 return false; 306 }, 307 [&](const FramerateChange& aChange) -> bool { return false; }, 308 [&](const UsageChange& aChange) -> bool { return false; }, 309 [&](const ContentHintChange& aChange) -> bool { return false; }, 310 [&](const SampleRateChange& aChange) -> bool { return false; }, 311 [&](const NumberOfChannelsChange& aChange) -> bool { return false; }); 312 }; 313 using P = MediaDataEncoder::ReconfigurationPromise; 314 if (ok) { 315 return P::CreateAndResolve(true, __func__); 316 } 317 return P::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); 318 } 319 320 RefPtr<MediaDataEncoder::EncodePromise> 321 FFmpegDataEncoder<LIBAV_VER>::ProcessDrain() { 322 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 323 324 FFMPEG_LOG("ProcessDrain"); 325 326 #if LIBAVCODEC_VERSION_MAJOR < 58 327 MOZ_CRASH("FFmpegDataEncoder needs ffmpeg 58 at least."); 328 return EncodePromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__); 329 #else 330 auto rv = DrainWithModernAPIs(); 331 if (rv.isErr()) { 332 MediaResult e = rv.unwrapErr(); 333 FFMPEG_LOG("%s", e.Description().get()); 334 return EncodePromise::CreateAndReject(rv.inspectErr(), __func__); 335 } 336 return EncodePromise::CreateAndResolve(rv.unwrap(), __func__); 337 #endif 338 } 339 340 RefPtr<ShutdownPromise> FFmpegDataEncoder<LIBAV_VER>::ProcessShutdown() { 341 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 342 343 FFMPEG_LOG("ProcessShutdown"); 344 345 ShutdownInternal(); 346 347 // Don't shut mTaskQueue down since it's owned by others. 348 return ShutdownPromise::CreateAndResolve(true, __func__); 349 } 350 351 void FFmpegDataEncoder<LIBAV_VER>::SetContextBitrate() { 352 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 353 MOZ_ASSERT(mCodecContext); 354 355 if (mConfig.mBitrateMode == BitrateMode::Constant) { 356 mCodecContext->rc_max_rate = static_cast<FFmpegBitRate>(mConfig.mBitrate); 357 mCodecContext->rc_min_rate = static_cast<FFmpegBitRate>(mConfig.mBitrate); 358 mCodecContext->bit_rate = static_cast<FFmpegBitRate>(mConfig.mBitrate); 359 FFMPEG_LOG("Encoding in CBR: %d", mConfig.mBitrate); 360 } else { 361 mCodecContext->rc_max_rate = static_cast<FFmpegBitRate>(mConfig.mBitrate); 362 mCodecContext->rc_min_rate = 0; 363 mCodecContext->bit_rate = static_cast<FFmpegBitRate>(mConfig.mBitrate); 364 FFMPEG_LOG("Encoding in VBR: [%d;%d]", (int)mCodecContext->rc_min_rate, 365 (int)mCodecContext->rc_max_rate); 366 } 367 } 368 369 void FFmpegDataEncoder<LIBAV_VER>::ShutdownInternal() { 370 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 371 372 FFMPEG_LOG("ShutdownInternal"); 373 374 DestroyFrame(); 375 376 if (mCodecContext) { 377 ReleaseCodecContext(); 378 mCodecContext = nullptr; 379 } 380 } 381 382 Result<AVCodecContext*, MediaResult> 383 FFmpegDataEncoder<LIBAV_VER>::AllocateCodecContext(bool aHardware) { 384 AVCodec* codec = nullptr; 385 if (aHardware) { 386 #ifdef MOZ_USE_HWDECODE 387 codec = FindHardwareEncoder(mLib, mCodecID); 388 #endif 389 } else { 390 codec = FindSoftwareEncoder(mLib, mCodecID); 391 } 392 if (!codec) { 393 return Err(MediaResult( 394 NS_ERROR_DOM_MEDIA_FATAL_ERR, 395 RESULT_DETAIL("failed to find ffmpeg encoder for codec id %d", 396 mCodecID))); 397 } 398 399 AVCodecContext* ctx = mLib->avcodec_alloc_context3(codec); 400 if (!ctx) { 401 return Err(MediaResult( 402 NS_ERROR_OUT_OF_MEMORY, 403 RESULT_DETAIL("failed to allocate ffmpeg context for codec %s", 404 codec->name))); 405 } 406 407 MOZ_ASSERT(ctx->codec == codec); 408 409 return ctx; 410 } 411 412 int FFmpegDataEncoder<LIBAV_VER>::OpenCodecContext(const AVCodec* aCodec, 413 AVDictionary** aOptions) { 414 MOZ_ASSERT(mCodecContext); 415 416 StaticMutexAutoLock mon(sMutex); 417 return mLib->avcodec_open2(mCodecContext, aCodec, aOptions); 418 } 419 420 void FFmpegDataEncoder<LIBAV_VER>::ReleaseCodecContext() { 421 StaticMutexAutoLock mon(sMutex); 422 if (!mCodecContext) { 423 return; 424 } 425 426 #if LIBAVCODEC_VERSION_MAJOR < 57 427 mLib->avcodec_close(mCodecContext); 428 mLib->av_freep(&mCodecContext); 429 #else 430 mLib->avcodec_free_context(&mCodecContext); 431 #endif 432 } 433 434 bool FFmpegDataEncoder<LIBAV_VER>::PrepareFrame() { 435 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 436 437 // TODO: Merge the duplicate part with FFmpegDataDecoder's PrepareFrame. 438 #if LIBAVCODEC_VERSION_MAJOR >= 55 439 if (mFrame) { 440 mLib->av_frame_unref(mFrame); 441 } else { 442 mFrame = mLib->av_frame_alloc(); 443 } 444 #elif LIBAVCODEC_VERSION_MAJOR == 54 445 if (mFrame) { 446 mLib->avcodec_get_frame_defaults(mFrame); 447 } else { 448 mFrame = mLib->avcodec_alloc_frame(); 449 } 450 #else 451 mLib->av_freep(&mFrame); 452 mFrame = mLib->avcodec_alloc_frame(); 453 #endif 454 return !!mFrame; 455 } 456 457 void FFmpegDataEncoder<LIBAV_VER>::DestroyFrame() { 458 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 459 if (mFrame) { 460 #if LIBAVCODEC_VERSION_MAJOR >= 55 461 mLib->av_frame_unref(mFrame); 462 mLib->av_frame_free(&mFrame); 463 #elif LIBAVCODEC_VERSION_MAJOR == 54 464 mLib->avcodec_free_frame(&mFrame); 465 #else 466 mLib->av_freep(&mFrame); 467 #endif 468 mFrame = nullptr; 469 } 470 } 471 472 // avcodec_send_frame and avcodec_receive_packet were introduced in version 58. 473 #if LIBAVCODEC_VERSION_MAJOR >= 58 474 Result<MediaDataEncoder::EncodedData, MediaResult> 475 FFmpegDataEncoder<LIBAV_VER>::EncodeWithModernAPIs() { 476 // Initialize AVPacket. 477 AVPacket* pkt = mLib->av_packet_alloc(); 478 479 if (!pkt) { 480 return Err( 481 MediaResult(NS_ERROR_OUT_OF_MEMORY, "failed to allocate packet"_ns)); 482 } 483 484 auto freePacket = MakeScopeExit([this, &pkt] { mLib->av_packet_free(&pkt); }); 485 486 // Send frame and receive packets. 487 if (int ret = mLib->avcodec_send_frame(mCodecContext, mFrame); ret < 0) { 488 // In theory, avcodec_send_frame could sent -EAGAIN to signal its internal 489 // buffers is full. In practice this can't happen as we only feed one frame 490 // at a time, and we immediately call avcodec_receive_packet right after. 491 // TODO: Create a NS_ERROR_DOM_MEDIA_ENCODE_ERR in ErrorList.py? 492 return Err(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 493 RESULT_DETAIL("avcodec_send_frame error: %s", 494 MakeErrorString(mLib, ret).get()))); 495 } 496 497 EncodedData output; 498 while (true) { 499 int ret = mLib->avcodec_receive_packet(mCodecContext, pkt); 500 if (ret == AVERROR(EAGAIN)) { 501 // The encoder is asking for more inputs. 502 FFMPEG_LOG("encoder is asking for more input!"); 503 break; 504 } 505 506 if (ret < 0) { 507 // AVERROR_EOF is returned when the encoder has been fully flushed, but it 508 // shouldn't happen here. 509 return Err(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 510 RESULT_DETAIL("avcodec_receive_packet error: %s", 511 MakeErrorString(mLib, ret).get()))); 512 } 513 514 auto r = ToMediaRawData(pkt); 515 mLib->av_packet_unref(pkt); 516 if (r.isErr()) { 517 MediaResult e = r.unwrapErr(); 518 FFMPEG_LOG("%s", e.Description().get()); 519 return Err(e); 520 } 521 522 RefPtr<MediaRawData> d = r.unwrap(); 523 if (!d) { 524 // This can happen if e.g. DTX is enabled 525 FFMPEG_LOG("No encoded packet output"); 526 continue; 527 } 528 output.AppendElement(std::move(d)); 529 } 530 531 FFMPEG_LOG("Got %zu encoded data", output.Length()); 532 return std::move(output); 533 } 534 535 Result<MediaDataEncoder::EncodedData, MediaResult> 536 FFmpegDataEncoder<LIBAV_VER>::DrainWithModernAPIs() { 537 MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); 538 MOZ_ASSERT(mCodecContext); 539 540 // TODO: Create a common utility to merge the duplicate code below with 541 // EncodeWithModernAPIs above. 542 543 // Initialize AVPacket. 544 AVPacket* pkt = mLib->av_packet_alloc(); 545 if (!pkt) { 546 return Err( 547 MediaResult(NS_ERROR_OUT_OF_MEMORY, "failed to allocate packet"_ns)); 548 } 549 auto freePacket = MakeScopeExit([this, &pkt] { mLib->av_packet_free(&pkt); }); 550 551 // Enter draining mode by sending NULL to the avcodec_send_frame(). Note that 552 // this can leave the encoder in a permanent EOF state after draining. As a 553 // result, the encoder is unable to continue encoding. A new 554 // AVCodecContext/encoder creation is required if users need to encode after 555 // draining. 556 // 557 // TODO: Use `avcodec_flush_buffers` to drain the pending packets if 558 // AV_CODEC_CAP_ENCODER_FLUSH is set in mCodecContext->codec->capabilities. 559 if (int ret = mLib->avcodec_send_frame(mCodecContext, nullptr); ret < 0) { 560 if (ret == AVERROR_EOF) { 561 // The encoder has been flushed. Drain can be called multiple time. 562 FFMPEG_LOG("encoder has been flushed!"); 563 return EncodedData(); 564 } 565 566 return Err(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 567 RESULT_DETAIL("avcodec_send_frame error: %s", 568 MakeErrorString(mLib, ret).get()))); 569 } 570 571 EncodedData output; 572 while (true) { 573 int ret = mLib->avcodec_receive_packet(mCodecContext, pkt); 574 if (ret == AVERROR_EOF) { 575 FFMPEG_LOG("encoder has no more output packet!"); 576 break; 577 } 578 579 if (ret < 0) { 580 // avcodec_receive_packet should not result in a -EAGAIN once it's in 581 // draining mode. 582 return Err(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 583 RESULT_DETAIL("avcodec_receive_packet error: %s", 584 MakeErrorString(mLib, ret).get()))); 585 } 586 587 auto r = ToMediaRawData(pkt); 588 mLib->av_packet_unref(pkt); 589 if (r.isErr()) { 590 MediaResult e = r.unwrapErr(); 591 FFMPEG_LOG("%s", e.Description().get()); 592 return Err(e); 593 } 594 595 RefPtr<MediaRawData> d = r.unwrap(); 596 if (!d) { 597 return Err( 598 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 599 "failed to create a MediaRawData from the AVPacket"_ns)); 600 } 601 output.AppendElement(std::move(d)); 602 } 603 604 FFMPEG_LOG("Encoding successful, %zu packets", output.Length()); 605 606 // TODO: Evaluate a better solution (Bug 1869466) 607 // TODO: Only re-create AVCodecContext when avcodec_flush_buffers is 608 // unavailable. 609 ShutdownInternal(); 610 MediaResult r = InitEncoder(); 611 if (NS_FAILED(r.Code())) { 612 FFMPEG_LOG("%s", r.Description().get()); 613 return Err(r); 614 } 615 616 return std::move(output); 617 } 618 #endif // LIBAVCODEC_VERSION_MAJOR >= 58 619 620 } // namespace mozilla