Bug 1002297 - Seek in unbuffered ranges with MSE - r=kinetik

This commit is contained in:
Chris Double 2014-06-02 22:25:42 +00:00
parent 74c49d3d46
commit 1d1a56c87a
6 changed files with 92 additions and 13 deletions

View File

@ -3064,6 +3064,14 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
return;
}
// Section 2.4.3.1 of the Media Source Extensions spec requires
// changing to HAVE_METADATA when seeking into an unbuffered
// range.
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
return;
}
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_METADATA &&
mDownloadSuspendedByCache &&
mDecoder &&

View File

@ -119,6 +119,9 @@ public:
NEXT_FRAME_UNAVAILABLE_BUFFERING,
// The next frame of audio/video is unavailable for some other reasons
NEXT_FRAME_UNAVAILABLE,
// The next frame is unavailable due to waiting for more Media Source
// Extensions data to become available.
NEXT_FRAME_WAIT_FOR_MSE_DATA,
// Sentinel value
NEXT_FRAME_UNINITIALIZED
};

View File

@ -299,6 +299,7 @@ MediaSource::MediaSource(nsPIDOMWindow* aWindow)
, mDuration(UnspecifiedNaN<double>())
, mDecoder(nullptr)
, mReadyState(MediaSourceReadyState::Closed)
, mWaitForDataMonitor("MediaSource.WaitForData.Monitor")
{
mSourceBuffers = new SourceBufferList(this);
mActiveSourceBuffers = new SourceBufferList(this);
@ -395,6 +396,20 @@ MediaSource::NotifyEvicted(double aStart, double aEnd)
mSourceBuffers->Evict(aStart, aEnd);
}
void
MediaSource::WaitForData()
{
MonitorAutoLock lock(mWaitForDataMonitor);
lock.Wait();
}
void
MediaSource::NotifyGotData()
{
MonitorAutoLock lock(mWaitForDataMonitor);
lock.NotifyAll();
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
mSourceBuffers, mActiveSourceBuffers)

View File

@ -13,6 +13,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/MediaSourceBinding.h"
#include "mozilla/Monitor.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionNoteChild.h"
@ -89,6 +90,12 @@ public:
// that were evicted are provided.
void NotifyEvicted(double aStart, double aEnd);
// Block thread waiting for data to be appended to a SourceBuffer.
void WaitForData();
// Unblock threads waiting for data to be appended to a SourceBuffer.
void NotifyGotData();
private:
explicit MediaSource(nsPIDOMWindow* aWindow);
@ -106,6 +113,10 @@ private:
nsRefPtr<MediaSourceDecoder> mDecoder;
MediaSourceReadyState mReadyState;
// Monitor for waiting for when new data is appended to
// a Source Buffer.
Monitor mWaitForDataMonitor;
};
NS_DEFINE_STATIC_IID_ACCESSOR(MediaSource, MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID)

View File

@ -130,6 +130,11 @@ public:
void InitializePendingDecoders();
bool IsShutdown() {
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
return mDecoder->IsShutdown();
}
private:
bool MaybeSwitchVideoReaders(int64_t aTimeThreshold) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@ -383,25 +388,60 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType, MediaSourceDecoder*
return decoder.forget();
}
namespace {
class ChangeToHaveMetadata : public nsRunnable {
public:
ChangeToHaveMetadata(AbstractMediaDecoder* aDecoder) :
mDecoder(aDecoder)
{
}
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
auto owner = mDecoder->GetOwner();
if (owner) {
owner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_WAIT_FOR_MSE_DATA);
}
return NS_OK;
}
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
};
}
nsresult
MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
int64_t aCurrentTime)
{
ResetDecode();
if (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)) {
NS_DispatchToMainThread(new ChangeToHaveMetadata(mDecoder));
}
dom::SourceBufferList* sbl = mMediaSource->ActiveSourceBuffers();
if (sbl->AllContainsTime (aTime / USECS_PER_S)) {
if (GetAudioReader()) {
nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
// Loop until we have the requested time range in the source buffers.
// This is a workaround for our lack of async functionality in the
// MediaDecoderStateMachine. Bug 979104 implements what we need and
// we'll remove this for an async approach based on that in bug XXXXXXX.
while (!mMediaSource->ActiveSourceBuffers()->AllContainsTime (aTime / USECS_PER_S)
&& !IsShutdown()) {
mMediaSource->WaitForData();
MaybeSwitchVideoReaders(aTime);
}
if (IsShutdown()) {
return NS_OK;
}
ResetDecode();
if (GetAudioReader()) {
nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
if (GetVideoReader()) {
nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
}
if (GetVideoReader()) {
nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;

View File

@ -493,6 +493,8 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
// Schedule the state machine thread to ensure playback starts
// if required when data is appended.
mMediaSource->GetDecoder()->ScheduleStateMachineThread();
mMediaSource->NotifyGotData();
}
void