Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  TestMediaDataEncoder.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "gtest/gtest.h"

#include "AnnexB.h"
#include "BufferReader.h"
#include "H264.h"
#include "ImageContainer.h"
#include "mozilla/AbstractThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/media/MediaUtils.h"  // For media::Await
#include "PEMFactory.h"
#include "TimeUnits.h"
#include "VideoUtils.h"
#include "VPXDecoder.h"
#include <algorithm>

#define RUN_IF_SUPPORTED(codecType, test)   \
  do {                                      \
    RefPtr<PEMFactory> f(new PEMFactory()); \
    if (f->SupportsCodec(codecType)) {      \
      test();                               \
    }                                       \
  } while (0)

#define BLOCK_SIZE 64
#define NUM_FRAMES 150UL
#define FRAME_RATE 30
#define FRAME_DURATION (1000000 / FRAME_RATE)
#define BIT_RATE (1000 * 1000)  // 1Mbps
#define BIT_RATE_MODE BitrateMode::Variable
#define KEYFRAME_INTERVAL FRAME_RATE  // 1 keyframe per second

using namespace mozilla;

static gfx::IntSize kImageSize(640, 480);
static gfx::IntSize kImageSize4K(3840, 2160);
// Set codec to avc1.42001E - Base profile, constraint 0, level 30.
MOZ_RUNINIT const H264Specific kH264SpecificAnnexB(H264_PROFILE_BASE,
                                                   H264_LEVEL::H264_LEVEL_3,
                                                   H264BitStreamFormat::ANNEXB);
MOZ_RUNINIT const H264Specific kH264SpecificAVCC(H264_PROFILE_BASE,
                                                 H264_LEVEL::H264_LEVEL_3,
                                                 H264BitStreamFormat::AVC);

class MediaDataEncoderTest : public testing::Test {
 protected:
  void SetUp() override {
    Preferences::SetBool("media.ffmpeg.encoder.enabled"true);
    Preferences::SetInt("logging.FFmpegVideo", 5);
    mData.Init(kImageSize);
    mData4K.Init(kImageSize4K);
  }

  void TearDown() override {
    mData.Deinit();
    mData4K.Deinit();
  }

 public:
  struct FrameSource final {
    layers::PlanarYCbCrData mYUV;
    UniquePtr<uint8_t[]> mBuffer;
    RefPtr<layers::BufferRecycleBin> mRecycleBin;
    int16_t mColorStep = 4;

    void Init(const gfx::IntSize& aSize) {
      mYUV.mPictureRect = gfx::IntRect(0, 0, aSize.width, aSize.height);
      mYUV.mYStride = aSize.width;
      mYUV.mCbCrStride = (aSize.width + 1) / 2;
      mYUV.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
      auto ySize = mYUV.YDataSize();
      auto cbcrSize = mYUV.CbCrDataSize();
      size_t bufferSize =
          mYUV.mYStride * ySize.height + 2 * mYUV.mCbCrStride * cbcrSize.height;
      mBuffer = MakeUnique<uint8_t[]>(bufferSize);
      std::fill_n(mBuffer.get(), bufferSize, 0x7F);
      mYUV.mYChannel = mBuffer.get();
      mYUV.mCbChannel = mYUV.mYChannel + mYUV.mYStride * ySize.height;
      mYUV.mCrChannel = mYUV.mCbChannel + mYUV.mCbCrStride * cbcrSize.height;
      mYUV.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
      mRecycleBin = new layers::BufferRecycleBin();
    }

    void Deinit() {
      mBuffer.reset();
      mRecycleBin = nullptr;
    }

