tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GMPVideoDecoder.cpp (16419B)


      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 "GMPVideoDecoder.h"
      8 
      9 #include "AnnexB.h"
     10 #include "GMPDecoderModule.h"
     11 #include "GMPLog.h"
     12 #include "GMPUtils.h"
     13 #include "GMPVideoHost.h"
     14 #include "H264.h"
     15 #include "MP4Decoder.h"
     16 #include "MediaData.h"
     17 #include "VPXDecoder.h"
     18 #include "VideoUtils.h"
     19 #include "mozilla/EndianUtils.h"
     20 #include "mozilla/StaticPrefs_media.h"
     21 #include "nsServiceManagerUtils.h"
     22 #include "prsystem.h"
     23 
     24 namespace mozilla {
     25 
     26 GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
     27    : mConfig(aParams.VideoConfig()),
     28      mImageContainer(aParams.mImageContainer),
     29      mCrashHelper(aParams.mCrashHelper),
     30      mKnowsCompositor(aParams.mKnowsCompositor),
     31      mTrackingId(aParams.mTrackingId) {}
     32 
     33 nsCString GMPVideoDecoder::GetCodecName() const {
     34  if (MP4Decoder::IsH264(mConfig.mMimeType)) {
     35    return "h264"_ns;
     36  } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
     37    return "vp8"_ns;
     38  } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
     39    return "vp9"_ns;
     40  }
     41  return "unknown"_ns;
     42 }
     43 
     44 void GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame) {
     45  GMP_LOG_DEBUG("GMPVideoDecoder::Decoded");
     46 
     47  GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
     48  MOZ_ASSERT(IsOnGMPThread());
     49 
     50  VideoData::YCbCrBuffer b;
     51  for (int i = 0; i < kGMPNumOfPlanes; ++i) {
     52    b.mPlanes[i].mData = decodedFrame->Buffer(GMPPlaneType(i));
     53    b.mPlanes[i].mStride = decodedFrame->Stride(GMPPlaneType(i));
     54    if (i == kGMPYPlane) {
     55      b.mPlanes[i].mWidth = decodedFrame->Width();
     56      b.mPlanes[i].mHeight = decodedFrame->Height();
     57    } else {
     58      b.mPlanes[i].mWidth = (decodedFrame->Width() + 1) / 2;
     59      b.mPlanes[i].mHeight = (decodedFrame->Height() + 1) / 2;
     60    }
     61    b.mPlanes[i].mSkip = 0;
     62  }
     63 
     64  b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
     65  b.mYUVColorSpace =
     66      DefaultColorSpace({decodedFrame->Width(), decodedFrame->Height()});
     67 
     68  UniquePtr<SampleMetadata> sampleData;
     69  if (auto entryHandle = mSamples.Lookup(decodedFrame->Timestamp())) {
     70    sampleData = std::move(entryHandle.Data());
     71    entryHandle.Remove();
     72  } else {
     73    GMP_LOG_DEBUG(
     74        "GMPVideoDecoder::Decoded(this=%p) missing sample metadata for "
     75        "time %" PRIu64,
     76        this, decodedFrame->Timestamp());
     77    if (mSamples.IsEmpty()) {
     78      // If we have no remaining samples in the table, then we have processed
     79      // all outstanding decode requests.
     80      ProcessReorderQueue(mDecodePromise, __func__);
     81    }
     82    return;
     83  }
     84 
     85  MOZ_ASSERT(sampleData);
     86 
     87  gfx::IntRect pictureRegion(0, 0, decodedFrame->Width(),
     88                             decodedFrame->Height());
     89  Result<already_AddRefed<VideoData>, MediaResult> r =
     90      VideoData::CreateAndCopyData(
     91          mConfig, mImageContainer, sampleData->mOffset,
     92          media::TimeUnit::FromMicroseconds(decodedFrame->UpdatedTimestamp()),
     93          media::TimeUnit::FromMicroseconds(decodedFrame->Duration()), b,
     94          sampleData->mKeyframe, media::TimeUnit::FromMicroseconds(-1),
     95          pictureRegion, mKnowsCompositor);
     96  if (r.isErr()) {
     97    mReorderQueue.Clear();
     98    mUnorderedData.Clear();
     99    mSamples.Clear();
    100    mDecodePromise.RejectIfExists(r.unwrapErr(), __func__);
    101    return;
    102  }
    103 
    104  RefPtr<VideoData> v = r.unwrap();
    105  MOZ_ASSERT(v);
    106  mPerformanceRecorder.Record(
    107      static_cast<int64_t>(decodedFrame->Timestamp()),
    108      [&](DecodeStage& aStage) {
    109        aStage.SetImageFormat(DecodeStage::YUV420P);
    110        aStage.SetResolution(decodedFrame->Width(), decodedFrame->Height());
    111        aStage.SetYUVColorSpace(b.mYUVColorSpace);
    112        aStage.SetColorDepth(b.mColorDepth);
    113        aStage.SetColorRange(b.mColorRange);
    114        aStage.SetStartTimeAndEndTime(v->mTime.ToMicroseconds(),
    115                                      v->GetEndTime().ToMicroseconds());
    116      });
    117 
    118  if (mReorderFrames) {
    119    mReorderQueue.Push(std::move(v));
    120  } else {
    121    mUnorderedData.AppendElement(std::move(v));
    122  }
    123 
    124  if (mSamples.IsEmpty()) {
    125    // If we have no remaining samples in the table, then we have processed
    126    // all outstanding decode requests.
    127    ProcessReorderQueue(mDecodePromise, __func__);
    128  }
    129 }
    130 
    131 void GMPVideoDecoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId) {
    132  GMP_LOG_DEBUG("GMPVideoDecoder::ReceivedDecodedReferenceFrame");
    133  MOZ_ASSERT(IsOnGMPThread());
    134 }
    135 
    136 void GMPVideoDecoder::ReceivedDecodedFrame(const uint64_t aPictureId) {
    137  GMP_LOG_DEBUG("GMPVideoDecoder::ReceivedDecodedFrame");
    138  MOZ_ASSERT(IsOnGMPThread());
    139 }
    140 
    141 void GMPVideoDecoder::InputDataExhausted() {
    142  GMP_LOG_DEBUG("GMPVideoDecoder::InputDataExhausted");
    143  MOZ_ASSERT(IsOnGMPThread());
    144  mSamples.Clear();
    145  ProcessReorderQueue(mDecodePromise, __func__);
    146 }
    147 
    148 void GMPVideoDecoder::DrainComplete() {
    149  GMP_LOG_DEBUG("GMPVideoDecoder::DrainComplete");
    150  MOZ_ASSERT(IsOnGMPThread());
    151  mSamples.Clear();
    152 
    153  if (mDrainPromise.IsEmpty()) {
    154    return;
    155  }
    156 
    157  DecodedData results;
    158  if (mReorderFrames) {
    159    results.SetCapacity(mReorderQueue.Length());
    160    while (!mReorderQueue.IsEmpty()) {
    161      results.AppendElement(mReorderQueue.Pop());
    162    }
    163  } else {
    164    results = std::move(mUnorderedData);
    165  }
    166 
    167  mDrainPromise.Resolve(std::move(results), __func__);
    168 }
    169 
    170 void GMPVideoDecoder::ResetComplete() {
    171  GMP_LOG_DEBUG("GMPVideoDecoder::ResetComplete");
    172  MOZ_ASSERT(IsOnGMPThread());
    173  mPerformanceRecorder.Record(std::numeric_limits<int64_t>::max());
    174  mFlushPromise.ResolveIfExists(true, __func__);
    175 }
    176 
    177 void GMPVideoDecoder::Error(GMPErr aErr) {
    178  GMP_LOG_DEBUG("GMPVideoDecoder::Error");
    179  MOZ_ASSERT(IsOnGMPThread());
    180  Teardown(ToMediaResult(aErr, "Error GMP callback"_ns), __func__);
    181 }
    182 
    183 void GMPVideoDecoder::Terminated() {
    184  GMP_LOG_DEBUG("GMPVideoDecoder::Terminated");
    185  MOZ_ASSERT(IsOnGMPThread());
    186  Teardown(
    187      MediaResult(NS_ERROR_DOM_MEDIA_ABORT_ERR, "Terminated GMP callback"_ns),
    188      __func__);
    189 }
    190 
    191 void GMPVideoDecoder::ProcessReorderQueue(
    192    MozPromiseHolder<DecodePromise>& aPromise, StaticString aMethodName) {
    193  if (aPromise.IsEmpty()) {
    194    return;
    195  }
    196 
    197  if (!mReorderFrames) {
    198    aPromise.Resolve(std::move(mUnorderedData), aMethodName);
    199    return;
    200  }
    201 
    202  DecodedData results;
    203  size_t availableFrames = mReorderQueue.Length();
    204  if (availableFrames > mMaxRefFrames) {
    205    size_t resolvedFrames = availableFrames - mMaxRefFrames;
    206    results.SetCapacity(resolvedFrames);
    207    do {
    208      results.AppendElement(mReorderQueue.Pop());
    209    } while (--resolvedFrames > 0);
    210  }
    211 
    212  aPromise.Resolve(std::move(results), aMethodName);
    213 }
    214 
    215 GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
    216    : mConfig(aParams.mConfig),
    217      mGMP(nullptr),
    218      mHost(nullptr),
    219      mConvertNALUnitLengths(false),
    220      mCrashHelper(aParams.mCrashHelper),
    221      mImageContainer(aParams.mImageContainer),
    222      mKnowsCompositor(aParams.mKnowsCompositor),
    223      mTrackingId(aParams.mTrackingId),
    224      mCanDecodeBatch(StaticPrefs::media_gmp_decoder_decode_batch()),
    225      mReorderFrames(StaticPrefs::media_gmp_decoder_reorder_frames()) {}
    226 
    227 void GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags) {
    228  if (MP4Decoder::IsH264(mConfig.mMimeType)) {
    229    aTags.AppendElement("h264"_ns);
    230  } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
    231    aTags.AppendElement("vp8"_ns);
    232  } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
    233    aTags.AppendElement("vp9"_ns);
    234  }
    235 }
    236 
    237 nsCString GMPVideoDecoder::GetNodeId() { return SHARED_GMP_DECODING_NODE_ID; }
    238 
    239 GMPUniquePtr<GMPVideoEncodedFrame> GMPVideoDecoder::CreateFrame(
    240    MediaRawData* aSample) {
    241  GMPVideoFrame* ftmp = nullptr;
    242  GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
    243  if (GMP_FAILED(err)) {
    244    return nullptr;
    245  }
    246 
    247  GMPUniquePtr<GMPVideoEncodedFrame> frame(
    248      static_cast<GMPVideoEncodedFrame*>(ftmp));
    249  err = frame->CreateEmptyFrame(aSample->Size());
    250  if (GMP_FAILED(err)) {
    251    return nullptr;
    252  }
    253 
    254  memcpy(frame->Buffer(), aSample->Data(), frame->Size());
    255 
    256  // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
    257  // suit the GMP API.
    258  if (mConvertNALUnitLengths) {
    259    const int kNALLengthSize = 4;
    260    uint8_t* buf = frame->Buffer();
    261    while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
    262      uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
    263      *reinterpret_cast<uint32_t*>(buf) = length;
    264      buf += length;
    265    }
    266  }
    267 
    268  frame->SetBufferType(GMP_BufferLength32);
    269 
    270  frame->SetEncodedWidth(mConfig.mDisplay.width);
    271  frame->SetEncodedHeight(mConfig.mDisplay.height);
    272  frame->SetTimeStamp(aSample->mTime.ToMicroseconds());
    273  frame->SetCompleteFrame(true);
    274  frame->SetDuration(aSample->mDuration.ToMicroseconds());
    275  frame->SetFrameType(aSample->mKeyframe ? kGMPKeyFrame : kGMPDeltaFrame);
    276 
    277  return frame;
    278 }
    279 
    280 const VideoInfo& GMPVideoDecoder::GetConfig() const { return mConfig; }
    281 
    282 void GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP,
    283                                  GMPVideoHost* aHost) {
    284  MOZ_ASSERT(IsOnGMPThread());
    285 
    286  if (!aGMP) {
    287    mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
    288    return;
    289  }
    290  MOZ_ASSERT(aHost);
    291 
    292  if (mInitPromise.IsEmpty()) {
    293    // GMP must have been shutdown while we were waiting for Init operation
    294    // to complete.
    295    aGMP->Close();
    296    return;
    297  }
    298 
    299  bool isOpenH264 = aGMP->GetPluginType() == GMPPluginType::OpenH264;
    300 
    301  GMPVideoCodec codec;
    302  memset(&codec, 0, sizeof(codec));
    303 
    304  codec.mGMPApiVersion = kGMPVersion34;
    305  nsTArray<uint8_t> codecSpecific;
    306  if (MP4Decoder::IsH264(mConfig.mMimeType)) {
    307    codec.mCodecType = kGMPVideoCodecH264;
    308    codecSpecific.AppendElement(0);  // mPacketizationMode.
    309    codecSpecific.AppendElements(mConfig.mExtraData->Elements(),
    310                                 mConfig.mExtraData->Length());
    311    // OpenH264 expects pseudo-AVCC, but others must be passed
    312    // AnnexB for H264.
    313    mConvertToAnnexB = !isOpenH264;
    314    mMaxRefFrames = H264::ComputeMaxRefFrames(mConfig.mExtraData);
    315  } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
    316    codec.mCodecType = kGMPVideoCodecVP8;
    317  } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
    318    codec.mCodecType = kGMPVideoCodecVP9;
    319  } else {
    320    // Unrecognized mime type
    321    aGMP->Close();
    322    mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
    323    return;
    324  }
    325  codec.mWidth = mConfig.mImage.width;
    326  codec.mHeight = mConfig.mImage.height;
    327  codec.mUseThreadedDecode = StaticPrefs::media_gmp_decoder_multithreaded();
    328  codec.mLogLevel = GetGMPLibraryLogLevel();
    329 
    330  nsresult rv =
    331      aGMP->InitDecode(codec, codecSpecific, this, PR_GetNumberOfProcessors());
    332  if (NS_FAILED(rv)) {
    333    aGMP->Close();
    334    mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
    335    return;
    336  }
    337 
    338  mGMP = aGMP;
    339  mHost = aHost;
    340 
    341  // GMP implementations have interpreted the meaning of GMP_BufferLength32
    342  // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
    343  // specified in the GMP API, where each buffer is prefixed by a 32-bit
    344  // host-endian buffer length that includes the size of the buffer length
    345  // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
    346  // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
    347  // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
    348  // and do not include the length of the buffer length field.
    349  mConvertNALUnitLengths = isOpenH264;
    350 
    351  mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
    352 }
    353 
    354 RefPtr<MediaDataDecoder::InitPromise> GMPVideoDecoder::Init() {
    355  MOZ_ASSERT(IsOnGMPThread());
    356 
    357  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    358  MOZ_ASSERT(mMPS);
    359 
    360  RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
    361 
    362  nsTArray<nsCString> tags;
    363  InitTags(tags);
    364  UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
    365  if (NS_FAILED(mMPS->GetGMPVideoDecoder(mCrashHelper, &tags, GetNodeId(),
    366                                         std::move(callback)))) {
    367    mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
    368  }
    369 
    370  return promise;
    371 }
    372 
    373 RefPtr<MediaDataDecoder::DecodePromise> GMPVideoDecoder::Decode(
    374    MediaRawData* aSample) {
    375  MOZ_ASSERT(IsOnGMPThread());
    376 
    377  RefPtr<MediaRawData> sample(aSample);
    378  if (!mGMP) {
    379    return DecodePromise::CreateAndReject(
    380        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    381                    RESULT_DETAIL("mGMP not initialized")),
    382        __func__);
    383  }
    384 
    385  if (mTrackingId) {
    386    MediaInfoFlag flag = MediaInfoFlag::None;
    387    flag |= (aSample->mKeyframe ? MediaInfoFlag::KeyFrame
    388                                : MediaInfoFlag::NonKeyFrame);
    389    if (mGMP->GetPluginType() == GMPPluginType::OpenH264) {
    390      flag |= MediaInfoFlag::SoftwareDecoding;
    391    }
    392    if (MP4Decoder::IsH264(mConfig.mMimeType)) {
    393      flag |= MediaInfoFlag::VIDEO_H264;
    394    } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
    395      flag |= MediaInfoFlag::VIDEO_VP8;
    396    } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
    397      flag |= MediaInfoFlag::VIDEO_VP9;
    398    }
    399    mPerformanceRecorder.Start(aSample->mTime.ToMicroseconds(),
    400                               "GMPVideoDecoder"_ns, *mTrackingId, flag);
    401  }
    402 
    403  GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
    404  if (!frame) {
    405    return DecodePromise::CreateAndReject(
    406        MediaResult(NS_ERROR_OUT_OF_MEMORY,
    407                    RESULT_DETAIL("CreateFrame returned null")),
    408        __func__);
    409  }
    410 
    411  uint64_t frameTimestamp = frame->TimeStamp();
    412  RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
    413  nsTArray<uint8_t> info;  // No codec specific per-frame info to pass.
    414  nsresult rv = mGMP->Decode(std::move(frame), false, info, 0);
    415  if (NS_FAILED(rv)) {
    416    mDecodePromise.Reject(MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
    417                                      RESULT_DETAIL("mGMP->Decode:%" PRIx32,
    418                                                    static_cast<uint32_t>(rv))),
    419                          __func__);
    420  }
    421 
    422  // If we have multiple outstanding frames, we need to track which offset
    423  // belongs to which frame. During seek, it is possible to get the same frame
    424  // requested twice, if the old frame is still outstanding. We will simply drop
    425  // the extra decoded frame and request more input if the last outstanding.
    426  mSamples.WithEntryHandle(frameTimestamp, [&](auto entryHandle) {
    427    auto sampleData = MakeUnique<SampleMetadata>(sample);
    428    entryHandle.InsertOrUpdate(std::move(sampleData));
    429  });
    430 
    431  return p;
    432 }
    433 
    434 RefPtr<MediaDataDecoder::FlushPromise> GMPVideoDecoder::Flush() {
    435  MOZ_ASSERT(IsOnGMPThread());
    436 
    437  mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    438  mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    439 
    440  RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
    441  if (!mGMP || NS_FAILED(mGMP->Reset())) {
    442    // Abort the flush.
    443    mPerformanceRecorder.Record(std::numeric_limits<int64_t>::max());
    444    mFlushPromise.Resolve(true, __func__);
    445  }
    446  return p;
    447 }
    448 
    449 RefPtr<MediaDataDecoder::DecodePromise> GMPVideoDecoder::Drain() {
    450  MOZ_ASSERT(IsOnGMPThread());
    451 
    452  MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
    453 
    454  RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
    455  if (!mGMP || NS_FAILED(mGMP->Drain())) {
    456    mDrainPromise.Resolve(DecodedData(), __func__);
    457  }
    458 
    459  return p;
    460 }
    461 
    462 void GMPVideoDecoder::Teardown(const MediaResult& aResult,
    463                               StaticString aCallSite) {
    464  MOZ_ASSERT(IsOnGMPThread());
    465 
    466  // Ensure we are kept alive at least until we return.
    467  RefPtr<GMPVideoDecoder> self(this);
    468 
    469  mInitPromise.RejectIfExists(aResult, aCallSite);
    470  mDecodePromise.RejectIfExists(aResult, aCallSite);
    471 
    472  if (mGMP) {
    473    // Note this unblocks flush and drain operations waiting for callbacks.
    474    mGMP->Close();
    475    mGMP = nullptr;
    476  } else {
    477    mFlushPromise.RejectIfExists(aResult, aCallSite);
    478    mDrainPromise.RejectIfExists(aResult, aCallSite);
    479  }
    480 
    481  mHost = nullptr;
    482 }
    483 
    484 RefPtr<ShutdownPromise> GMPVideoDecoder::Shutdown() {
    485  MOZ_ASSERT(IsOnGMPThread());
    486  Teardown(MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Shutdown"_ns), __func__);
    487  return ShutdownPromise::CreateAndResolve(true, __func__);
    488 }
    489 
    490 }  // namespace mozilla