Bug 1456743 - P4. Actual implementation of SourceBuffer.changeType. r=bryce

Currently, the new init segment provided following a call to changeType() must contain the same number of audio and video tracks as previously.

The Chrome team has indicated concerns in regards to this restriction. TBD.

MozReview-Commit-ID: 3S6YVtQILF9

--HG--
extra : rebase_source : 59574301d8d4b6f04fc40a97a0917222f1d42fe4
This commit is contained in:
Jean-Yves Avenard 2018-04-30 19:16:31 +02:00
Родитель 5616b876de
Коммит 97cad9b3dd
4 изменённых файлов: 108 добавлений и 14 удалений

Просмотреть файл

@ -301,6 +301,47 @@ void
SourceBuffer::ChangeType(const nsAString& aType, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
DecoderDoctorDiagnostics diagnostics;
nsresult rv = MediaSource::IsTypeSupported(aType, &diagnostics);
diagnostics.StoreFormatDiagnostics(mMediaSource->GetOwner()
? mMediaSource->GetOwner()->GetExtantDoc()
: nullptr,
aType, NS_SUCCEEDED(rv), __func__);
MSE_API("ChangeType(aType=%s)%s",
NS_ConvertUTF16toUTF8(aType).get(),
rv == NS_OK ? "" : " [not supported]");
if (NS_FAILED(rv)) {
DDLOG(DDLogCategory::API, "ChangeType", rv);
aRv.Throw(rv);
return;
}
if (!mMediaSource->GetDecoder() ||
mMediaSource->GetDecoder()->OwnerHasError()) {
MSE_DEBUG("HTMLMediaElement.error is not null");
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (!IsAttached() || mUpdating) {
DDLOG(DDLogCategory::API, "ChangeType", NS_ERROR_DOM_INVALID_STATE_ERR);
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
}
if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
DDLOG(DDLogCategory::API, "ChangeType", NS_ERROR_DOM_INVALID_STATE_ERR);
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
MOZ_ASSERT(containerType);
mType = *containerType;
ResetParserState();
mTrackBuffersManager->ChangeType(mType);
}
void

Просмотреть файл

@ -24,7 +24,8 @@ public:
Reset,
RangeRemoval,
EvictData,
Detach
Detach,
ChangeType
};
typedef Pair<bool, SourceBufferAttributes> AppendBufferResult;
@ -112,6 +113,20 @@ public:
const char* GetTypeName() const override { return "Detach"; }
};
class ChangeTypeTask : public SourceBufferTask {
public:
explicit ChangeTypeTask(const MediaContainerType& aType)
: mType(aType)
{
}
static const Type sType = Type::ChangeType;
Type GetType() const override { return Type::ChangeType; }
const char* GetTypeName() const override { return "ChangeType"; }
const MediaContainerType mType;
};
} // end mozilla namespace
#endif

Просмотреть файл

