2014-02-04 01:49:21 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 11:12:37 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2010-04-02 03:03:07 +00:00
|
|
|
|
2013-09-05 17:29:38 +00:00
|
|
|
#include "MediaDecoderReader.h"
|
2012-11-19 15:11:21 +00:00
|
|
|
#include "AbstractMediaDecoder.h"
|
2014-11-12 04:50:21 +00:00
|
|
|
#include "MediaResource.h"
|
2010-04-27 08:53:44 +00:00
|
|
|
#include "VideoUtils.h"
|
2012-08-21 04:06:46 +00:00
|
|
|
#include "ImageContainer.h"
|
2010-04-02 03:03:07 +00:00
|
|
|
|
2015-03-20 19:53:32 +00:00
|
|
|
#include "nsPrintfCString.h"
|
2012-01-11 08:23:07 +00:00
|
|
|
#include "mozilla/mozalloc.h"
|
2013-07-30 14:25:31 +00:00
|
|
|
#include <stdint.h>
|
2013-01-15 12:22:03 +00:00
|
|
|
#include <algorithm>
|
2012-01-11 08:23:07 +00:00
|
|
|
|
2015-06-09 19:41:24 +00:00
|
|
|
using namespace mozilla::media;
|
|
|
|
|
2012-11-14 19:45:33 +00:00
|
|
|
namespace mozilla {
|
|
|
|
|
2010-04-02 03:03:07 +00:00
|
|
|
// Un-comment to enable logging of seek bisections.
|
|
|
|
//#define SEEK_LOGGING
|
|
|
|
|
2012-11-14 19:46:40 +00:00
|
|
|
extern PRLogModuleInfo* gMediaDecoderLog;
|
2014-10-28 20:30:36 +00:00
|
|
|
#define DECODER_LOG(x, ...) \
|
2015-06-03 22:25:57 +00:00
|
|
|
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("Decoder=%p " x, mDecoder, ##__VA_ARGS__))
|
2010-04-02 03:03:07 +00:00
|
|
|
|
2015-03-20 19:53:32 +00:00
|
|
|
// Same workaround as MediaDecoderStateMachine.cpp.
|
|
|
|
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
|
|
|
|
#define DECODER_WARN(x, ...) \
|
|
|
|
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder, ##__VA_ARGS__).get()))
|
|
|
|
|
2014-03-19 21:33:12 +00:00
|
|
|
class VideoQueueMemoryFunctor : public nsDequeFunctor {
|
|
|
|
public:
|
|
|
|
VideoQueueMemoryFunctor() : mSize(0) {}
|
|
|
|
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
|
|
|
|
|
|
|
|
virtual void* operator()(void* aObject) {
|
|
|
|
const VideoData* v = static_cast<const VideoData*>(aObject);
|
|
|
|
mSize += v->SizeOfIncludingThis(MallocSizeOf);
|
2012-08-21 04:06:46 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-03-19 21:33:12 +00:00
|
|
|
size_t mSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class AudioQueueMemoryFunctor : public nsDequeFunctor {
|
|
|
|
public:
|
|
|
|
AudioQueueMemoryFunctor() : mSize(0) {}
|
|
|
|
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
|
|
|
|
|
|
|
|
virtual void* operator()(void* aObject) {
|
|
|
|
const AudioData* audioData = static_cast<const AudioData*>(aObject);
|
|
|
|
mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
|
|
|
|
return nullptr;
|
2013-07-07 20:33:56 +00:00
|
|
|
}
|
2014-03-19 21:33:12 +00:00
|
|
|
|
|
|
|
size_t mSize;
|
|
|
|
};
|
2012-08-21 04:06:46 +00:00
|
|
|
|
2015-06-09 00:16:42 +00:00
|
|
|
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
|
2015-07-16 18:13:49 +00:00
|
|
|
TaskQueue* aBorrowedTaskQueue)
|
2014-06-18 05:07:02 +00:00
|
|
|
: mAudioCompactor(mAudioQueue)
|
|
|
|
, mDecoder(aDecoder)
|
2015-06-09 22:43:48 +00:00
|
|
|
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
|
2015-07-16 18:13:49 +00:00
|
|
|
: new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
|
|
|
/* aSupportsTailDispatch = */ true))
|
2015-06-17 21:22:10 +00:00
|
|
|
, mWatchManager(this, mTaskQueue)
|
2015-06-19 20:45:09 +00:00
|
|
|
, mTimer(new MediaTimer())
|
2015-06-17 21:22:10 +00:00
|
|
|
, mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
|
2015-06-09 19:41:24 +00:00
|
|
|
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
|
2015-06-19 20:45:09 +00:00
|
|
|
, mThrottleDuration(TimeDuration::FromMilliseconds(500))
|
|
|
|
, mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration)
|
2014-06-18 05:07:02 +00:00
|
|
|
, mIgnoreAudioOutputFormat(false)
|
2014-12-10 22:03:56 +00:00
|
|
|
, mHitAudioDecodeError(false)
|
2015-01-11 20:43:11 +00:00
|
|
|
, mShutdown(false)
|
2015-06-09 00:16:42 +00:00
|
|
|
, mTaskQueueIsBorrowed(!!aBorrowedTaskQueue)
|
2014-06-18 05:07:02 +00:00
|
|
|
, mAudioDiscontinuity(false)
|
|
|
|
, mVideoDiscontinuity(false)
|
2010-04-02 03:03:07 +00:00
|
|
|
{
|
2012-11-14 19:46:40 +00:00
|
|
|
MOZ_COUNT_CTOR(MediaDecoderReader);
|
2015-06-09 19:41:24 +00:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
// Dispatch initialization that needs to happen on that task queue.
|
|
|
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderReader::InitializationTask);
|
|
|
|
mTaskQueue->Dispatch(r.forget());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDecoderReader::InitializationTask()
|
|
|
|
{
|
|
|
|
if (mDecoder->CanonicalDurationOrNull()) {
|
|
|
|
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
|
|
|
|
}
|
2015-06-17 21:22:10 +00:00
|
|
|
|
|
|
|
// Initialize watchers.
|
|
|
|
mWatchManager.Watch(mDuration, &MediaDecoderReader::UpdateBuffered);
|
2010-04-02 03:03:07 +00:00
|
|
|
}
|
|
|
|
|
2012-11-14 19:46:40 +00:00
|
|
|
MediaDecoderReader::~MediaDecoderReader()
|
2010-04-02 03:03:07 +00:00
|
|
|
{
|
2014-12-02 05:51:02 +00:00
|
|
|
MOZ_ASSERT(mShutdown);
|
2015-02-10 02:50:02 +00:00
|
|
|
MOZ_ASSERT(!mDecoder);
|
2010-04-02 03:03:07 +00:00
|
|
|
ResetDecode();
|
2012-11-14 19:46:40 +00:00
|
|
|
MOZ_COUNT_DTOR(MediaDecoderReader);
|
2010-04-02 03:03:07 +00:00
|
|
|
}
|
|
|
|
|
2014-03-19 21:33:12 +00:00
|
|
|
size_t MediaDecoderReader::SizeOfVideoQueueInBytes() const
|
|
|
|
{
|
|
|
|
VideoQueueMemoryFunctor functor;
|
|
|
|
mVideoQueue.LockedForEach(functor);
|
|
|
|
return functor.mSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
|
|
|
|
{
|
|
|
|
AudioQueueMemoryFunctor functor;
|
|
|
|
mAudioQueue.LockedForEach(functor);
|
|
|
|
return functor.mSize;
|
|
|
|
}
|
|
|
|
|
2014-12-17 23:41:19 +00:00
|
|
|
size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
|
|
|
|
{
|
|
|
|
return mVideoQueue.GetSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
|
|
|
|
{
|
|
|
|
return mAudioQueue.GetSize();
|
|
|
|
}
|
|
|
|
|
2012-11-14 19:46:40 +00:00
|
|
|
nsresult MediaDecoderReader::ResetDecode()
|
2010-04-02 03:03:07 +00:00
|
|
|
{
|
2012-12-06 23:27:08 +00:00
|
|
|
VideoQueue().Reset();
|
|
|
|
AudioQueue().Reset();
|
2010-04-02 03:03:07 +00:00
|
|
|
|
2014-06-18 05:07:02 +00:00
|
|
|
mAudioDiscontinuity = true;
|
|
|
|
mVideoDiscontinuity = true;
|
|
|
|
|
2015-02-14 00:53:34 +00:00
|
|
|
mBaseAudioPromise.RejectIfExists(CANCELED, __func__);
|
|
|
|
mBaseVideoPromise.RejectIfExists(CANCELED, __func__);
|
|
|
|
|
|
|
|
return NS_OK;
|
2010-04-02 03:03:07 +00:00
|
|
|
}
|
|
|
|
|
2015-06-30 08:03:08 +00:00
|
|
|
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
|
|
|
MediaDecoderReader::DecodeToFirstVideoData()
|
2014-08-22 03:11:58 +00:00
|
|
|
{
|
2015-06-30 08:03:08 +00:00
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
|
|
|
typedef MediaDecoderReader::VideoDataPromise PromiseType;
|
|
|
|
nsRefPtr<PromiseType::Private> p = new PromiseType::Private(__func__);
|
|
|
|
nsRefPtr<MediaDecoderReader> self = this;
|
|
|
|
InvokeUntil([self] () -> bool {
|
|
|
|
MOZ_ASSERT(self->OnTaskQueue());
|
|
|
|
NS_ENSURE_TRUE(!self->mShutdown, false);
|
|
|
|
bool skip = false;
|
|
|
|
if (!self->DecodeVideoFrame(skip, 0)) {
|
|
|
|
self->VideoQueue().Finish();
|
|
|
|
return !!self->VideoQueue().PeekFront();
|
2014-08-22 03:11:58 +00:00
|
|
|
}
|
2015-06-30 08:03:08 +00:00
|
|
|
return true;
|
|
|
|
}, [self] () -> bool {
|
|
|
|
MOZ_ASSERT(self->OnTaskQueue());
|
|
|
|
return self->VideoQueue().GetSize();
|
2015-07-16 18:31:21 +00:00
|
|
|
})->Then(OwnerThread(), __func__, [self, p] () {
|
2015-06-30 08:03:08 +00:00
|
|
|
p->Resolve(self->VideoQueue().PeekFront(), __func__);
|
|
|
|
}, [p] () {
|
|
|
|
// We don't have a way to differentiate EOS, error, and shutdown here. :-(
|
|
|
|
p->Reject(END_OF_STREAM, __func__);
|
|
|
|
});
|
|
|
|
|
|
|
|
return p.forget();
|
2014-08-22 03:11:58 +00:00
|
|
|
}
|
|
|
|
|
2014-11-12 04:50:20 +00:00
|
|
|
void
|
2015-06-17 21:22:10 +00:00
|
|
|
MediaDecoderReader::UpdateBuffered()
|
2015-06-19 20:45:09 +00:00
|
|
|
{
|
2015-06-17 21:22:10 +00:00
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
|
|
|
NS_ENSURE_TRUE_VOID(!mShutdown);
|
|
|
|
mBuffered = GetBuffered();
|
2015-06-19 20:45:09 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 20:45:09 +00:00
|
|
|
void
|
|
|
|
MediaDecoderReader::ThrottledNotifyDataArrived(const Interval<int64_t>& aInterval)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
|
|
|
NS_ENSURE_TRUE_VOID(!mShutdown);
|
|
|
|
|
|
|
|
if (mThrottledInterval.isNothing()) {
|
|
|
|
mThrottledInterval.emplace(aInterval);
|
2015-07-17 05:43:07 +00:00
|
|
|
} else if (mThrottledInterval.ref().Contains(aInterval)) {
|
|
|
|
return;
|
2015-06-19 20:45:09 +00:00
|
|
|
} else if (!mThrottledInterval.ref().Contiguous(aInterval)) {
|
|
|
|
DoThrottledNotify();
|
|
|
|
mThrottledInterval.emplace(aInterval);
|
|
|
|
} else {
|
|
|
|
mThrottledInterval = Some(mThrottledInterval.ref().Span(aInterval));
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's been long enough since our last update, do it.
|
|
|
|
if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) {
|
|
|
|
DoThrottledNotify();
|
|
|
|
} else if (!mThrottledNotify.Exists()) {
|
|
|
|
// Otherwise, schedule an update if one isn't scheduled already.
|
|
|
|
nsRefPtr<MediaDecoderReader> self = this;
|
|
|
|
mThrottledNotify.Begin(
|
|
|
|
mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__)
|
2015-07-16 18:31:21 +00:00
|
|
|
->Then(OwnerThread(), __func__,
|
2015-06-19 20:45:09 +00:00
|
|
|
[self] () -> void {
|
|
|
|
self->mThrottledNotify.Complete();
|
|
|
|
NS_ENSURE_TRUE_VOID(!self->mShutdown);
|
|
|
|
self->DoThrottledNotify();
|
|
|
|
},
|
|
|
|
[self] () -> void {
|
|
|
|
self->mThrottledNotify.Complete();
|
|
|
|
NS_WARNING("throttle callback rejected");
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDecoderReader::DoThrottledNotify()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
|
|
|
mLastThrottledNotify = TimeStamp::Now();
|
|
|
|
mThrottledNotify.DisconnectIfExists();
|
|
|
|
Interval<int64_t> interval = mThrottledInterval.ref();
|
|
|
|
mThrottledInterval.reset();
|
|
|
|
NotifyDataArrived(interval);
|
|
|
|
}
|
|
|
|
|
2015-05-18 06:15:47 +00:00
|
|
|
media::TimeIntervals
|
|
|
|
MediaDecoderReader::GetBuffered()
|
2014-06-10 07:31:09 +00:00
|
|
|
{
|
2015-06-17 21:22:10 +00:00
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
2015-07-22 10:20:53 +00:00
|
|
|
if (!HaveStartTime()) {
|
|
|
|
return media::TimeIntervals();
|
|
|
|
}
|
2014-11-12 04:50:21 +00:00
|
|
|
AutoPinned<MediaResource> stream(mDecoder->GetResource());
|
2015-06-09 19:41:24 +00:00
|
|
|
|
2015-06-23 16:06:35 +00:00
|
|
|
if (!mDuration.Ref().isSome()) {
|
2015-06-09 19:41:24 +00:00
|
|
|
return TimeIntervals();
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
2015-06-09 19:41:24 +00:00
|
|
|
|
2015-06-23 16:06:35 +00:00
|
|
|
return GetEstimatedBufferedTimeRanges(stream, mDuration.Ref().ref().ToMicroseconds());
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
2010-08-13 02:28:15 +00:00
|
|
|
|
2015-03-20 19:53:32 +00:00
|
|
|
nsRefPtr<MediaDecoderReader::MetadataPromise>
|
2015-05-01 05:37:29 +00:00
|
|
|
MediaDecoderReader::AsyncReadMetadata()
|
2015-03-20 19:53:32 +00:00
|
|
|
{
|
|
|
|
typedef ReadMetadataFailureReason Reason;
|
|
|
|
|
2015-03-17 22:16:06 +00:00
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
2015-03-20 19:53:32 +00:00
|
|
|
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
|
2015-05-01 05:37:29 +00:00
|
|
|
DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
|
2015-03-20 19:53:32 +00:00
|
|
|
|
|
|
|
if (IsWaitingMediaResources()) {
|
|
|
|
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to read the metadata.
|
|
|
|
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
|
|
|
|
nsresult rv = ReadMetadata(&metadata->mInfo, getter_Transfers(metadata->mTags));
|
|
|
|
|
2015-05-01 05:37:29 +00:00
|
|
|
// Reading metadata can cause us to discover that we need resources (a hardware
|
|
|
|
// resource initialized but not yet ready for use).
|
2015-03-20 19:53:32 +00:00
|
|
|
if (IsWaitingMediaResources()) {
|
|
|
|
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We're not waiting for anything. If we didn't get the metadata, that's an
|
|
|
|
// error.
|
|
|
|
if (NS_FAILED(rv) || !metadata->mInfo.HasValidMedia()) {
|
|
|
|
DECODER_WARN("ReadMetadata failed, rv=%x HasValidMedia=%d", rv, metadata->mInfo.HasValidMedia());
|
|
|
|
return MetadataPromise::CreateAndReject(Reason::METADATA_ERROR, __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Success!
|
|
|
|
return MetadataPromise::CreateAndResolve(metadata, __func__);
|
|
|
|
}
|
|
|
|
|
2015-02-14 00:53:34 +00:00
|
|
|
class ReRequestVideoWithSkipTask : public nsRunnable
|
|
|
|
{
|
2014-06-18 05:07:02 +00:00
|
|
|
public:
|
2015-02-14 00:53:34 +00:00
|
|
|
ReRequestVideoWithSkipTask(MediaDecoderReader* aReader,
|
|
|
|
int64_t aTimeThreshold)
|
2014-06-18 05:07:02 +00:00
|
|
|
: mReader(aReader)
|
|
|
|
, mTimeThreshold(aTimeThreshold)
|
|
|
|
{
|
|
|
|
}
|
2015-02-14 00:53:34 +00:00
|
|
|
|
|
|
|
NS_METHOD Run()
|
|
|
|
{
|
2015-06-09 00:26:42 +00:00
|
|
|
MOZ_ASSERT(mReader->OnTaskQueue());
|
2015-02-14 00:53:34 +00:00
|
|
|
|
|
|
|
// Make sure ResetDecode hasn't been called in the mean time.
|
|
|
|
if (!mReader->mBaseVideoPromise.IsEmpty()) {
|
2015-07-05 16:39:30 +00:00
|
|
|
mReader->RequestVideoData(/* aSkip = */ true, mTimeThreshold,
|
|
|
|
/* aForceDecodeAhead = */ false);
|
2015-02-14 00:53:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<MediaDecoderReader> mReader;
|
|
|
|
const int64_t mTimeThreshold;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ReRequestAudioTask : public nsRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ReRequestAudioTask(MediaDecoderReader* aReader)
|
|
|
|
: mReader(aReader)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_METHOD Run()
|
|
|
|
{
|
2015-06-09 00:26:42 +00:00
|
|
|
MOZ_ASSERT(mReader->OnTaskQueue());
|
2015-02-14 00:53:34 +00:00
|
|
|
|
|
|
|
// Make sure ResetDecode hasn't been called in the mean time.
|
|
|
|
if (!mReader->mBaseAudioPromise.IsEmpty()) {
|
|
|
|
mReader->RequestAudioData();
|
|
|
|
}
|
|
|
|
|
2014-06-18 05:07:02 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-02-14 00:53:34 +00:00
|
|
|
|
2014-06-18 05:07:02 +00:00
|
|
|
private:
|
|
|
|
nsRefPtr<MediaDecoderReader> mReader;
|
|
|
|
};
|
|
|
|
|
2014-12-10 22:03:56 +00:00
|
|
|
nsRefPtr<MediaDecoderReader::VideoDataPromise>
|
2014-06-18 05:07:02 +00:00
|
|
|
MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
|
2015-07-05 16:39:30 +00:00
|
|
|
int64_t aTimeThreshold,
|
|
|
|
bool aForceDecodeAhead)
|
2014-06-18 05:07:02 +00:00
|
|
|
{
|
2014-12-10 22:03:56 +00:00
|
|
|
nsRefPtr<VideoDataPromise> p = mBaseVideoPromise.Ensure(__func__);
|
2014-06-18 05:07:02 +00:00
|
|
|
bool skip = aSkipToNextKeyframe;
|
|
|
|
while (VideoQueue().GetSize() == 0 &&
|
|
|
|
!VideoQueue().IsFinished()) {
|
|
|
|
if (!DecodeVideoFrame(skip, aTimeThreshold)) {
|
|
|
|
VideoQueue().Finish();
|
|
|
|
} else if (skip) {
|
|
|
|
// We still need to decode more data in order to skip to the next
|
|
|
|
// keyframe. Post another task to the decode task queue to decode
|
|
|
|
// again. We don't just decode straight in a loop here, as that
|
|
|
|
// would hog the decode task queue.
|
2015-02-14 00:53:34 +00:00
|
|
|
RefPtr<nsIRunnable> task(new ReRequestVideoWithSkipTask(this, aTimeThreshold));
|
2015-05-01 13:14:16 +00:00
|
|
|
mTaskQueue->Dispatch(task.forget());
|
2014-12-10 22:03:56 +00:00
|
|
|
return p;
|
2014-06-10 07:31:09 +00:00
|
|
|
}
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
|
|
|
if (VideoQueue().GetSize() > 0) {
|
2014-11-19 21:01:10 +00:00
|
|
|
nsRefPtr<VideoData> v = VideoQueue().PopFront();
|
2014-06-18 05:07:02 +00:00
|
|
|
if (v && mVideoDiscontinuity) {
|
|
|
|
v->mDiscontinuity = true;
|
|
|
|
mVideoDiscontinuity = false;
|
2010-08-13 02:28:15 +00:00
|
|
|
}
|
2014-12-10 22:03:56 +00:00
|
|
|
mBaseVideoPromise.Resolve(v, __func__);
|
2014-06-18 05:07:02 +00:00
|
|
|
} else if (VideoQueue().IsFinished()) {
|
2014-12-10 22:03:56 +00:00
|
|
|
mBaseVideoPromise.Reject(END_OF_STREAM, __func__);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(false, "Dropping this promise on the floor");
|
2010-08-13 02:28:15 +00:00
|
|
|
}
|
2014-12-10 22:03:56 +00:00
|
|
|
|
|
|
|
return p;
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
2013-08-13 04:49:25 +00:00
|
|
|
|
2014-12-10 22:03:56 +00:00
|
|
|
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
2014-06-18 05:07:02 +00:00
|
|
|
MediaDecoderReader::RequestAudioData()
|
|
|
|
{
|
2014-12-10 22:03:56 +00:00
|
|
|
nsRefPtr<AudioDataPromise> p = mBaseAudioPromise.Ensure(__func__);
|
2014-06-18 05:07:02 +00:00
|
|
|
while (AudioQueue().GetSize() == 0 &&
|
|
|
|
!AudioQueue().IsFinished()) {
|
|
|
|
if (!DecodeAudioData()) {
|
|
|
|
AudioQueue().Finish();
|
2014-07-30 18:58:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// AudioQueue size is still zero, post a task to try again. Don't spin
|
|
|
|
// waiting in this while loop since it somehow prevents audio EOS from
|
|
|
|
// coming in gstreamer 1.x when there is still video buffer waiting to be
|
|
|
|
// consumed. (|mVideoSinkBufferCount| > 0)
|
|
|
|
if (AudioQueue().GetSize() == 0 && mTaskQueue) {
|
2015-02-14 00:53:34 +00:00
|
|
|
RefPtr<nsIRunnable> task(new ReRequestAudioTask(this));
|
2015-05-01 13:14:16 +00:00
|
|
|
mTaskQueue->Dispatch(task.forget());
|
2014-12-10 22:03:56 +00:00
|
|
|
return p;
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (AudioQueue().GetSize() > 0) {
|
2014-11-19 21:01:10 +00:00
|
|
|
nsRefPtr<AudioData> a = AudioQueue().PopFront();
|
2014-06-18 05:07:02 +00:00
|
|
|
if (mAudioDiscontinuity) {
|
|
|
|
a->mDiscontinuity = true;
|
|
|
|
mAudioDiscontinuity = false;
|
2014-06-13 20:20:37 +00:00
|
|
|
}
|
2014-12-10 22:03:56 +00:00
|
|
|
mBaseAudioPromise.Resolve(a, __func__);
|
2014-06-18 05:07:02 +00:00
|
|
|
} else if (AudioQueue().IsFinished()) {
|
2014-12-10 22:03:56 +00:00
|
|
|
mBaseAudioPromise.Reject(mHitAudioDecodeError ? DECODE_ERROR : END_OF_STREAM, __func__);
|
|
|
|
mHitAudioDecodeError = false;
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(false, "Dropping this promise on the floor");
|
2014-06-10 07:31:09 +00:00
|
|
|
}
|
2014-12-10 22:03:56 +00:00
|
|
|
|
|
|
|
return p;
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
MediaDecoderReader::BreakCycles()
|
|
|
|
{
|
2015-06-11 03:08:59 +00:00
|
|
|
// Nothing left to do here these days. We keep this method around so that, if
|
|
|
|
// we need it, we don't have to make all of the subclass implementations call
|
|
|
|
// the superclass method again.
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
|
|
|
|
2014-12-09 19:43:21 +00:00
|
|
|
nsRefPtr<ShutdownPromise>
|
2014-06-18 05:07:02 +00:00
|
|
|
MediaDecoderReader::Shutdown()
|
|
|
|
{
|
2015-03-17 22:16:06 +00:00
|
|
|
MOZ_ASSERT(OnTaskQueue());
|
2014-12-02 05:51:02 +00:00
|
|
|
mShutdown = true;
|
2014-12-10 22:03:56 +00:00
|
|
|
|
|
|
|
mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
|
|
|
|
mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
|
|
|
|
|
2015-06-19 20:45:09 +00:00
|
|
|
mThrottledNotify.DisconnectIfExists();
|
|
|
|
|
2014-06-18 05:07:02 +00:00
|
|
|
ReleaseMediaResources();
|
2015-06-09 19:41:24 +00:00
|
|
|
mDuration.DisconnectIfConnected();
|
2015-06-17 21:22:10 +00:00
|
|
|
mBuffered.DisconnectAll();
|
|
|
|
|
|
|
|
// Shut down the watch manager before shutting down our task queue.
|
|
|
|
mWatchManager.Shutdown();
|
2015-06-09 19:41:24 +00:00
|
|
|
|
2014-12-09 19:43:21 +00:00
|
|
|
nsRefPtr<ShutdownPromise> p;
|
|
|
|
|
|
|
|
// Spin down the task queue if necessary. We wait until BreakCycles to null
|
|
|
|
// out mTaskQueue, since otherwise any remaining tasks could crash when they
|
2015-06-09 00:26:42 +00:00
|
|
|
// invoke OnTaskQueue().
|
2014-12-02 05:51:03 +00:00
|
|
|
if (mTaskQueue && !mTaskQueueIsBorrowed) {
|
2014-12-09 19:43:21 +00:00
|
|
|
// If we own our task queue, shutdown ends when the task queue is done.
|
|
|
|
p = mTaskQueue->BeginShutdown();
|
|
|
|
} else {
|
|
|
|
// If we don't own our task queue, we resolve immediately (though
|
|
|
|
// asynchronously).
|
2015-02-10 02:38:11 +00:00
|
|
|
p = ShutdownPromise::CreateAndResolve(true, __func__);
|
2014-12-02 05:51:03 +00:00
|
|
|
}
|
2014-12-09 19:43:21 +00:00
|
|
|
|
2015-06-19 20:45:09 +00:00
|
|
|
mTimer = nullptr;
|
2015-02-10 02:50:02 +00:00
|
|
|
mDecoder = nullptr;
|
|
|
|
|
2014-12-09 19:43:21 +00:00
|
|
|
return p;
|
2014-06-18 05:07:02 +00:00
|
|
|
}
|
|
|
|
|
2012-11-14 19:45:33 +00:00
|
|
|
} // namespace mozilla
|
2015-03-20 19:53:32 +00:00
|
|
|
|
|
|
|
#undef DECODER_LOG
|
|
|
|
#undef DECODER_WARN
|
|
|
|
#undef DECODER_WARN_HELPER
|