    already_AddRefed<MediaData> GetFrame(const size_t aIndex) {
      Draw(aIndex);
      RefPtr<layers::PlanarYCbCrImage> img =
          new layers::RecyclingPlanarYCbCrImage(mRecycleBin);
      img->CopyData(mYUV);
      RefPtr<MediaData> frame = VideoData::CreateFromImage(
          kImageSize, 0,
          // The precise time unit should be media::TimeUnit(1, FRAME_RATE)
          // instead of media::TimeUnit(FRAME_DURATION, USECS_PER_S)
          // (FRAME_DURATION microseconds), but this setting forces us to take
          // care some potential rounding issue, e.g., when converting to a time
          // unit based in FRAME_RATE by TimeUnit::ToTicksAtRate(FRAME_RATE),
          // the time unit would be calculated from 999990 / 1000000, which
          // could be zero.
          media::TimeUnit::FromMicroseconds(AssertedCast<int64_t>(aIndex) *
                                            FRAME_DURATION),
          media::TimeUnit::FromMicroseconds(FRAME_DURATION), img,
          (aIndex & 0xF) == 0,
          media::TimeUnit::FromMicroseconds(AssertedCast<int64_t>(aIndex) *
                                            FRAME_DURATION));
      return frame.forget();
    }

    void DrawChessboard(uint8_t* aAddr, const size_t aWidth,
                        const size_t aHeight, const size_t aOffset) {
      uint8_t pixels[2][BLOCK_SIZE];
      size_t x = aOffset % BLOCK_SIZE;
      if ((aOffset / BLOCK_SIZE) & 1) {
        x = BLOCK_SIZE - x;
      }
      for (size_t i = 0; i < x; i++) {
        pixels[0][i] = 0x00;
        pixels[1][i] = 0xFF;
      }
      for (size_t i = x; i < BLOCK_SIZE; i++) {
        pixels[0][i] = 0xFF;
        pixels[1][i] = 0x00;
      }

      uint8_t* p = aAddr;
      for (size_t row = 0; row < aHeight; row++) {
        for (size_t col = 0; col < aWidth; col += BLOCK_SIZE) {
          memcpy(p, pixels[((row / BLOCK_SIZE) + (col / BLOCK_SIZE)) % 2],
                 BLOCK_SIZE);
          p += BLOCK_SIZE;
        }
      }
    }

    void Draw(const size_t aIndex) {
      auto ySize = mYUV.YDataSize();
      DrawChessboard(mYUV.mYChannel, ySize.width, ySize.height, aIndex << 1);
      int16_t color = AssertedCast<int16_t>(mYUV.mCbChannel[0] + mColorStep);
      if (color > 255 || color < 0) {
        mColorStep = AssertedCast<int16_t>(-mColorStep);
        color = AssertedCast<int16_t>(mYUV.mCbChannel[0] + mColorStep);
      }

      size_t size = (mYUV.mCrChannel - mYUV.mCbChannel);

      std::fill_n(mYUV.mCbChannel, size, static_cast<uint8_t>(color));
      std::fill_n(mYUV.mCrChannel, size, 0xFF - static_cast<uint8_t>(color));
    }
  };

 public:
  FrameSource mData;
  FrameSource mData4K;
};

template <typename T>
already_AddRefed<MediaDataEncoder> CreateVideoEncoder(
    CodecType aCodec, Usage aUsage, dom::ImageBitmapFormat aPixelFormat,
    gfx::IntSize aSize, ScalabilityMode aScalabilityMode,
    const Maybe<T>& aSpecific) {
  RefPtr<PEMFactory> f(new PEMFactory());

  if (!f->SupportsCodec(aCodec)) {
    return nullptr;
  }

  const RefPtr<TaskQueue> taskQueue(
      TaskQueue::Create(GetMediaThreadPool(MediaThreadType::PLATFORM_ENCODER),
                        "TestMediaDataEncoder"));

  RefPtr<MediaDataEncoder> e;
  const HardwarePreference pref = HardwarePreference::None;
  e = f->CreateEncoder(
      EncoderConfig(aCodec, aSize, aUsage, aPixelFormat, aPixelFormat,
                    FRAME_RATE /* FPS */,
                    KEYFRAME_INTERVAL /* keyframe interval */,
                    BIT_RATE /* bitrate */, 0, 0, BIT_RATE_MODE, pref,
                    aScalabilityMode, aSpecific),
      taskQueue);

  return e.forget();
}