@ -111,6 +111,7 @@ TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
: mInputBuffer(new MediaByteBuffer)
, mBufferFull(false)
, mFirstInitializationSegmentReceived(false)
, mChangeTypeReceived(false)
, mNewMediaSegmentStarted(false)
, mActiveTrack(false)
, mType(aType)
@ -280,6 +281,14 @@ TrackBuffersManager::ProcessTasks()
ShutdownDemuxers();
ResetTaskQueue();
return;
case Type::ChangeType:
MOZ_RELEASE_ASSERT(!mCurrentTask);
mType = task->As<ChangeTypeTask>()->mType;
mChangeTypeReceived = true;
mInitData = nullptr;
CompleteResetParserState();
CreateDemuxerforMIMEType();
break;
default:
NS_WARNING("Invalid Task");
}
@ -385,6 +394,15 @@ TrackBuffersManager::EvictData(const TimeUnit& aPlaybackTime, int64_t aSize)
return result;
}
void
TrackBuffersManager::ChangeType(const MediaContainerType& aType)
{
MOZ_ASSERT(NS_IsMainThread());
QueueTask(new ChangeTypeTask(aType));
}
TimeIntervals
TrackBuffersManager::Buffered() const
{
@ -475,10 +493,13 @@ TrackBuffersManager::CompleteResetParserState()
}
// We could be left with a demuxer in an unusable state. It needs to be
// recreated. We store in the InputBuffer an init segment which will be parsed
// during the next Segment Parser Loop and a new demuxer will be created and
// initialized.
if (mFirstInitializationSegmentReceived) {
// recreated. Unless we have a pending changeType operation, we store in the
// InputBuffer an init segment which will be parsed during the next Segment
// Parser Loop and a new demuxer will be created and initialized.
// If we are in the middle of a changeType operation, then we do not have an
// init segment yet. The next appendBuffer operation will need to provide such
// init segment.
if (mFirstInitializationSegmentReceived && !mChangeTypeReceived) {
MOZ_ASSERT(mInitData && mInitData->Length(), "we must have an init segment");
// The aim here is really to destroy our current demuxer.
CreateDemuxerforMIMEType();
@ -486,8 +507,10 @@ TrackBuffersManager::CompleteResetParserState()
// to mInputBuffer as it will get modified in the Segment Parser Loop.
mInputBuffer = new MediaByteBuffer;
mInputBuffer->AppendElements(*mInitData);
}
RecreateParser(true);
} else {
RecreateParser(false);
}
}
int64_t
@ -721,7 +744,7 @@ TrackBuffersManager::SegmentParserLoop()
MediaResult haveInitSegment = mParser->IsInitSegmentPresent(mInputBuffer);
if (NS_SUCCEEDED(haveInitSegment)) {
SetAppendState(AppendState::PARSING_INIT_SEGMENT);
if (mFirstInitializationSegmentReceived) {
if (mFirstInitializationSegmentReceived && !mChangeTypeReceived) {
// This is a new initialization segment. Obsolete the old one.
RecreateParser(false);
}
@ -772,8 +795,12 @@ TrackBuffersManager::SegmentParserLoop()
return;
}
if (mSourceBufferAttributes->GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT) {
// 1. If the first initialization segment received flag is false, then run the append error algorithm with the decode error parameter set to true and abort this algorithm.
if (!mFirstInitializationSegmentReceived) {
// 1. If the first initialization segment received flag is false, then run
// the append error algorithm with the decode error parameter set to
// true and abort this algorithm.
// Or we are in the process of changeType, in which case we must first
// get an init segment before getting a media segment.
if (!mFirstInitializationSegmentReceived || mChangeTypeReceived) {
RejectAppend(NS_ERROR_FAILURE, __func__);
return;
}
@ -1108,13 +1135,17 @@ TrackBuffersManager::OnDemuxerInitDone(const MediaResult& aResult)
if (mFirstInitializationSegmentReceived) {
if (numVideos != mVideoTracks.mNumTracks ||
numAudios != mAudioTracks.mNumTracks ||
(numVideos && info.mVideo.mMimeType != mVideoTracks.mInfo->mMimeType) ||
(numAudios && info.mAudio.mMimeType != mAudioTracks.mInfo->mMimeType)) {
(!mChangeTypeReceived &&
((numVideos &&
info.mVideo.mMimeType != mVideoTracks.mInfo->mMimeType) ||
(numAudios &&
info.mAudio.mMimeType != mAudioTracks.mInfo->mMimeType)))) {
RejectAppend(NS_ERROR_FAILURE, __func__);
return;
}
// 1. If more than one track for a single type are present (ie 2 audio tracks),
// then the Track IDs match the ones in the first initialization segment.
// 1. If more than one track for a single type are present (ie 2 audio
// tracks), then the Track IDs match the ones in the first initialization
// segment.
// TODO
// 2. Add the appropriate track descriptions from this initialization
// segment to each of the track buffers.
@ -1214,6 +1245,9 @@ TrackBuffersManager::OnDemuxerInitDone(const MediaResult& aResult)
mVideoTracks.mLastInfo = new TrackInfoSharedPtr(info.mVideo, streamID);
}
// We have now completed the changeType operation.
mChangeTypeReceived = false;
UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
if (crypto && crypto->IsEncrypted()) {
// Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.

Просмотреть файл

@ -115,6 +115,9 @@ public:
// and if still more space is needed remove from the end.
EvictDataResult EvictData(const media::TimeUnit& aPlaybackTime, int64_t aSize);
// Queue a task to run ChangeType
void ChangeType(const MediaContainerType& aType);
// Returns the buffered range currently managed.
// This may be called on any thread.
// Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
@ -205,10 +208,11 @@ private:
// Accessed on both the main thread and the task queue.
Atomic<bool> mBufferFull;
bool mFirstInitializationSegmentReceived;
bool mChangeTypeReceived;
// Set to true once a new segment is started.
bool mNewMediaSegmentStarted;
bool mActiveTrack;
const MediaContainerType mType;
MediaContainerType mType;
// ContainerParser objects and methods.
// Those are used to parse the incoming input buffer.