@@ -274,13 +274,37 @@ static Result<MediaDataEncoder::EncodedData, MediaResult> Encode(
274274 MediaDataEncoder::EncodedData output;
275275 for (size_t i = 0 ; i < aNumFrames; i++) {
276276 RefPtr<MediaData> frame = aSource.GetFrame (i);
277- output.AppendElements (MOZ_TRY (
278- WaitFor (aEncoder->Encode (frame))));
277+ output.AppendElements (MOZ_TRY (WaitFor (aEncoder->Encode (frame))));
279278 }
280279 output.AppendElements (std::move (MOZ_TRY (Drain (aEncoder))));
281280 return output;
282281}
283282
283+ static Result<MediaDataEncoder::EncodedData, MediaResult> EncodeBatch (
284+ const RefPtr<MediaDataEncoder>& aEncoder, const size_t aTotalNumFrames,
285+ MediaDataEncoderTest::FrameSource& aSource, const size_t aBatchSize) {
286+ if (aBatchSize == 0 || aTotalNumFrames == 0 ) {
287+ return Err (MediaResult (
288+ NS_ERROR_INVALID_ARG,
289+ " Batch size and total number of frames must be greater than 0" ));
290+ }
291+
292+ MediaDataEncoder::EncodedData output;
293+ nsTArray<RefPtr<MediaData>> frames;
294+ for (size_t i = 0 ; i < aTotalNumFrames; i++) {
295+ frames.AppendElement (aSource.GetFrame (i));
296+ if (frames.Length () == aBatchSize || i == aTotalNumFrames - 1 ) {
297+ nsTArray<RefPtr<MediaData>> batch = std::move (frames);
298+ output.AppendElements (
299+ MOZ_TRY (WaitFor (aEncoder->Encode (std::move (batch)))));
300+ }
301+ }
302+ MOZ_RELEASE_ASSERT (frames.IsEmpty ());
303+
304+ output.AppendElements (std::move (MOZ_TRY (Drain (aEncoder))));
305+ return output;
306+ }
307+
284308Result<uint8_t , nsresult> GetNALUSize (const mozilla::MediaRawData* aSample) {
285309 return AVCCConfig::Parse (aSample).map (
286310 [](AVCCConfig config) { return config.NALUSize (); });
@@ -445,6 +469,90 @@ TEST_F(MediaDataEncoderTest, H264Encodes4KAVCCRealtime) {
445469 H264EncodesTest (Usage::Realtime, AsVariant (kH264SpecificAVCC ), mData4K );
446470}
447471
472+ static void H264EncodeBatchTest (
473+ Usage aUsage, const EncoderConfig::CodecSpecific& aSpecific,
474+ MediaDataEncoderTest::FrameSource& aFrameSource) {
475+ ASSERT_TRUE (aSpecific.is <H264Specific>());
476+ ASSERT_TRUE (aSpecific.as <H264Specific>().mFormat ==
477+ H264BitStreamFormat::ANNEXB ||
478+ aSpecific.as <H264Specific>().mFormat == H264BitStreamFormat::AVC);
479+
480+ RUN_IF_SUPPORTED (CodecType::H264, [&]() {
481+ bool isAVCC =
482+ aSpecific.as <H264Specific>().mFormat == H264BitStreamFormat::AVC;
483+
484+ RefPtr<MediaDataEncoder> e = CreateH264Encoder (
485+ aUsage, EncoderConfig::SampleFormat (dom::ImageBitmapFormat::YUV420P),
486+ aFrameSource.GetSize (), ScalabilityMode::None, aSpecific);
487+ EnsureInit (e);
488+
489+ constexpr size_t batchSize = 6 ;
490+ MediaDataEncoder::EncodedData output = GET_OR_RETURN_ON_ERROR (
491+ EncodeBatch (e, NUM_FRAMES, aFrameSource, batchSize));
492+ if (aUsage == Usage::Realtime && kImageSize4K <= aFrameSource.GetSize ()) {
493+ // Realtime encoding may drop frames for large frame sizes.
494+ EXPECT_LE (output.Length (), NUM_FRAMES);
495+ } else {
496+ EXPECT_EQ (output.Length (), NUM_FRAMES);
497+ }
498+ if (isAVCC) {
499+ uint8_t naluSize = GetNALUSize (output[0 ]).unwrapOr (0 );
500+ EXPECT_GT (naluSize, 0 );
501+ EXPECT_LE (naluSize, 4 );
502+ for (auto frame : output) {
503+ if (frame->mExtraData && !frame->mExtraData ->IsEmpty ()) {
504+ naluSize = GetNALUSize (frame).unwrapOr (0 );
505+ EXPECT_GT (naluSize, 0 );
506+ EXPECT_LE (naluSize, 4 );
507+ }
508+ EXPECT_TRUE (IsValidAVCC (frame, naluSize).isOk ());
509+ }
510+ } else {
511+ for (auto frame : output) {
512+ EXPECT_TRUE (AnnexB::IsAnnexB (frame));
513+ }
514+ }
515+
516+ WaitForShutdown (e);
517+ });
518+ };
519+
520+ TEST_F (MediaDataEncoderTest, H264EncodeBatchAnnexBRecord) {
521+ H264EncodeBatchTest (Usage::Record, AsVariant (kH264SpecificAnnexB ), mData );
522+ }
523+
524+ TEST_F (MediaDataEncoderTest, H264EncodeBatchAnnexBRealtime) {
525+ H264EncodeBatchTest (Usage::Realtime, AsVariant (kH264SpecificAnnexB ), mData );
526+ }
527+
528+ TEST_F (MediaDataEncoderTest, H264EncodeBatchAVCCRecord) {
529+ H264EncodeBatchTest (Usage::Record, AsVariant (kH264SpecificAVCC ), mData );
530+ }
531+
532+ TEST_F (MediaDataEncoderTest, H264EncodeBatchAVCCRealtime) {
533+ H264EncodeBatchTest (Usage::Realtime, AsVariant (kH264SpecificAVCC ), mData );
534+ }
535+
536+ TEST_F (MediaDataEncoderTest, H264EncodeBatch4KAnnexBRecord) {
537+ SKIP_IF_ANDROID_SW (); // Android SW can't encode 4K.
538+ H264EncodeBatchTest (Usage::Record, AsVariant (kH264SpecificAnnexB ), mData4K );
539+ }
540+
541+ TEST_F (MediaDataEncoderTest, H264EncodeBatch4KAnnexBRealtime) {
542+ SKIP_IF_ANDROID_SW (); // Android SW can't encode 4K.
543+ H264EncodeBatchTest (Usage::Realtime, AsVariant (kH264SpecificAnnexB ), mData4K );
544+ }
545+
546+ TEST_F (MediaDataEncoderTest, H264EncodeBatch4KAVCCRecord) {
547+ SKIP_IF_ANDROID_SW (); // Android SW can't encode 4K.
548+ H264EncodeBatchTest (Usage::Record, AsVariant (kH264SpecificAVCC ), mData4K );
549+ }
550+
551+ TEST_F (MediaDataEncoderTest, H264EncodeBatch4KAVCCRealtime) {
552+ SKIP_IF_ANDROID_SW (); // Android SW can't encode 4K.
553+ H264EncodeBatchTest (Usage::Realtime, AsVariant (kH264SpecificAVCC ), mData4K );
554+ }
555+
448556#if !defined(ANDROID)
449557static void H264EncodeAfterDrainTest (
450558 Usage aUsage, const EncoderConfig::CodecSpecific& aSpecific,
0 commit comments