static already_AddRefed<MediaDataEncoder> CreateH264Encoder(
    Usage aUsage = Usage::Realtime,
    dom::ImageBitmapFormat aPixelFormat = dom::ImageBitmapFormat::YUV420P,
    gfx::IntSize aSize = kImageSize,
    ScalabilityMode aScalabilityMode = ScalabilityMode::None,
    const Maybe<H264Specific>& aSpecific = Some(kH264SpecificAnnexB)) {
  return CreateVideoEncoder(CodecType::H264, aUsage, aPixelFormat, aSize,
                            aScalabilityMode, aSpecific);
}

void WaitForShutdown(const RefPtr<MediaDataEncoder>& aEncoder) {
  MOZ_RELEASE_ASSERT(aEncoder);

  Maybe<bool> result;
  // media::Await() supports exclusive promises only, but ShutdownPromise is
  // not.
  aEncoder->Shutdown()->Then(
      AbstractThread::MainThread(), __func__,
      [&result](bool rv) {
        EXPECT_TRUE(rv);
        result = Some(true);
      },
      []() { FAIL() << "Shutdown should never be rejected"; });
  SpinEventLoopUntil("TestMediaDataEncoder.cpp:WaitForShutdown"_ns,
                     [&result]() { return result; });
}

TEST_F(MediaDataEncoderTest, H264Create) {
  RUN_IF_SUPPORTED(CodecType::H264, []() {
    RefPtr<MediaDataEncoder> e = CreateH264Encoder();
    EXPECT_TRUE(e);
    WaitForShutdown(e);
  });
}

static bool EnsureInit(const RefPtr<MediaDataEncoder>& aEncoder) {
  if (!aEncoder) {
    return false;
  }

  bool succeeded;
  media::Await(
      GetMediaThreadPool(MediaThreadType::SUPERVISOR), aEncoder->Init(),
      [&succeeded](bool) { succeeded = true; },
      [&succeeded](const MediaResult& r) { succeeded = false; });
  return succeeded;
}

TEST_F(MediaDataEncoderTest, H264Inits) {
  RUN_IF_SUPPORTED(CodecType::H264, []() {
    // w/o codec specific: should fail for h264.
    RefPtr<MediaDataEncoder> e =
        CreateH264Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                          kImageSize, ScalabilityMode::None, Nothing());
    EXPECT_FALSE(e);

    // w/ codec specific
    e = CreateH264Encoder();
    EXPECT_TRUE(EnsureInit(e));
    WaitForShutdown(e);
  });
}

static MediaDataEncoder::EncodedData Encode(
    const RefPtr<MediaDataEncoder>& aEncoder, const size_t aNumFrames,
    MediaDataEncoderTest::FrameSource& aSource) {
  MediaDataEncoder::EncodedData output;
  bool succeeded;
  for (size_t i = 0; i < aNumFrames; i++) {
    RefPtr<MediaData> frame = aSource.GetFrame(i);
    media::Await(
        GetMediaThreadPool(MediaThreadType::SUPERVISOR),
        aEncoder->Encode(frame),
        [&output, &succeeded](MediaDataEncoder::EncodedData encoded) {
          output.AppendElements(std::move(encoded));
          succeeded = true;
        },
        [&succeeded](const MediaResult& r) { succeeded = false; });
    EXPECT_TRUE(succeeded);
    if (!succeeded) {
      return output;
    }
  }

  size_t pending = 0;
  do {
    media::Await(
        GetMediaThreadPool(MediaThreadType::SUPERVISOR), aEncoder->Drain(),
        [&pending, &output, &succeeded](MediaDataEncoder::EncodedData encoded) {
          pending = encoded.Length();
          output.AppendElements(std::move(encoded));
          succeeded = true;
        },
        [&succeeded](const MediaResult& r) { succeeded = false; });
    EXPECT_TRUE(succeeded);
    if (!succeeded) {
      return output;
    }
  } while (pending > 0);

  return output;
}

Result<uint8_t, nsresult> GetNALUSize(const mozilla::MediaRawData* aSample) {
  return AVCCConfig::Parse(aSample).map(
      [](AVCCConfig config) { return config.NALUSize(); });
}

