Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "MediaDecoderReader.h"
8 :
9 : #include "AbstractMediaDecoder.h"
10 : #include "ImageContainer.h"
11 : #include "MediaPrefs.h"
12 : #include "MediaResource.h"
13 : #include "VideoUtils.h"
14 : #include "mozilla/Mutex.h"
15 : #include "mozilla/SharedThreadPool.h"
16 : #include "mozilla/TaskQueue.h"
17 : #include "mozilla/mozalloc.h"
18 : #include "nsPrintfCString.h"
19 : #include <algorithm>
20 : #include <stdint.h>
21 :
22 : using namespace mozilla::media;
23 :
24 : namespace mozilla {
25 :
26 : // Un-comment to enable logging of seek bisections.
27 : //#define SEEK_LOGGING
28 :
29 : extern LazyLogModule gMediaDecoderLog;
30 :
31 : // avoid redefined macro in unified build
32 : #undef FMT
33 : #undef DECODER_LOG
34 : #undef DECODER_WARN
35 :
36 : #define FMT(x, ...) "Decoder=%p " x, mDecoder, ##__VA_ARGS__
37 : #define DECODER_LOG(...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (FMT(__VA_ARGS__)))
38 : #define DECODER_WARN(...) NS_WARNING(nsPrintfCString(FMT(__VA_ARGS__)).get())
39 :
40 0 : class VideoQueueMemoryFunctor : public nsDequeFunctor {
41 : public:
42 0 : VideoQueueMemoryFunctor() : mSize(0) {}
43 :
44 0 : MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
45 :
46 0 : virtual void* operator()(void* aObject) {
47 0 : const VideoData* v = static_cast<const VideoData*>(aObject);
48 0 : mSize += v->SizeOfIncludingThis(MallocSizeOf);
49 0 : return nullptr;
50 : }
51 :
52 : size_t mSize;
53 : };
54 :
55 :
56 0 : class AudioQueueMemoryFunctor : public nsDequeFunctor {
57 : public:
58 0 : AudioQueueMemoryFunctor() : mSize(0) {}
59 :
60 0 : MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
61 :
62 0 : virtual void* operator()(void* aObject) {
63 0 : const AudioData* audioData = static_cast<const AudioData*>(aObject);
64 0 : mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
65 0 : return nullptr;
66 : }
67 :
68 : size_t mSize;
69 : };
70 :
71 0 : MediaDecoderReader::MediaDecoderReader(const MediaDecoderReaderInit& aInit)
72 : : mAudioCompactor(mAudioQueue)
73 0 : , mDecoder(aInit.mDecoder)
74 : , mTaskQueue(new TaskQueue(
75 0 : GetMediaThreadPool(MediaThreadType::PLAYBACK),
76 : "MediaDecoderReader::mTaskQueue",
77 0 : /* aSupportsTailDispatch = */ true))
78 0 : , mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
79 : , mIgnoreAudioOutputFormat(false)
80 : , mHitAudioDecodeError(false)
81 : , mShutdown(false)
82 0 : , mResource(aInit.mResource)
83 : {
84 0 : MOZ_COUNT_CTOR(MediaDecoderReader);
85 0 : MOZ_ASSERT(NS_IsMainThread());
86 0 : }
87 :
88 : nsresult
89 0 : MediaDecoderReader::Init()
90 : {
91 0 : return InitInternal();
92 : }
93 :
94 0 : MediaDecoderReader::~MediaDecoderReader()
95 : {
96 0 : MOZ_ASSERT(mShutdown);
97 0 : MOZ_COUNT_DTOR(MediaDecoderReader);
98 0 : }
99 :
100 0 : size_t MediaDecoderReader::SizeOfVideoQueueInBytes() const
101 : {
102 0 : VideoQueueMemoryFunctor functor;
103 0 : mVideoQueue.LockedForEach(functor);
104 0 : return functor.mSize;
105 : }
106 :
107 0 : size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
108 : {
109 0 : AudioQueueMemoryFunctor functor;
110 0 : mAudioQueue.LockedForEach(functor);
111 0 : return functor.mSize;
112 : }
113 :
114 0 : size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
115 : {
116 0 : return mVideoQueue.GetSize();
117 : }
118 :
119 0 : size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
120 : {
121 0 : return mAudioQueue.GetSize();
122 : }
123 :
124 : void
125 0 : MediaDecoderReader::UpdateDuration(const media::TimeUnit& aDuration)
126 : {
127 0 : MOZ_ASSERT(OnTaskQueue());
128 0 : mDuration = Some(aDuration);
129 0 : UpdateBuffered();
130 0 : }
131 :
132 0 : nsresult MediaDecoderReader::ResetDecode(TrackSet aTracks)
133 : {
134 0 : if (aTracks.contains(TrackInfo::kVideoTrack)) {
135 0 : VideoQueue().Reset();
136 0 : mBaseVideoPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
137 : }
138 :
139 0 : if (aTracks.contains(TrackInfo::kAudioTrack)) {
140 0 : AudioQueue().Reset();
141 0 : mBaseAudioPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
142 : }
143 :
144 0 : return NS_OK;
145 : }
146 :
147 : RefPtr<MediaDecoderReader::VideoDataPromise>
148 0 : MediaDecoderReader::DecodeToFirstVideoData()
149 : {
150 0 : MOZ_ASSERT(OnTaskQueue());
151 : typedef VideoDataPromise PromiseType;
152 0 : RefPtr<PromiseType::Private> p = new PromiseType::Private(__func__);
153 0 : RefPtr<MediaDecoderReader> self = this;
154 0 : InvokeUntil([self] () -> bool {
155 0 : MOZ_ASSERT(self->OnTaskQueue());
156 0 : NS_ENSURE_TRUE(!self->mShutdown, false);
157 0 : bool skip = false;
158 0 : if (!self->DecodeVideoFrame(skip, media::TimeUnit::Zero())) {
159 0 : self->VideoQueue().Finish();
160 0 : return !!self->VideoQueue().PeekFront();
161 : }
162 0 : return true;
163 0 : }, [self] () -> bool {
164 0 : MOZ_ASSERT(self->OnTaskQueue());
165 0 : return self->VideoQueue().GetSize();
166 0 : })->Then(OwnerThread(), __func__, [self, p] () {
167 0 : p->Resolve(self->VideoQueue().PeekFront(), __func__);
168 0 : }, [p] () {
169 : // We don't have a way to differentiate EOS, error, and shutdown here. :-(
170 0 : p->Reject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
171 0 : });
172 :
173 0 : return p.forget();
174 : }
175 :
176 : void
177 0 : MediaDecoderReader::UpdateBuffered()
178 : {
179 0 : MOZ_ASSERT(OnTaskQueue());
180 0 : NS_ENSURE_TRUE_VOID(!mShutdown);
181 0 : mBuffered = GetBuffered();
182 : }
183 :
184 : void
185 0 : MediaDecoderReader::VisibilityChanged()
186 0 : {}
187 :
188 : media::TimeIntervals
189 0 : MediaDecoderReader::GetBuffered()
190 : {
191 0 : MOZ_ASSERT(OnTaskQueue());
192 :
193 0 : if (mDuration.isNothing()) {
194 0 : return TimeIntervals();
195 : }
196 :
197 0 : AutoPinned<MediaResource> stream(mResource);
198 0 : return GetEstimatedBufferedTimeRanges(stream, mDuration->ToMicroseconds());
199 : }
200 :
201 : RefPtr<MediaDecoderReader::MetadataPromise>
202 0 : MediaDecoderReader::AsyncReadMetadata()
203 : {
204 0 : MOZ_ASSERT(OnTaskQueue());
205 0 : DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
206 :
207 : // Attempt to read the metadata.
208 0 : MetadataHolder metadata;
209 0 : metadata.mInfo = MakeUnique<MediaInfo>();
210 0 : MetadataTags* tags = nullptr;
211 0 : nsresult rv = ReadMetadata(metadata.mInfo.get(), &tags);
212 0 : metadata.mTags.reset(tags);
213 0 : metadata.mInfo->AssertValid();
214 :
215 : // Update the buffer ranges before resolving the metadata promise. Bug 1320258.
216 0 : UpdateBuffered();
217 :
218 : // We're not waiting for anything. If we didn't get the metadata, that's an
219 : // error.
220 0 : if (NS_FAILED(rv) || !metadata.mInfo->HasValidMedia()) {
221 0 : DECODER_WARN("ReadMetadata failed, rv=%" PRIx32 " HasValidMedia=%d",
222 0 : static_cast<uint32_t>(rv), metadata.mInfo->HasValidMedia());
223 0 : return MetadataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
224 : }
225 :
226 : // Success!
227 0 : return MetadataPromise::CreateAndResolve(Move(metadata), __func__);
228 : }
229 :
230 0 : class ReRequestVideoWithSkipTask : public Runnable
231 : {
232 : public:
233 0 : ReRequestVideoWithSkipTask(MediaDecoderReader* aReader,
234 : const media::TimeUnit& aTimeThreshold)
235 0 : : Runnable("ReRequestVideoWithSkipTask")
236 : , mReader(aReader)
237 0 : , mTimeThreshold(aTimeThreshold)
238 : {
239 0 : }
240 :
241 0 : NS_IMETHOD Run() override
242 : {
243 0 : MOZ_ASSERT(mReader->OnTaskQueue());
244 :
245 : // Make sure ResetDecode hasn't been called in the mean time.
246 0 : if (!mReader->mBaseVideoPromise.IsEmpty()) {
247 0 : mReader->RequestVideoData(mTimeThreshold);
248 : }
249 :
250 0 : return NS_OK;
251 : }
252 :
253 : private:
254 : RefPtr<MediaDecoderReader> mReader;
255 : const media::TimeUnit mTimeThreshold;
256 : };
257 :
258 0 : class ReRequestAudioTask : public Runnable
259 : {
260 : public:
261 0 : explicit ReRequestAudioTask(MediaDecoderReader* aReader)
262 0 : : Runnable("ReRequestAudioTask")
263 0 : , mReader(aReader)
264 : {
265 0 : }
266 :
267 0 : NS_IMETHOD Run() override
268 : {
269 0 : MOZ_ASSERT(mReader->OnTaskQueue());
270 :
271 : // Make sure ResetDecode hasn't been called in the mean time.
272 0 : if (!mReader->mBaseAudioPromise.IsEmpty()) {
273 0 : mReader->RequestAudioData();
274 : }
275 :
276 0 : return NS_OK;
277 : }
278 :
279 : private:
280 : RefPtr<MediaDecoderReader> mReader;
281 : };
282 :
283 : RefPtr<MediaDecoderReader::VideoDataPromise>
284 0 : MediaDecoderReader::RequestVideoData(const media::TimeUnit& aTimeThreshold)
285 : {
286 0 : RefPtr<VideoDataPromise> p = mBaseVideoPromise.Ensure(__func__);
287 0 : bool skip = false;
288 0 : while (VideoQueue().GetSize() == 0 &&
289 0 : !VideoQueue().IsFinished()) {
290 0 : if (!DecodeVideoFrame(skip, aTimeThreshold)) {
291 0 : VideoQueue().Finish();
292 0 : } else if (skip) {
293 : // We still need to decode more data in order to skip to the next
294 : // keyframe. Post another task to the decode task queue to decode
295 : // again. We don't just decode straight in a loop here, as that
296 : // would hog the decode task queue.
297 : RefPtr<nsIRunnable> task(
298 0 : new ReRequestVideoWithSkipTask(this, aTimeThreshold));
299 0 : mTaskQueue->Dispatch(task.forget());
300 0 : return p;
301 : }
302 : }
303 0 : if (VideoQueue().GetSize() > 0) {
304 0 : RefPtr<VideoData> v = VideoQueue().PopFront();
305 0 : mBaseVideoPromise.Resolve(v, __func__);
306 0 : } else if (VideoQueue().IsFinished()) {
307 0 : mBaseVideoPromise.Reject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
308 : } else {
309 0 : MOZ_ASSERT(false, "Dropping this promise on the floor");
310 : }
311 :
312 0 : return p;
313 : }
314 :
315 : RefPtr<MediaDecoderReader::AudioDataPromise>
316 0 : MediaDecoderReader::RequestAudioData()
317 : {
318 0 : RefPtr<AudioDataPromise> p = mBaseAudioPromise.Ensure(__func__);
319 0 : while (AudioQueue().GetSize() == 0 &&
320 0 : !AudioQueue().IsFinished()) {
321 0 : if (!DecodeAudioData()) {
322 0 : AudioQueue().Finish();
323 0 : break;
324 : }
325 : // AudioQueue size is still zero, post a task to try again. Don't spin
326 : // waiting in this while loop since it somehow prevents audio EOS from
327 : // coming in gstreamer 1.x when there is still video buffer waiting to be
328 : // consumed. (|mVideoSinkBufferCount| > 0)
329 0 : if (AudioQueue().GetSize() == 0) {
330 0 : RefPtr<nsIRunnable> task(new ReRequestAudioTask(this));
331 0 : mTaskQueue->Dispatch(task.forget());
332 0 : return p;
333 : }
334 : }
335 0 : if (AudioQueue().GetSize() > 0) {
336 0 : RefPtr<AudioData> a = AudioQueue().PopFront();
337 0 : mBaseAudioPromise.Resolve(a, __func__);
338 0 : } else if (AudioQueue().IsFinished()) {
339 0 : mBaseAudioPromise.Reject(mHitAudioDecodeError
340 : ? NS_ERROR_DOM_MEDIA_FATAL_ERR
341 0 : : NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
342 0 : mHitAudioDecodeError = false;
343 : } else {
344 0 : MOZ_ASSERT(false, "Dropping this promise on the floor");
345 : }
346 :
347 0 : return p;
348 : }
349 :
350 : RefPtr<ShutdownPromise>
351 0 : MediaDecoderReader::Shutdown()
352 : {
353 0 : MOZ_ASSERT(OnTaskQueue());
354 0 : mShutdown = true;
355 :
356 0 : mBaseAudioPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
357 0 : mBaseVideoPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
358 :
359 0 : ReleaseResources();
360 0 : mBuffered.DisconnectAll();
361 :
362 0 : mDecoder = nullptr;
363 :
364 0 : return mTaskQueue->BeginShutdown();
365 : }
366 :
367 : } // namespace mozilla
|