content/media/fmp4/MP4Reader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     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/. */
     7 #include "MP4Reader.h"
     8 #include "MediaResource.h"
     9 #include "mp4_demuxer/mp4_demuxer.h"
    10 #include "mp4_demuxer/Streams.h"
    11 #include "nsSize.h"
    12 #include "VideoUtils.h"
    13 #include "mozilla/dom/HTMLMediaElement.h"
    14 #include "ImageContainer.h"
    15 #include "Layers.h"
    16 #include "SharedThreadPool.h"
    17 #include "mozilla/Preferences.h"
    19 using mozilla::layers::Image;
    20 using mozilla::layers::LayerManager;
    21 using mozilla::layers::LayersBackend;
    23 #ifdef PR_LOGGING
    24 PRLogModuleInfo* GetDemuxerLog() {
    25   static PRLogModuleInfo* log = nullptr;
    26   if (!log) {
    27     log = PR_NewLogModule("MP4Demuxer");
    28   }
    29   return log;
    30 }
    31 #define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
    32 #else
    33 #define LOG(...)
    34 #endif
    36 using namespace mp4_demuxer;
    38 namespace mozilla {
    40 // Uncomment to enable verbose per-sample logging.
    41 //#define LOG_SAMPLE_DECODE 1
    43 class MP4Stream : public mp4_demuxer::Stream {
    44 public:
    46   MP4Stream(MediaResource* aResource)
    47     : mResource(aResource)
    48   {
    49     MOZ_COUNT_CTOR(MP4Stream);
    50     MOZ_ASSERT(aResource);
    51   }
    52   virtual ~MP4Stream() {
    53     MOZ_COUNT_DTOR(MP4Stream);
    54   }
    56   virtual bool ReadAt(int64_t aOffset,
    57                       uint8_t* aBuffer,
    58                       uint32_t aCount,
    59                       uint32_t* aBytesRead) MOZ_OVERRIDE {
    60     uint32_t sum = 0;
    61     do {
    62       uint32_t offset = aOffset + sum;
    63       char* buffer = reinterpret_cast<char*>(aBuffer + sum);
    64       uint32_t toRead = aCount - sum;
    65       uint32_t bytesRead = 0;
    66       nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
    67       if (NS_FAILED(rv)) {
    68         return false;
    69       }
    70       sum += bytesRead;
    71     } while (sum < aCount);
    72     *aBytesRead = sum;
    73     return true;
    74   }
    76   virtual int64_t Length() const MOZ_OVERRIDE {
    77     return mResource->GetLength();
    78   }
    80 private:
    81   RefPtr<MediaResource> mResource;
    82 };
    84 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
    85   : MediaDecoderReader(aDecoder)
    86   , mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
    87   , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
    88   , mLastReportedNumDecodedFrames(0)
    89   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
    90 {
    91   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
    92   MOZ_COUNT_CTOR(MP4Reader);
    93 }
    95 MP4Reader::~MP4Reader()
    96 {
    97   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
    98   MOZ_COUNT_DTOR(MP4Reader);
    99   Shutdown();
   100 }
   102 void
   103 MP4Reader::Shutdown()
   104 {
   105   if (mAudio.mDecoder) {
   106     Flush(kAudio);
   107     mAudio.mDecoder->Shutdown();
   108     mAudio.mDecoder = nullptr;
   109   }
   110   if (mAudio.mTaskQueue) {
   111     mAudio.mTaskQueue->Shutdown();
   112     mAudio.mTaskQueue = nullptr;
   113   }
   114   if (mVideo.mDecoder) {
   115     Flush(kVideo);
   116     mVideo.mDecoder->Shutdown();
   117     mVideo.mDecoder = nullptr;
   118   }
   119   if (mVideo.mTaskQueue) {
   120     mVideo.mTaskQueue->Shutdown();
   121     mVideo.mTaskQueue = nullptr;
   122   }
   123 }
   125 void
   126 MP4Reader::InitLayersBackendType()
   127 {
   128   if (!IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
   129     // Not playing video, we don't care about the layers backend type.
   130     return;
   131   }
   132   // Extract the layer manager backend type so that platform decoders
   133   // can determine whether it's worthwhile using hardware accelerated
   134   // video decoding.
   135   MediaDecoderOwner* owner = mDecoder->GetOwner();
   136   if (!owner) {
   137     NS_WARNING("MP4Reader without a decoder owner, can't get HWAccel");
   138     return;
   139   }
   141   dom::HTMLMediaElement* element = owner->GetMediaElement();
   142   NS_ENSURE_TRUE_VOID(element);
   144   nsRefPtr<LayerManager> layerManager =
   145     nsContentUtils::LayerManagerForDocument(element->OwnerDoc());
   146   NS_ENSURE_TRUE_VOID(layerManager);
   148   mLayersBackendType = layerManager->GetCompositorBackendType();
   149 }
   151 nsresult
   152 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
   153 {
   154   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   155   PlatformDecoderModule::Init();
   156   mMP4Stream = new MP4Stream(mDecoder->GetResource());
   157   mDemuxer = new MP4Demuxer(mMP4Stream);
   159   InitLayersBackendType();
   161   mAudio.mTaskQueue = new MediaTaskQueue(
   162     SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Audio Decode")));
   163   NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
   165   mVideo.mTaskQueue = new MediaTaskQueue(
   166     SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Video Decode")));
   167   NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
   169   return NS_OK;
   170 }
   172 nsresult
   173 MP4Reader::ReadMetadata(MediaInfo* aInfo,
   174                         MetadataTags** aTags)
   175 {
   176   bool ok = mDemuxer->Init();
   177   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   179   const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
   180   mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasAudio() &&
   181                                             audio.IsValidConfig();
   182   // If we have audio, we *only* allow AAC to be decoded.
   183   if (HasAudio() && audio.codec() != kCodecAAC) {
   184     return NS_ERROR_FAILURE;
   185   }
   187   const VideoDecoderConfig& video = mDemuxer->VideoConfig();
   188   mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasVideo() &&
   189                                             video.IsValidConfig();
   190   // If we have video, we *only* allow H.264 to be decoded.
   191   if (HasVideo() && video.codec() != kCodecH264) {
   192     return NS_ERROR_FAILURE;
   193   }
   195   mPlatform = PlatformDecoderModule::Create();
   196   NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
   198   if (HasAudio()) {
   199     mInfo.mAudio.mRate = audio.samples_per_second();
   200     mInfo.mAudio.mChannels = ChannelLayoutToChannelCount(audio.channel_layout());
   201     mAudio.mCallback = new DecoderCallback(this, kAudio);
   202     mAudio.mDecoder = mPlatform->CreateAACDecoder(audio,
   203                                                   mAudio.mTaskQueue,
   204                                                   mAudio.mCallback);
   205     NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
   206     nsresult rv = mAudio.mDecoder->Init();
   207     NS_ENSURE_SUCCESS(rv, rv);
   208   }
   210   if (HasVideo()) {
   211     IntSize sz = video.natural_size();
   212     mInfo.mVideo.mDisplay = nsIntSize(sz.width(), sz.height());
   213     mVideo.mCallback = new  DecoderCallback(this, kVideo);
   214     mVideo.mDecoder = mPlatform->CreateH264Decoder(video,
   215                                                    mLayersBackendType,
   216                                                    mDecoder->GetImageContainer(),
   217                                                    mVideo.mTaskQueue,
   218                                                    mVideo.mCallback);
   219     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
   220     nsresult rv = mVideo.mDecoder->Init();
   221     NS_ENSURE_SUCCESS(rv, rv);
   222   }
   224   // Get the duration, and report it to the decoder if we have it.
   225   Microseconds duration = mDemuxer->Duration();
   226   if (duration != -1) {
   227     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   228     mDecoder->SetMediaDuration(duration);
   229   }
   230   // We can seek if we get a duration *and* the reader reports that it's
   231   // seekable.
   232   if (!mDemuxer->CanSeek()) {
   233     mDecoder->SetMediaSeekable(false);
   234   }
   236   *aInfo = mInfo;
   237   *aTags = nullptr;
   239   return NS_OK;
   240 }
   242 bool
   243 MP4Reader::HasAudio()
   244 {
   245   return mAudio.mActive;
   246 }
   248 bool
   249 MP4Reader::HasVideo()
   250 {
   251   return mVideo.mActive;
   252 }
   254 MP4Reader::DecoderData&
   255 MP4Reader::GetDecoderData(TrackType aTrack)
   256 {
   257   MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
   258   return (aTrack == kAudio) ? mAudio : mVideo;
   259 }
   261 MP4SampleQueue&
   262 MP4Reader::SampleQueue(TrackType aTrack)
   263 {
   264   return GetDecoderData(aTrack).mDemuxedSamples;
   265 }
   267 MediaDataDecoder*
   268 MP4Reader::Decoder(mp4_demuxer::TrackType aTrack)
   269 {
   270   return GetDecoderData(aTrack).mDecoder;
   271 }
   273 MP4Sample*
   274 MP4Reader::PopSample(TrackType aTrack)
   275 {
   276   // Unfortunately the demuxer outputs in the order samples appear in the
   277   // media, not on a per stream basis. We cache the samples we get from
   278   // streams other than the one we want.
   279   MP4SampleQueue& sampleQueue = SampleQueue(aTrack);
   280   while (sampleQueue.empty()) {
   281     nsAutoPtr<MP4Sample> sample;
   282     bool eos = false;
   283     bool ok = mDemuxer->Demux(&sample, &eos);
   284     if (!ok || eos) {
   285       MOZ_ASSERT(!sample);
   286       return nullptr;
   287     }
   288     MOZ_ASSERT(sample);
   289     MP4Sample* s = sample.forget();
   290     SampleQueue(s->type).push_back(s);
   291   }
   292   MOZ_ASSERT(!sampleQueue.empty());
   293   MP4Sample* sample = sampleQueue.front();
   294   sampleQueue.pop_front();
   295   return sample;
   296 }
   298 // How async decoding works:
   299 //
   300 // When MP4Reader::Decode() is called:
   301 // * Lock the DecoderData. We assume the state machine wants
   302 //   output from the decoder (in future, we'll assume decoder wants input
   303 //   when the output MediaQueue isn't "full").
   304 // * Cache the value of mNumSamplesOutput, as prevFramesOutput.
   305 // * While we've not output data (mNumSamplesOutput != prevNumFramesOutput)
   306 //   and while we still require input, we demux and input data in the reader.
   307 //   We assume we require input if
   308 //   ((mNumSamplesInput - mNumSamplesOutput) < sDecodeAheadMargin) or
   309 //   mInputExhausted is true. Before we send input, we reset mInputExhausted
   310 //   and increment mNumFrameInput, and drop the lock on DecoderData.
   311 // * Once we no longer require input, we wait on the DecoderData
   312 //   lock for output, or for the input exhausted callback. If we receive the
   313 //   input exhausted callback, we go back and input more data.
   314 // * When our output callback is called, we take the DecoderData lock and
   315 //   increment mNumSamplesOutput. We notify the DecoderData lock. This will
   316 //   awaken the Decode thread, and unblock it, and it will return.
   317 bool
   318 MP4Reader::Decode(TrackType aTrack)
   319 {
   320   DecoderData& data = GetDecoderData(aTrack);
   321   MOZ_ASSERT(data.mDecoder);
   323   data.mMonitor.Lock();
   324   uint64_t prevNumFramesOutput = data.mNumSamplesOutput;
   325   while (prevNumFramesOutput == data.mNumSamplesOutput) {
   326     data.mMonitor.AssertCurrentThreadOwns();
   327     if (data.mError) {
   328       // Decode error!
   329       data.mMonitor.Unlock();
   330       return false;
   331     }
   332     // Send input to the decoder, if we need to. We assume the decoder
   333     // needs input if it's told us it's out of input, or we're beneath the
   334     // "low water mark" for the amount of input we've sent it vs the amount
   335     // out output we've received. We always try to keep the decoder busy if
   336     // possible, so we try to maintain at least a few input samples ahead,
   337     // if we need output.
   338     while (prevNumFramesOutput == data.mNumSamplesOutput &&
   339            (data.mInputExhausted ||
   340            (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead)) {
   341       data.mMonitor.AssertCurrentThreadOwns();
   342       data.mMonitor.Unlock();
   343       nsAutoPtr<MP4Sample> compressed(PopSample(aTrack));
   344       if (!compressed) {
   345         // EOS, or error. Let the state machine know there are no more
   346         // frames coming.
   347         return false;
   348       }
   349       data.mMonitor.Lock();
   350       data.mInputExhausted = false;
   351       data.mNumSamplesInput++;
   352       data.mMonitor.Unlock();
   353       if (NS_FAILED(data.mDecoder->Input(compressed))) {
   354         return false;
   355       }
   356       // If Input() failed, we let the auto pointer delete |compressed|.
   357       // Otherwise, we assume the decoder will delete it when it's finished
   358       // with it.
   359       compressed.forget();
   360       data.mMonitor.Lock();
   361     }
   362     data.mMonitor.AssertCurrentThreadOwns();
   363     while (!data.mError &&
   364            prevNumFramesOutput == data.mNumSamplesOutput &&
   365            !data.mInputExhausted ) {
   366       data.mMonitor.Wait();
   367     }
   368   }
   369   data.mMonitor.AssertCurrentThreadOwns();
   370   data.mMonitor.Unlock();
   371   return true;
   372 }
   374 #ifdef LOG_SAMPLE_DECODE
   375 static const char*
   376 TrackTypeToStr(TrackType aTrack)
   377 {
   378   MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
   379   switch (aTrack) {
   380     case kAudio: return "Audio";
   381     case kVideo: return "Video";
   382     default: return "Unknown";
   383   }
   384 }
   385 #endif
   387 void
   388 MP4Reader::Output(mp4_demuxer::TrackType aTrack, MediaData* aSample)
   389 {
   390 #ifdef LOG_SAMPLE_DECODE
   391   LOG("Decoded %s sample time=%lld dur=%lld",
   392       TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
   393 #endif
   395   DecoderData& data = GetDecoderData(aTrack);
   396   // Don't accept output while we're flushing.
   397   MonitorAutoLock mon(data.mMonitor);
   398   if (data.mIsFlushing) {
   399     mon.NotifyAll();
   400     return;
   401   }
   403   switch (aTrack) {
   404     case kAudio: {
   405       MOZ_ASSERT(aSample->mType == MediaData::AUDIO_SAMPLES);
   406       AudioQueue().Push(static_cast<AudioData*>(aSample));
   407       break;
   408     }
   409     case kVideo: {
   410       MOZ_ASSERT(aSample->mType == MediaData::VIDEO_FRAME);
   411       VideoQueue().Push(static_cast<VideoData*>(aSample));
   412       break;
   413     }
   414     default:
   415       break;
   416   }
   418   data.mNumSamplesOutput++;
   419   mon.NotifyAll();
   420 }
   422 void
   423 MP4Reader::InputExhausted(mp4_demuxer::TrackType aTrack)
   424 {
   425   DecoderData& data = GetDecoderData(aTrack);
   426   MonitorAutoLock mon(data.mMonitor);
   427   data.mInputExhausted = true;
   428   mon.NotifyAll();
   429 }
   431 void
   432 MP4Reader::Error(mp4_demuxer::TrackType aTrack)
   433 {
   434   DecoderData& data = GetDecoderData(aTrack);
   435   MonitorAutoLock mon(data.mMonitor);
   436   data.mError = true;
   437   mon.NotifyAll();
   438 }
   440 bool
   441 MP4Reader::DecodeAudioData()
   442 {
   443   MOZ_ASSERT(HasAudio() && mPlatform && mAudio.mDecoder);
   444   return Decode(kAudio);
   445 }
   447 void
   448 MP4Reader::Flush(mp4_demuxer::TrackType aTrack)
   449 {
   450   DecoderData& data = GetDecoderData(aTrack);
   451   if (!data.mDecoder) {
   452     return;
   453   }
   454   // Purge the current decoder's state.
   455   // Set a flag so that we ignore all output while we call
   456   // MediaDataDecoder::Flush().
   457   {
   458     data.mIsFlushing = true;
   459     MonitorAutoLock mon(data.mMonitor);
   460   }
   461   data.mDecoder->Flush();
   462   {
   463     data.mIsFlushing = false;
   464     MonitorAutoLock mon(data.mMonitor);
   465   }
   466 }
   468 bool
   469 MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed)
   470 {
   471   MOZ_ASSERT(mVideo.mDecoder);
   473   Flush(kVideo);
   475   // Loop until we reach the next keyframe after the threshold.
   476   while (true) {
   477     nsAutoPtr<MP4Sample> compressed(PopSample(kVideo));
   478     if (!compressed) {
   479       // EOS, or error. Let the state machine know.
   480       return false;
   481     }
   482     parsed++;
   483     if (!compressed->is_sync_point ||
   484         compressed->composition_timestamp < aTimeThreshold) {
   485       continue;
   486     }
   487     mVideo.mDemuxedSamples.push_front(compressed.forget());
   488     break;
   489   }
   491   return true;
   492 }
   494 bool
   495 MP4Reader::DecodeVideoFrame(bool &aKeyframeSkip,
   496                             int64_t aTimeThreshold)
   497 {
   498   // Record number of frames decoded and parsed. Automatically update the
   499   // stats counters using the AutoNotifyDecoded stack-based class.
   500   uint32_t parsed = 0, decoded = 0;
   501   AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
   503   MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
   505   if (aKeyframeSkip) {
   506     bool ok = SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
   507     if (!ok) {
   508       NS_WARNING("Failed to skip demux up to next keyframe");
   509       return false;
   510     }
   511     aKeyframeSkip = false;
   512     nsresult rv = mVideo.mDecoder->Flush();
   513     NS_ENSURE_SUCCESS(rv, false);
   514   }
   516   bool rv = Decode(kVideo);
   517   {
   518     // Report the number of "decoded" frames as the difference in the
   519     // mNumSamplesOutput field since the last time we were called.
   520     MonitorAutoLock mon(mVideo.mMonitor);
   521     uint64_t delta = mVideo.mNumSamplesOutput - mLastReportedNumDecodedFrames;
   522     decoded = static_cast<uint32_t>(delta);
   523     mLastReportedNumDecodedFrames = mVideo.mNumSamplesOutput;
   524   }
   525   return rv;
   526 }
   528 nsresult
   529 MP4Reader::Seek(int64_t aTime,
   530                 int64_t aStartTime,
   531                 int64_t aEndTime,
   532                 int64_t aCurrentTime)
   533 {
   534   if (!mDemuxer->CanSeek()) {
   535     return NS_ERROR_FAILURE;
   536   }
   537   return NS_ERROR_NOT_IMPLEMENTED;
   538 }
   540 } // namespace mozilla

mercurial