Result<Ok, nsresult> IsValidAVCC(const mozilla::MediaRawData* aSample,
                                 uint8_t aNALUSize) {
  BufferReader reader(aSample->Data(), aSample->Size());
  while (reader.Remaining() >= aNALUSize) {
    uint32_t nalLen;
    switch (aNALUSize) {
      case 1:
        MOZ_TRY_VAR(nalLen, reader.ReadU8());
        break;
      case 2:
        MOZ_TRY_VAR(nalLen, reader.ReadU16());
        break;
      case 3:
        MOZ_TRY_VAR(nalLen, reader.ReadU24());
        break;
      case 4:
        MOZ_TRY_VAR(nalLen, reader.ReadU32());
        break;
      default:
        return Err(NS_ERROR_INVALID_ARG);
    }
    const uint8_t* p = reader.Read(nalLen);
    if (!p) {
      return Err(NS_ERROR_ILLEGAL_VALUE);
    }
  }
  return Ok();
}

TEST_F(MediaDataEncoderTest, H264EncodesAnnexBRecord) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in AnnexB format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Record, dom::ImageBitmapFormat::YUV420P, kImageSize,
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAnnexB(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in AnnexB format.
    e = CreateH264Encoder(Usage::Record, dom::ImageBitmapFormat::YUV420P,
                          kImageSize, ScalabilityMode::None,
                          Some(kH264SpecificAnnexB));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      EXPECT_TRUE(AnnexB::IsAnnexB(frame));
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264EncodesAnnexBRealtime) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in AnnexB format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, kImageSize,
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAnnexB(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in AnnexB format.
    e = CreateH264Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                          kImageSize, ScalabilityMode::None,
                          Some(kH264SpecificAnnexB));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      EXPECT_TRUE(AnnexB::IsAnnexB(frame));
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264EncodesAVCCRecord) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in avcC format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Record, dom::ImageBitmapFormat::YUV420P, kImageSize,
        ScalabilityMode::None, Some(kH264SpecificAVCC));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in avcC format.
    e = CreateH264Encoder(Usage::Record, dom::ImageBitmapFormat::YUV420P,
                          kImageSize, ScalabilityMode::None,
                          Some(kH264SpecificAVCC));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    uint8_t naluSize = GetNALUSize(output[0]).unwrapOr(0);
    EXPECT_GT(naluSize, 0);
    EXPECT_LE(naluSize, 4);
    for (auto frame : output) {
      if (frame->mExtraData && !frame->mExtraData->IsEmpty()) {
        naluSize = GetNALUSize(frame).unwrapOr(0);
        EXPECT_GT(naluSize, 0);
        EXPECT_LE(naluSize, 4);
      }
      EXPECT_TRUE(IsValidAVCC(frame, naluSize).isOk());
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264EncodesAVCCRealtime) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in avcC format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, kImageSize,
        ScalabilityMode::None, Some(kH264SpecificAVCC));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in avcC format.
    e = CreateH264Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                          kImageSize, ScalabilityMode::None,
                          Some(kH264SpecificAVCC));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    uint8_t naluSize = GetNALUSize(output[0]).unwrapOr(0);
    EXPECT_GT(naluSize, 0);
    EXPECT_LE(naluSize, 4);
    for (auto frame : output) {
      if (frame->mExtraData && !frame->mExtraData->IsEmpty()) {
        naluSize = GetNALUSize(frame).unwrapOr(0);
        EXPECT_GT(naluSize, 0);
        EXPECT_LE(naluSize, 4);
      }
      EXPECT_TRUE(IsValidAVCC(frame, naluSize).isOk());
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264Encodes4KAnnexBRecord) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in AnnexB format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Record, dom::ImageBitmapFormat::YUV420P, kImageSize4K,
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData4K);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAnnexB(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in AnnexB format.
    e = CreateH264Encoder(Usage::Record, dom::ImageBitmapFormat::YUV420P,
                          kImageSize4K, ScalabilityMode::None,
                          Some(kH264SpecificAnnexB));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData4K);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      EXPECT_TRUE(AnnexB::IsAnnexB(frame));
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264Encodes4KAnnexBRealtime) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in AnnexB format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, kImageSize4K,
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData4K);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAnnexB(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in AnnexB format.
    e = CreateH264Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                          kImageSize4K, ScalabilityMode::None,
                          Some(kH264SpecificAnnexB));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData4K);
    EXPECT_LE(output.Length(),
              NUM_FRAMES);  // Frames can be dropped in realtime usage.
    for (auto frame : output) {
      EXPECT_TRUE(AnnexB::IsAnnexB(frame));
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264Encodes4KAVCCRecord) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in avcC format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Record, dom::ImageBitmapFormat::YUV420P, kImageSize4K,
        ScalabilityMode::None, Some(kH264SpecificAVCC));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData4K);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in avcC format.
    e = CreateH264Encoder(Usage::Record, dom::ImageBitmapFormat::YUV420P,
                          kImageSize4K, ScalabilityMode::None,
                          Some(kH264SpecificAVCC));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData4K);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    uint8_t naluSize = GetNALUSize(output[0]).unwrapOr(0);
    EXPECT_GT(naluSize, 0);
    EXPECT_LE(naluSize, 4);
    for (auto frame : output) {
      if (frame->mExtraData && !frame->mExtraData->IsEmpty()) {
        naluSize = GetNALUSize(frame).unwrapOr(0);
        EXPECT_GT(naluSize, 0);
        EXPECT_LE(naluSize, 4);
      }
      EXPECT_TRUE(IsValidAVCC(frame, naluSize).isOk());
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264Encodes4KAVCCRealtime) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encode one frame and output in avcC format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, kImageSize4K,
        ScalabilityMode::None, Some(kH264SpecificAVCC));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData4K);
    EXPECT_EQ(output.Length(), 1UL);
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    WaitForShutdown(e);

    // Encode multiple frames and output in avcC format.
    e = CreateH264Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                          kImageSize4K, ScalabilityMode::None,
                          Some(kH264SpecificAVCC));
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData4K);
    EXPECT_LE(output.Length(),
              NUM_FRAMES);  // Frames can be dropped in realtime usage.
    EXPECT_TRUE(AnnexB::IsAVCC(output[0]));
    uint8_t naluSize = GetNALUSize(output[0]).unwrapOr(0);
    EXPECT_GT(naluSize, 0);
    EXPECT_LE(naluSize, 4);
    for (auto frame : output) {
      if (frame->mExtraData && !frame->mExtraData->IsEmpty()) {
        naluSize = GetNALUSize(frame).unwrapOr(0);
        EXPECT_GT(naluSize, 0);
        EXPECT_LE(naluSize, 4);
      }
      EXPECT_TRUE(IsValidAVCC(frame, naluSize).isOk());
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, H264Duration) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    RefPtr<MediaDataEncoder> e = CreateH264Encoder();
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (const auto& frame : output) {
      EXPECT_GT(frame->mDuration, media::TimeUnit::Zero());
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, InvalidSize) {
  RUN_IF_SUPPORTED(CodecType::H264, []() {
    RefPtr<MediaDataEncoder> e0x0 = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, {0, 0},
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EXPECT_EQ(e0x0, nullptr);

    RefPtr<MediaDataEncoder> e0x1 = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, {0, 1},
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EXPECT_EQ(e0x1, nullptr);

    RefPtr<MediaDataEncoder> e1x0 = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, {1, 0},
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EXPECT_EQ(e1x0, nullptr);
  });
}

