Fri, 16 Jan 2015 04:50:19 +0100
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