LCOV - code coverage report
Current view: top level - dom/media/platforms/agnostic/gmp - GMPVideoDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 207 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 21 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "GMPDecoderModule.h"
       9             : #include "GMPVideoHost.h"
      10             : #include "MediaData.h"
      11             : #include "VPXDecoder.h"
      12             : #include "mozilla/EndianUtils.h"
      13             : #include "prsystem.h"
      14             : #include "mp4_demuxer/AnnexB.h"
      15             : 
      16             : namespace mozilla {
      17             : 
      18             : #if defined(DEBUG)
      19           0 : static bool IsOnGMPThread()
      20             : {
      21             :   nsCOMPtr<mozIGeckoMediaPluginService> mps =
      22           0 :     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
      23           0 :   MOZ_ASSERT(mps);
      24             : 
      25           0 :   nsCOMPtr<nsIThread> gmpThread;
      26           0 :   nsresult rv = mps->GetThread(getter_AddRefs(gmpThread));
      27           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv) && gmpThread);
      28           0 :   return gmpThread->EventTarget()->IsOnCurrentThread();
      29             : }
      30             : #endif
      31             : 
      32           0 : GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
      33           0 :   : mConfig(aParams.VideoConfig())
      34           0 :   , mTaskQueue(aParams.mTaskQueue)
      35           0 :   , mImageContainer(aParams.mImageContainer)
      36           0 :   , mLayersBackend(aParams.GetLayersBackend())
      37           0 :   , mCrashHelper(aParams.mCrashHelper)
      38             : {
      39           0 : }
      40             : 
      41             : void
      42           0 : GMPVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
      43             : {
      44           0 :   GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
      45             : 
      46           0 :   MOZ_ASSERT(IsOnGMPThread());
      47             : 
      48           0 :   VideoData::YCbCrBuffer b;
      49           0 :   for (int i = 0; i < kGMPNumOfPlanes; ++i) {
      50           0 :     b.mPlanes[i].mData = decodedFrame->Buffer(GMPPlaneType(i));
      51           0 :     b.mPlanes[i].mStride = decodedFrame->Stride(GMPPlaneType(i));
      52           0 :     if (i == kGMPYPlane) {
      53           0 :       b.mPlanes[i].mWidth = decodedFrame->Width();
      54           0 :       b.mPlanes[i].mHeight = decodedFrame->Height();
      55             :     } else {
      56           0 :       b.mPlanes[i].mWidth = (decodedFrame->Width() + 1) / 2;
      57           0 :       b.mPlanes[i].mHeight = (decodedFrame->Height() + 1) / 2;
      58             :     }
      59           0 :     b.mPlanes[i].mOffset = 0;
      60           0 :     b.mPlanes[i].mSkip = 0;
      61             :   }
      62             : 
      63             :   gfx::IntRect pictureRegion(
      64           0 :     0, 0, decodedFrame->Width(), decodedFrame->Height());
      65           0 :   RefPtr<VideoData> v = VideoData::CreateAndCopyData(
      66             :     mConfig,
      67             :     mImageContainer,
      68             :     mLastStreamOffset,
      69           0 :     media::TimeUnit::FromMicroseconds(decodedFrame->Timestamp()),
      70           0 :     media::TimeUnit::FromMicroseconds(decodedFrame->Duration()),
      71             :     b,
      72             :     false,
      73           0 :     media::TimeUnit::FromMicroseconds(-1),
      74           0 :     pictureRegion);
      75           0 :   RefPtr<GMPVideoDecoder> self = this;
      76           0 :   if (v) {
      77           0 :     mDecodedData.AppendElement(Move(v));
      78             :   } else {
      79           0 :     mDecodedData.Clear();
      80           0 :     mDecodePromise.RejectIfExists(
      81           0 :       MediaResult(NS_ERROR_OUT_OF_MEMORY,
      82           0 :                   RESULT_DETAIL("CallBack::CreateAndCopyData")),
      83           0 :       __func__);
      84             :   }
      85           0 : }
      86             : 
      87             : void
      88           0 : GMPVideoDecoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
      89             : {
      90           0 :   MOZ_ASSERT(IsOnGMPThread());
      91           0 : }
      92             : 
      93             : void
      94           0 : GMPVideoDecoder::ReceivedDecodedFrame(const uint64_t aPictureId)
      95             : {
      96           0 :   MOZ_ASSERT(IsOnGMPThread());
      97           0 : }
      98             : 
      99             : void
     100           0 : GMPVideoDecoder::InputDataExhausted()
     101             : {
     102           0 :   MOZ_ASSERT(IsOnGMPThread());
     103           0 :   mDecodePromise.ResolveIfExists(mDecodedData, __func__);
     104           0 :   mDecodedData.Clear();
     105           0 : }
     106             : 
     107             : void
     108           0 : GMPVideoDecoder::DrainComplete()
     109             : {
     110           0 :   MOZ_ASSERT(IsOnGMPThread());
     111           0 :   mDrainPromise.ResolveIfExists(mDecodedData, __func__);
     112           0 :   mDecodedData.Clear();
     113           0 : }
     114             : 
     115             : void
     116           0 : GMPVideoDecoder::ResetComplete()
     117             : {
     118           0 :   MOZ_ASSERT(IsOnGMPThread());
     119           0 :   mFlushPromise.ResolveIfExists(true, __func__);
     120           0 : }
     121             : 
     122             : void
     123           0 : GMPVideoDecoder::Error(GMPErr aErr)
     124             : {
     125           0 :   MOZ_ASSERT(IsOnGMPThread());
     126             :   auto error = MediaResult(aErr == GMPDecodeErr ? NS_ERROR_DOM_MEDIA_DECODE_ERR
     127             :                                                 : NS_ERROR_DOM_MEDIA_FATAL_ERR,
     128           0 :                            RESULT_DETAIL("GMPErr:%x", aErr));
     129           0 :   mDecodePromise.RejectIfExists(error, __func__);
     130           0 :   mDrainPromise.RejectIfExists(error, __func__);
     131           0 :   mFlushPromise.RejectIfExists(error, __func__);
     132           0 : }
     133             : 
     134             : void
     135           0 : GMPVideoDecoder::Terminated()
     136             : {
     137           0 :   MOZ_ASSERT(IsOnGMPThread());
     138           0 :   Error(GMPErr::GMPAbortedErr);
     139           0 : }
     140             : 
     141           0 : GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
     142             :   : mConfig(aParams.mConfig)
     143             :   , mGMP(nullptr)
     144             :   , mHost(nullptr)
     145             :   , mConvertNALUnitLengths(false)
     146             :   , mCrashHelper(aParams.mCrashHelper)
     147           0 :   , mImageContainer(aParams.mImageContainer)
     148             : {
     149           0 : }
     150             : 
     151             : void
     152           0 : GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
     153             : {
     154           0 :   if (MP4Decoder::IsH264(mConfig.mMimeType)) {
     155           0 :     aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
     156           0 :   } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
     157           0 :     aTags.AppendElement(NS_LITERAL_CSTRING("vp8"));
     158           0 :   } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
     159           0 :     aTags.AppendElement(NS_LITERAL_CSTRING("vp9"));
     160             :   }
     161           0 : }
     162             : 
     163             : nsCString
     164           0 : GMPVideoDecoder::GetNodeId()
     165             : {
     166           0 :   return SHARED_GMP_DECODING_NODE_ID;
     167             : }
     168             : 
     169             : GMPUniquePtr<GMPVideoEncodedFrame>
     170           0 : GMPVideoDecoder::CreateFrame(MediaRawData* aSample)
     171             : {
     172           0 :   GMPVideoFrame* ftmp = nullptr;
     173           0 :   GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
     174           0 :   if (GMP_FAILED(err)) {
     175           0 :     return nullptr;
     176             :   }
     177             : 
     178             :   GMPUniquePtr<GMPVideoEncodedFrame> frame(
     179           0 :     static_cast<GMPVideoEncodedFrame*>(ftmp));
     180           0 :   err = frame->CreateEmptyFrame(aSample->Size());
     181           0 :   if (GMP_FAILED(err)) {
     182           0 :     return nullptr;
     183             :   }
     184             : 
     185           0 :   memcpy(frame->Buffer(), aSample->Data(), frame->Size());
     186             : 
     187             :   // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
     188             :   // suit the GMP API.
     189           0 :   if (mConvertNALUnitLengths) {
     190           0 :     const int kNALLengthSize = 4;
     191           0 :     uint8_t* buf = frame->Buffer();
     192           0 :     while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
     193           0 :       uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
     194           0 :       *reinterpret_cast<uint32_t *>(buf) = length;
     195           0 :       buf += length;
     196             :     }
     197             :   }
     198             : 
     199           0 :   frame->SetBufferType(GMP_BufferLength32);
     200             : 
     201           0 :   frame->SetEncodedWidth(mConfig.mDisplay.width);
     202           0 :   frame->SetEncodedHeight(mConfig.mDisplay.height);
     203           0 :   frame->SetTimeStamp(aSample->mTime.ToMicroseconds());
     204           0 :   frame->SetCompleteFrame(true);
     205           0 :   frame->SetDuration(aSample->mDuration.ToMicroseconds());
     206           0 :   frame->SetFrameType(aSample->mKeyframe ? kGMPKeyFrame : kGMPDeltaFrame);
     207             : 
     208           0 :   return frame;
     209             : }
     210             : 
     211             : const VideoInfo&
     212           0 : GMPVideoDecoder::GetConfig() const
     213             : {
     214           0 :   return mConfig;
     215             : }
     216             : 
     217             : void
     218           0 : GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
     219             : {
     220           0 :   MOZ_ASSERT(IsOnGMPThread());
     221             : 
     222           0 :   if (!aGMP) {
     223           0 :     mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     224           0 :     return;
     225             :   }
     226           0 :   MOZ_ASSERT(aHost);
     227             : 
     228           0 :   if (mInitPromise.IsEmpty()) {
     229             :     // GMP must have been shutdown while we were waiting for Init operation
     230             :     // to complete.
     231           0 :     aGMP->Close();
     232           0 :     return;
     233             :   }
     234             : 
     235           0 :   bool isOpenH264 = aGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
     236             : 
     237             :   GMPVideoCodec codec;
     238           0 :   memset(&codec, 0, sizeof(codec));
     239             : 
     240           0 :   codec.mGMPApiVersion = kGMPVersion33;
     241           0 :   nsTArray<uint8_t> codecSpecific;
     242           0 :   if (MP4Decoder::IsH264(mConfig.mMimeType)) {
     243           0 :     codec.mCodecType = kGMPVideoCodecH264;
     244           0 :     codecSpecific.AppendElement(0); // mPacketizationMode.
     245           0 :     codecSpecific.AppendElements(mConfig.mExtraData->Elements(),
     246           0 :                                  mConfig.mExtraData->Length());
     247             :     // OpenH264 expects pseudo-AVCC, but others must be passed
     248             :     // AnnexB for H264.
     249           0 :     mConvertToAnnexB = !isOpenH264;
     250           0 :   } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
     251           0 :     codec.mCodecType = kGMPVideoCodecVP8;
     252           0 :   } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
     253           0 :     codec.mCodecType = kGMPVideoCodecVP9;
     254             :   } else {
     255             :     // Unrecognized mime type
     256           0 :     aGMP->Close();
     257           0 :     mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     258           0 :     return;
     259             :   }
     260           0 :   codec.mWidth = mConfig.mImage.width;
     261           0 :   codec.mHeight = mConfig.mImage.height;
     262             : 
     263           0 :   nsresult rv = aGMP->InitDecode(codec,
     264             :                                  codecSpecific,
     265             :                                  this,
     266           0 :                                  PR_GetNumberOfProcessors());
     267           0 :   if (NS_FAILED(rv)) {
     268           0 :     aGMP->Close();
     269           0 :     mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     270           0 :     return;
     271             :   }
     272             : 
     273           0 :   mGMP = aGMP;
     274           0 :   mHost = aHost;
     275             : 
     276             :   // GMP implementations have interpreted the meaning of GMP_BufferLength32
     277             :   // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
     278             :   // specified in the GMP API, where each buffer is prefixed by a 32-bit
     279             :   // host-endian buffer length that includes the size of the buffer length
     280             :   // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
     281             :   // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
     282             :   // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
     283             :   // and do not include the length of the buffer length field.
     284           0 :   mConvertNALUnitLengths = isOpenH264;
     285             : 
     286           0 :   mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
     287             : }
     288             : 
     289             : RefPtr<MediaDataDecoder::InitPromise>
     290           0 : GMPVideoDecoder::Init()
     291             : {
     292           0 :   MOZ_ASSERT(IsOnGMPThread());
     293             : 
     294           0 :   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     295           0 :   MOZ_ASSERT(mMPS);
     296             : 
     297           0 :   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
     298             : 
     299           0 :   nsTArray<nsCString> tags;
     300           0 :   InitTags(tags);
     301           0 :   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
     302           0 :   if (NS_FAILED(mMPS->GetDecryptingGMPVideoDecoder(mCrashHelper,
     303             :                                                    &tags,
     304             :                                                    GetNodeId(),
     305             :                                                    Move(callback),
     306             :                                                    DecryptorId()))) {
     307           0 :     mInitPromise.Reject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     308             :   }
     309             : 
     310           0 :   return promise;
     311             : }
     312             : 
     313             : RefPtr<MediaDataDecoder::DecodePromise>
     314           0 : GMPVideoDecoder::Decode(MediaRawData* aSample)
     315             : {
     316           0 :   MOZ_ASSERT(IsOnGMPThread());
     317             : 
     318           0 :   RefPtr<MediaRawData> sample(aSample);
     319           0 :   if (!mGMP) {
     320             :     return DecodePromise::CreateAndReject(
     321           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     322           0 :                   RESULT_DETAIL("mGMP not initialized")),
     323           0 :       __func__);
     324             :   }
     325             : 
     326           0 :   mLastStreamOffset = sample->mOffset;
     327             : 
     328           0 :   GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
     329           0 :   if (!frame) {
     330             :     return DecodePromise::CreateAndReject(
     331           0 :       MediaResult(NS_ERROR_OUT_OF_MEMORY,
     332           0 :                   RESULT_DETAIL("CreateFrame returned null")),
     333           0 :       __func__);
     334             :   }
     335           0 :   RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
     336           0 :   nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
     337           0 :   nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
     338           0 :   if (NS_FAILED(rv)) {
     339           0 :     mDecodePromise.Reject(MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
     340           0 :                                       RESULT_DETAIL("mGMP->Decode:%" PRIx32,
     341             :                                                     static_cast<uint32_t>(rv))),
     342           0 :                           __func__);
     343             :   }
     344           0 :   return p;
     345             : }
     346             : 
     347             : RefPtr<MediaDataDecoder::FlushPromise>
     348           0 : GMPVideoDecoder::Flush()
     349             : {
     350           0 :   MOZ_ASSERT(IsOnGMPThread());
     351             : 
     352           0 :   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     353           0 :   mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     354             : 
     355           0 :   RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
     356           0 :   if (!mGMP || NS_FAILED(mGMP->Reset())) {
     357             :     // Abort the flush.
     358           0 :     mFlushPromise.Resolve(true, __func__);
     359             :   }
     360           0 :   return p;
     361             : }
     362             : 
     363             : RefPtr<MediaDataDecoder::DecodePromise>
     364           0 : GMPVideoDecoder::Drain()
     365             : {
     366           0 :   MOZ_ASSERT(IsOnGMPThread());
     367             : 
     368           0 :   MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
     369             : 
     370           0 :   RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
     371           0 :   if (!mGMP || NS_FAILED(mGMP->Drain())) {
     372           0 :     mDrainPromise.Resolve(DecodedData(), __func__);
     373             :   }
     374             : 
     375           0 :   return p;
     376             : }
     377             : 
     378             : RefPtr<ShutdownPromise>
     379           0 : GMPVideoDecoder::Shutdown()
     380             : {
     381           0 :   mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     382           0 :   mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     383             : 
     384             :   // Note that this *may* be called from the proxy thread also.
     385             :   // TODO: If that's the case, then this code is racy.
     386           0 :   if (!mGMP) {
     387           0 :     return ShutdownPromise::CreateAndResolve(true, __func__);
     388             :   }
     389             :   // Note this unblocks flush and drain operations waiting for callbacks.
     390           0 :   mGMP->Close();
     391           0 :   mGMP = nullptr;
     392           0 :   return ShutdownPromise::CreateAndResolve(true, __func__);
     393             : }
     394             : 
     395             : } // namespace mozilla

Generated by: LCOV version 1.13