#ifdef MOZ_WIDGET_ANDROID
TEST_F(MediaDataEncoderTest, AndroidNotSupportedSize) {
  RUN_IF_SUPPORTED(CodecType::H264, []() {
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Realtime, dom::ImageBitmapFormat::YUV420P, {1, 1},
        ScalabilityMode::None, Some(kH264SpecificAnnexB));
    EXPECT_NE(e, nullptr);
    EXPECT_FALSE(EnsureInit(e));
  });
}
#endif

#if defined(XP_LINUX) && !defined(ANDROID)
TEST_F(MediaDataEncoderTest, H264AVCC) {
  RUN_IF_SUPPORTED(CodecType::H264, [this]() {
    // Encod frames in avcC format.
    RefPtr<MediaDataEncoder> e = CreateH264Encoder(
        Usage::Record, dom::ImageBitmapFormat::YUV420P, kImageSize,
        ScalabilityMode::None, Some(kH264SpecificAVCC));
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      EXPECT_FALSE(AnnexB::IsAnnexB(frame));
      if (frame->mKeyframe) {
        AnnexB::IsAVCC(frame);
        AVCCConfig config = AVCCConfig::Parse(frame).unwrap();
        EXPECT_EQ(config.mAVCProfileIndication,
                  static_cast<decltype(config.mAVCProfileIndication)>(
                      kH264SpecificAVCC.mProfile));
        EXPECT_EQ(config.mAVCLevelIndication,
                  static_cast<decltype(config.mAVCLevelIndication)>(
                      kH264SpecificAVCC.mLevel));
      }
    }
    WaitForShutdown(e);
  });
}
#endif

static already_AddRefed<MediaDataEncoder> CreateVP8Encoder(
    Usage aUsage = Usage::Realtime,
    dom::ImageBitmapFormat aPixelFormat = dom::ImageBitmapFormat::YUV420P,
    gfx::IntSize aSize = kImageSize,
    ScalabilityMode aScalabilityMode = ScalabilityMode::None,
    const Maybe<VP8Specific>& aSpecific = Some(VP8Specific())) {
  return CreateVideoEncoder(CodecType::VP8, aUsage, aPixelFormat, aSize,
                            aScalabilityMode, aSpecific);
}

static already_AddRefed<MediaDataEncoder> CreateVP9Encoder(
    Usage aUsage = Usage::Realtime,
    dom::ImageBitmapFormat aPixelFormat = dom::ImageBitmapFormat::YUV420P,
    gfx::IntSize aSize = kImageSize,
    ScalabilityMode aScalabilityMode = ScalabilityMode::None,
    const Maybe<VP9Specific>& aSpecific = Some(VP9Specific())) {
  return CreateVideoEncoder(CodecType::VP9, aUsage, aPixelFormat, aSize,
                            aScalabilityMode, aSpecific);
}

TEST_F(MediaDataEncoderTest, VP8Create) {
  RUN_IF_SUPPORTED(CodecType::VP8, []() {
    RefPtr<MediaDataEncoder> e = CreateVP8Encoder();
    EXPECT_TRUE(e);
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP8Inits) {
  RUN_IF_SUPPORTED(CodecType::VP8, []() {
    // w/o codec specific.
    RefPtr<MediaDataEncoder> e =
        CreateVP8Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::None, Nothing());
    EXPECT_TRUE(EnsureInit(e));
    WaitForShutdown(e);

    // w/ codec specific
    e = CreateVP8Encoder();
    EXPECT_TRUE(EnsureInit(e));
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP8Encodes) {
  RUN_IF_SUPPORTED(CodecType::VP8, [this]() {
    // Encode one VPX frame.
    RefPtr<MediaDataEncoder> e = CreateVP8Encoder();
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    VPXDecoder::VPXStreamInfo info;
    EXPECT_TRUE(
        VPXDecoder::GetStreamInfo(*output[0], info, VPXDecoder::Codec::VP8));
    EXPECT_EQ(info.mKeyFrame, output[0]->mKeyframe);
    if (info.mKeyFrame) {
      EXPECT_EQ(info.mImage, kImageSize);
    }
    WaitForShutdown(e);

    // Encode multiple VPX frames.
    e = CreateVP8Encoder();
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP8));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP8Duration) {
  RUN_IF_SUPPORTED(CodecType::VP8, [this]() {
    RefPtr<MediaDataEncoder> e = CreateVP8Encoder();
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (const auto& frame : output) {
      EXPECT_GT(frame->mDuration, media::TimeUnit::Zero());
    }
    WaitForShutdown(e);
  });
}

#if defined(XP_LINUX) && !defined(ANDROID)
TEST_F(MediaDataEncoderTest, VP8EncodeAfterDrain) {
  RUN_IF_SUPPORTED(CodecType::VP8, [this]() {
    RefPtr<MediaDataEncoder> e = CreateVP8Encoder();
    EnsureInit(e);

    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP8));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }
    output.Clear();

    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP8));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }

    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP8EncodeWithScalabilityModeL1T2) {
  RUN_IF_SUPPORTED(CodecType::VP8, [this]() {
    VP8Specific specific(VPXComplexity::Normal, /* mComplexity */
                         true,                  /* mResilience */
                         2,                     /* mNumTemporalLayers */
                         true,                  /* mDenoising */
                         false,                 /* mAutoResize */
                         false                  /* mFrameDropping */
    );
    RefPtr<MediaDataEncoder> e =
        CreateVP8Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::L1T2, Some(specific));
    EnsureInit(e);

    const nsTArray<uint8_t> pattern({0, 1});
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    int temporal_idx = 0;
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (size_t i = 0; i < output.Length(); ++i) {
      const RefPtr<MediaRawData> frame = output[i];
      if (frame->mKeyframe) {
        temporal_idx = 0;
      }
      EXPECT_TRUE(frame->mTemporalLayerId);
      size_t idx = temporal_idx++ % pattern.Length();
      EXPECT_EQ(frame->mTemporalLayerId.value(), pattern[idx]);
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP8EncodeWithScalabilityModeL1T3) {
  RUN_IF_SUPPORTED(CodecType::VP8, [this]() {
    VP8Specific specific(VPXComplexity::Normal, /* mComplexity */
                         true,                  /* mResilience */
                         3,                     /* mNumTemporalLayers */
                         true,                  /* mDenoising */
                         false,                 /* mAutoResize */
                         false                  /* mFrameDropping */
    );
    RefPtr<MediaDataEncoder> e =
        CreateVP8Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::L1T3, Some(specific));
    EnsureInit(e);

    const nsTArray<uint8_t> pattern({0, 2, 1, 2});
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    int temporal_idx = 0;
    for (size_t i = 0; i < output.Length(); ++i) {
      const RefPtr<MediaRawData> frame = output[i];
      if (frame->mKeyframe) {
        temporal_idx = 0;
      }
      EXPECT_TRUE(frame->mTemporalLayerId);
      size_t idx = temporal_idx++ % pattern.Length();
      EXPECT_EQ(frame->mTemporalLayerId.value(), pattern[idx]);
    }
    WaitForShutdown(e);
  });
}
#endif

TEST_F(MediaDataEncoderTest, VP9Create) {
  RUN_IF_SUPPORTED(CodecType::VP9, []() {
    RefPtr<MediaDataEncoder> e = CreateVP9Encoder();
    EXPECT_TRUE(e);
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP9Inits) {
  RUN_IF_SUPPORTED(CodecType::VP9, []() {
    // w/o codec specific.
    RefPtr<MediaDataEncoder> e =
        CreateVP9Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::None, Nothing());
    EXPECT_TRUE(EnsureInit(e));
    WaitForShutdown(e);

    // w/ codec specific
    e = CreateVP9Encoder();
    EXPECT_TRUE(EnsureInit(e));
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP9Encodes) {
  RUN_IF_SUPPORTED(CodecType::VP9, [this]() {
    RefPtr<MediaDataEncoder> e = CreateVP9Encoder();
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, 1UL, mData);
    EXPECT_EQ(output.Length(), 1UL);
    VPXDecoder::VPXStreamInfo info;
    EXPECT_TRUE(
        VPXDecoder::GetStreamInfo(*output[0], info, VPXDecoder::Codec::VP9));
    EXPECT_EQ(info.mKeyFrame, output[0]->mKeyframe);
    if (info.mKeyFrame) {
      EXPECT_EQ(info.mImage, kImageSize);
    }
    WaitForShutdown(e);

    e = CreateVP9Encoder();
    EnsureInit(e);
    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP9));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP9Duration) {
  RUN_IF_SUPPORTED(CodecType::VP9, [this]() {
    RefPtr<MediaDataEncoder> e = CreateVP9Encoder();
    EnsureInit(e);
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (const auto& frame : output) {
      EXPECT_GT(frame->mDuration, media::TimeUnit::Zero());
    }
    WaitForShutdown(e);
  });
}

#if defined(XP_LINUX) && !defined(ANDROID)
TEST_F(MediaDataEncoderTest, VP9EncodeAfterDrain) {
  RUN_IF_SUPPORTED(CodecType::VP9, [this]() {
    RefPtr<MediaDataEncoder> e = CreateVP9Encoder();
    EnsureInit(e);

    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP9));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }
    output.Clear();

    output = Encode(e, NUM_FRAMES, mData);
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (auto frame : output) {
      VPXDecoder::VPXStreamInfo info;
      EXPECT_TRUE(
          VPXDecoder::GetStreamInfo(*frame, info, VPXDecoder::Codec::VP9));
      EXPECT_EQ(info.mKeyFrame, frame->mKeyframe);
      if (info.mKeyFrame) {
        EXPECT_EQ(info.mImage, kImageSize);
      }
    }

    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP9EncodeWithScalabilityModeL1T2) {
  RUN_IF_SUPPORTED(CodecType::VP9, [this]() {
    VP9Specific specific(VPXComplexity::Normal, /* mComplexity */
                         true,                  /* mResilience */
                         2,                     /* mNumTemporalLayers */
                         true,                  /* mDenoising */
                         false,                 /* mAutoResize */
                         false,                 /* mFrameDropping */
                         true,                  /* mAdaptiveQp */
                         1,                     /* mNumSpatialLayers */
                         false                  /* mFlexible */
    );

    RefPtr<MediaDataEncoder> e =
        CreateVP9Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::L1T2, Some(specific));
    EnsureInit(e);

    const nsTArray<uint8_t> pattern({0, 1});
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    int temporal_idx = 0;
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (size_t i = 0; i < output.Length(); ++i) {
      const RefPtr<MediaRawData> frame = output[i];
      if (frame->mKeyframe) {
        temporal_idx = 0;
      }
      EXPECT_TRUE(frame->mTemporalLayerId);
      size_t idx = temporal_idx++ % pattern.Length();
      EXPECT_EQ(frame->mTemporalLayerId.value(), pattern[idx]);
    }
    WaitForShutdown(e);
  });
}

TEST_F(MediaDataEncoderTest, VP9EncodeWithScalabilityModeL1T3) {
  RUN_IF_SUPPORTED(CodecType::VP9, [this]() {
    VP9Specific specific(VPXComplexity::Normal, /* mComplexity */
                         true,                  /* mResilience */
                         3,                     /* mNumTemporalLayers */
                         true,                  /* mDenoising */
                         false,                 /* mAutoResize */
                         false,                 /* mFrameDropping */
                         true,                  /* mAdaptiveQp */
                         1,                     /* mNumSpatialLayers */
                         false                  /* mFlexible */
    );

    RefPtr<MediaDataEncoder> e =
        CreateVP9Encoder(Usage::Realtime, dom::ImageBitmapFormat::YUV420P,
                         kImageSize, ScalabilityMode::L1T3, Some(specific));
    EnsureInit(e);

    const nsTArray<uint8_t> pattern({0, 2, 1, 2});
    MediaDataEncoder::EncodedData output = Encode(e, NUM_FRAMES, mData);
    int temporal_idx = 0;
    EXPECT_EQ(output.Length(), NUM_FRAMES);
    for (size_t i = 0; i < output.Length(); ++i) {
      const RefPtr<MediaRawData> frame = output[i];
      if (frame->mKeyframe) {
        temporal_idx = 0;
      }
      EXPECT_TRUE(frame->mTemporalLayerId);
      size_t idx = temporal_idx++ % pattern.Length();
      EXPECT_EQ(frame->mTemporalLayerId.value(), pattern[idx]);
    }
    WaitForShutdown(e);
  });
}
#endif

Messung V0.5
C=92 H=90 G=90

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge