diff --git a/gecko-media/gecko/glue/GeckoMediaDecoder.cpp b/gecko-media/gecko/glue/GeckoMediaDecoder.cpp index 5d34fb6..d3740fe 100644 --- a/gecko-media/gecko/glue/GeckoMediaDecoder.cpp +++ b/gecko-media/gecko/glue/GeckoMediaDecoder.cpp @@ -37,7 +37,6 @@ GeckoMediaDecoder::GeckoMediaDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) , mGeckoWatchManager(this, aInit.mOwner->AbstractMainThread()) { - mExplicitDuration.emplace(UnspecifiedNaN()); mGeckoWatchManager.Watch(mBuffered, &GeckoMediaDecoder::NotifyBuffered); } diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index e2ea847..9cef7cc 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -85,7 +85,7 @@ MediaSource::SourceBuffers() MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD(GetSourceBuffers, nullptr); - size_t* id = (*mImpl.mGetSourceBuffers)(mImpl.mContext); + size_t* id = CALLBACK_CALL(GetSourceBuffers); if (NS_WARN_IF(!id)) { return nullptr; } diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index b37664d..54deb0f 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -35,12 +35,16 @@ SourceBuffer::SourceBuffer(GeckoMediaSourceBufferImpl aImpl, mMediaSource = GetMediaSource(aParentId); MOZ_ASSERT(mMediaSource); - mTrackBuffersManager = - new TrackBuffersManager(mMediaSource->GetDecoder(), mime.value()); + RefPtr decoder = mMediaSource->GetDecoder(); + if (NS_WARN_IF(!decoder)) { + return; + } + + mTrackBuffersManager = new TrackBuffersManager(decoder, mime.value()); MSE_DEBUG("Create mTrackBuffersManager=%p", mTrackBuffersManager.get()); - mMediaSource->GetDecoder()->GetDemuxer()->AttachSourceBuffer(mTrackBuffersManager); + decoder->GetDemuxer()->AttachSourceBuffer(mTrackBuffersManager); } media::TimeIntervals diff --git a/gecko-media/gecko/glue/include/MediaSource.h b/gecko-media/gecko/glue/include/MediaSource.h index b995cb5..beb9798 100644 --- a/gecko-media/gecko/glue/include/MediaSource.h +++ b/gecko-media/gecko/glue/include/MediaSource.h @@ -33,7 +33,6 @@ class MediaSource final MediaSourceReadyState ReadyState(); // Attach this MediaSource to Decoder aDecoder. Returns false if already attached. - // FIXME This will likely get a GeckoMediaSourceDecoder at some point. bool Attach(MediaSourceDecoder* aDecoder); void Detach(); diff --git a/gecko-media/gecko/test/test.cpp b/gecko-media/gecko/test/test.cpp index f1fc934..ac5b3be 100644 --- a/gecko-media/gecko/test/test.cpp +++ b/gecko-media/gecko/test/test.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include #include @@ -18,6 +20,7 @@ #include "MediaData.h" #include "MediaDecoder.h" #include "MediaSource.h" +#include "MediaSourceDecoder.h" #include "MediaStreamGraph.h" #include "PlatformDecoderModule.h" #include "VideoUtils.h" @@ -553,31 +556,201 @@ TestGeckoDecoder() decoder->Shutdown(); } -struct DummyMediaSource +/** SourceBuffer **/ + +void +SourceBufferFree(void* aContext); + +class DummySourceBuffer +{ +public: + NS_INLINE_DECL_REFCOUNTING(DummySourceBuffer) + + DummySourceBuffer(size_t aParentId) + : mId(std::rand() % 1000 + std::time(0)) + , mReleased(false) + { + mImpl = { + this, + &SourceBufferFree, + }; + GeckoMedia_SourceBuffer_Create(mId, mImpl, aParentId, "video/mp4", true); + } + + GeckoMediaSourceBufferImpl mImpl; + size_t mId; + double mReleased; + +private: + ~DummySourceBuffer() + { + MOZ_ASSERT(mReleased == false); + GeckoMedia_SourceBuffer_Shutdown(mId); + MOZ_ASSERT(mReleased == true); + MOZ_ASSERT(GetSourceBuffer(mId) == nullptr); + }; +}; + +void +SourceBufferFree(void* aContext) +{ + static_cast(aContext)->mReleased = true; +} + +/** SourceBufferList **/ + +bool +IndexedGetter(void* aContext, uint32_t aIndex, size_t* aId); +uint32_t +Length(void* aContext); +void +SourceBufferListFree(void* aContext); + +class DummySourceBufferList +{ +public: + NS_INLINE_DECL_REFCOUNTING(DummySourceBufferList) + + DummySourceBufferList() + : mId(std::rand() % 1000 + std::time(0)) + , mReleased(false) + { + mImpl = { + this, + &SourceBufferListFree, + &IndexedGetter, + &Length, + }; + GeckoMedia_SourceBufferList_Create(mId, mImpl); + } + + GeckoMediaSourceBufferListImpl mImpl; + size_t mId; + double mReleased; + nsTArray mSourceBuffers; + +private: + ~DummySourceBufferList() + { + MOZ_ASSERT(mReleased == false); + GeckoMedia_SourceBufferList_Shutdown(mId); + MOZ_ASSERT(mReleased == true); + MOZ_ASSERT(GetSourceBufferList(mId) == nullptr); + }; +}; + +bool +IndexedGetter(void* aContext, uint32_t aIndex, size_t* aId) +{ + DummySourceBufferList* sbl = static_cast(aContext); + if (aIndex > sbl->mSourceBuffers.Length()) { + return false; + } + *aId = sbl->mSourceBuffers[aIndex]->mId; + return true; +} + +uint32_t +Length(void* aContext) +{ + return static_cast(aContext)->mSourceBuffers.Length(); +} + +void +SourceBufferListFree(void* aContext) +{ + DummySourceBufferList* sbl = static_cast(aContext); + sbl->mReleased = true; +} + +/** MediaSource **/ + +double +GetDuration(void* aContext); +MediaSourceReadyState +GetReadyState(void* aContext); +void +SetReadyState(void* aContext, MediaSourceReadyState aState); +bool +HasLiveSeekableRange(void* aContext); +GeckoMediaTimeInterval +LiveSeekableRange(void* aContext); +void +MediaSourceFree(void* aContext); +size_t* +GetSourceBuffers(void* aContext); +size_t* +GetActiveSourceBuffers(void* aContext); +void +ClearSourceBuffers(void* aContext); +void +ClearActiveSourceBuffers(void* aContext); + +class DummyMediaSource { - size_t id; - double released; - double duration; - MediaSourceReadyState readyState; - GeckoMediaTimeInterval liveSeekableRange; +public: + NS_INLINE_DECL_REFCOUNTING(DummyMediaSource) + + DummyMediaSource() + : mId(std::rand() % 1000 + std::time(0)) + , mReleased(false) + , mDuration(1.0) + , mReadyState(MediaSourceReadyState::Closed) + , mLiveSeekableRange(GeckoMediaTimeInterval{ 1.0, 1.0 }) + , mSourceBuffers(new DummySourceBufferList()) + , mActiveSourceBuffers(new DummySourceBufferList()) + { + mImpl = { + this, + &MediaSourceFree, + &GetReadyState, + &SetReadyState, + &GetDuration, + &HasLiveSeekableRange, + &LiveSeekableRange, + &GetSourceBuffers, + &GetActiveSourceBuffers, + &ClearSourceBuffers, + &ClearActiveSourceBuffers, + }; + GeckoMedia_MediaSource_Create(mId, mImpl); + } + + GeckoMediaSourceImpl mImpl; + size_t mId; + double mReleased; + double mDuration; + MediaSourceReadyState mReadyState; + GeckoMediaTimeInterval mLiveSeekableRange; + RefPtr mSourceBuffers; + RefPtr mActiveSourceBuffers; + +private: + ~DummyMediaSource() + { + MOZ_ASSERT(mReleased == false); + GeckoMedia_MediaSource_Shutdown(mId); + MOZ_ASSERT(mReleased == true); + MOZ_ASSERT(GetMediaSource(mId) == nullptr); + }; }; double GetDuration(void* aContext) { - return static_cast(aContext)->duration; + return static_cast(aContext)->mDuration; } MediaSourceReadyState GetReadyState(void* aContext) { - return static_cast(aContext)->readyState; + return static_cast(aContext)->mReadyState; } void SetReadyState(void* aContext, MediaSourceReadyState aState) { - static_cast(aContext)->readyState = aState; + static_cast(aContext)->mReadyState = aState; } bool @@ -589,147 +762,126 @@ HasLiveSeekableRange(void* aContext) GeckoMediaTimeInterval LiveSeekableRange(void* aContext) { - return static_cast(aContext)->liveSeekableRange; + return static_cast(aContext)->mLiveSeekableRange; } void MediaSourceFree(void* aContext) { - static_cast(aContext)->released = true; + static_cast(aContext)->mReleased = true; } -void -TestGeckoMediaSource() +size_t* +GetSourceBuffers(void* aContext) { - using namespace media; - double start = 0.5; - double end = 1.0; - TimeInterval interval(TimeUnit::FromSeconds(start), - TimeUnit::FromSeconds(end)); - - DummyMediaSource test1{ 1111, /* id */ - false, /* released */ - 1.0, /* duration */ - MediaSourceReadyState::Closed, - GeckoMediaTimeInterval{ start, end } }; - GeckoMediaSourceImpl test1_impl{ - &test1, /* mContext */ - &MediaSourceFree, /* mFree */ - &GetReadyState, /* mGetReadyState */ - &SetReadyState, /* mSetReadyState */ - &GetDuration, /* mGetDuration */ - &HasLiveSeekableRange, /* mHasLiveSeekableRange */ - &LiveSeekableRange /* mGetLiveSeekableRange */ - }; - MOZ_ASSERT(GetMediaSource(test1.id) == nullptr); - GeckoMedia_MediaSource_Create(test1.id, test1_impl); - auto mediaSource = GetMediaSource(test1.id); - MOZ_ASSERT(mediaSource != nullptr); - MOZ_ASSERT(mediaSource->Duration() == test1.duration); - MOZ_ASSERT(mediaSource->ReadyState() == test1.readyState); - MOZ_ASSERT(mediaSource->HasLiveSeekableRange()); - MOZ_ASSERT(mediaSource->LiveSeekableRange() == interval); - MOZ_ASSERT(test1.released == false); - GeckoMedia_MediaSource_Shutdown(test1.id); - MOZ_ASSERT(test1.released == true); - MOZ_ASSERT(GetMediaSource(test1.id) == nullptr); + return &(static_cast(aContext)->mSourceBuffers->mId); } -struct DummySourceBuffer +size_t* +GetActiveSourceBuffers(void* aContext) { - size_t id; - double released; -}; + return &(static_cast(aContext)->mActiveSourceBuffers->mId); +} void -SourceBufferFree(void* aContext) +ClearSourceBuffers(void* aContext) { - static_cast(aContext)->released = true; + static_cast(aContext) + ->mSourceBuffers->mSourceBuffers.Clear(); } void -TestGeckoMediaSourceBuffer() +ClearActiveSourceBuffers(void* aContext) { - DummySourceBuffer test1{ 2222, /* id */ - false /* released */ }; - GeckoMediaSourceBufferImpl test1_impl{ - &test1, /* mContext */ - &SourceBufferFree /* mFree */ - }; - MOZ_ASSERT(GetSourceBuffer(test1.id) == nullptr); - GeckoMedia_SourceBuffer_Create(test1.id, test1_impl, 0, "video/mp4", true); - auto sourceBuffer = GetSourceBuffer(test1.id); - MOZ_ASSERT(sourceBuffer != nullptr); - MOZ_ASSERT(test1.released == false); - GeckoMedia_SourceBuffer_Shutdown(test1.id); - MOZ_ASSERT(test1.released == true); - MOZ_ASSERT(GetSourceBuffer(test1.id) == nullptr); + static_cast(aContext) + ->mActiveSourceBuffers->mSourceBuffers.Clear(); } -struct DummySourceBufferList +void +TestGeckoMediaSource() { - size_t id; - double released; - size_t sourceBuffer; -}; + using namespace media; + double start = 1.0; + double end = 1.0; + TimeInterval interval(TimeUnit::FromSeconds(start), + TimeUnit::FromSeconds(end)); + RefPtr ms = new DummyMediaSource(); -bool -IndexedGetter(void* aContext, uint32_t aIndex, size_t* aId) -{ - if (aIndex > 0) { - return false; - } - *aId = static_cast(aContext)->sourceBuffer; - return true; -} + /** Construction and initial state. **/ + auto mediaSource = GetMediaSource(ms->mId); + MOZ_ASSERT(mediaSource != nullptr); + MOZ_ASSERT(mediaSource->Duration() == ms->mDuration); + MOZ_ASSERT(mediaSource->ReadyState() == ms->mReadyState); + MOZ_ASSERT(mediaSource->HasLiveSeekableRange()); + MOZ_ASSERT(mediaSource->LiveSeekableRange() == interval); + MOZ_ASSERT(mediaSource->GetDecoder() == nullptr); + MOZ_ASSERT(mediaSource->SourceBuffers()->Length() == 0); + MOZ_ASSERT(mediaSource->ActiveSourceBuffers()->Length() == 0); -uint32_t -Length(void*) -{ - return 1; + /** Decoder. **/ + RefPtr owner = new GeckoMediaDecoderOwner(); + MediaDecoderInit decoderInit( + owner, + 0.001, // volume + true, // mPreservesPitch + 1.0, // mPlaybackRate + false, // mMinimizePreroll + false, // mHasSuspendTaint + false, // mLooping + MediaContainerType(MEDIAMIMETYPE("application/x.mediasource"))); + RefPtr decoder = new MediaSourceDecoder(decoderInit); + + ms->mReadyState = MediaSourceReadyState::Open; + MOZ_ASSERT(mediaSource->ReadyState() == MediaSourceReadyState::Open); + MOZ_ASSERT( + mediaSource->Attach(decoder) == false, + "Should not be able to attach a decoder if ready state is not Closed"); + MOZ_ASSERT(mediaSource->GetDecoder() == nullptr); + ms->mReadyState = MediaSourceReadyState::Closed; + MOZ_ASSERT(mediaSource->ReadyState() == MediaSourceReadyState::Closed); + MOZ_ASSERT(mediaSource->Attach(decoder) == true); + MOZ_ASSERT(mediaSource->GetDecoder() == decoder); + MOZ_ASSERT(mediaSource->ReadyState() == MediaSourceReadyState::Open); + MOZ_ASSERT(mediaSource->Attach(decoder) == false, + "Should not be able to attach a decoder twice"); + + decoder->Load(nullptr); + + /** Source buffers **/ + RefPtr sb = new DummySourceBuffer(ms->mId); + ms->mSourceBuffers->mSourceBuffers.AppendElement(sb); + ms->mActiveSourceBuffers->mSourceBuffers.AppendElement(sb); + MOZ_ASSERT(mediaSource->SourceBuffers()->Length() == 1); + MOZ_ASSERT(mediaSource->ActiveSourceBuffers()->Length() == 1); + + mediaSource->Detach(); + MOZ_ASSERT(mediaSource->ReadyState() == MediaSourceReadyState::Closed); + decoder->Shutdown(); } void -SourceBufferListFree(void* aContext) +TestGeckoMediaSourceBuffer() { - static_cast(aContext)->released = true; + RefPtr ms = new DummyMediaSource(); + RefPtr sb = new DummySourceBuffer(ms->mId); + auto sourceBuffer = GetSourceBuffer(sb->mId); + MOZ_ASSERT(sourceBuffer != nullptr); } void TestGeckoMediaSourceBufferList() { - DummySourceBuffer test{ 2222, /* id */ - false /* released */ }; - GeckoMediaSourceBufferImpl test_impl{ - &test, /* mContext */ - &SourceBufferFree /* mFree */ - }; - MOZ_ASSERT(GetSourceBuffer(test.id) == nullptr); - GeckoMedia_SourceBuffer_Create(test.id, test_impl, 0, "video/mp4", true); - DummySourceBufferList test1{ 3333, /* id */ - false, /* released */ - test.id /* sourceBuffer id */ }; - GeckoMediaSourceBufferListImpl test1_impl{ - &test1, /* mContext */ - &SourceBufferListFree, /* mFree */ - &IndexedGetter, /* mIndexedGetter */ - &Length /* mLength */ - }; - MOZ_ASSERT(GetSourceBufferList(test1.id) == nullptr); - GeckoMedia_SourceBufferList_Create(test1.id, test1_impl); - auto sourceBufferList = GetSourceBufferList(test1.id); + RefPtr ms = new DummyMediaSource(); + RefPtr sb = new DummySourceBuffer(ms->mId); + RefPtr sbl = new DummySourceBufferList(); + auto sourceBufferList = GetSourceBufferList(sbl->mId); MOZ_ASSERT(sourceBufferList != nullptr); + MOZ_ASSERT(sourceBufferList->Length() == 0); + sbl->mSourceBuffers.AppendElement(sb); + MOZ_ASSERT(sourceBufferList->Length() == 1); bool found = false; auto mediaSource = sourceBufferList->IndexedGetter(0, found); MOZ_ASSERT(mediaSource != nullptr && found); - mediaSource = sourceBufferList->IndexedGetter(1, found); - MOZ_ASSERT(mediaSource == nullptr && !found); - MOZ_ASSERT(sourceBufferList->Length() == 1); - MOZ_ASSERT(test1.released == false); - GeckoMedia_SourceBufferList_Shutdown(test1.id); - MOZ_ASSERT(test1.released == true); - MOZ_ASSERT(GetSourceBufferList(test1.id) == nullptr); - GeckoMedia_SourceBuffer_Shutdown(test.id); } } // namespace mozilla @@ -738,6 +890,9 @@ extern "C" void TestGecko() { using namespace mozilla; + + srand(std::time(nullptr)); + TestPreferences(); TestString(); TestArray(); diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 3b73bbb..7850a09 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -271,6 +271,8 @@ mod tests { } fn test_media_source() { + use std::cell::Cell; + use std::ptr; use std::rc::Rc; #[derive(Clone, Copy)] #[allow(dead_code)] @@ -278,10 +280,28 @@ mod tests { Closed, Open, Ended, - Unknown, + } + impl From for i32 { + fn from(state: MediaSourceReadyState) -> Self { + match state { + MediaSourceReadyState::Closed => 0, + MediaSourceReadyState::Open => 1, + MediaSourceReadyState::Ended => 2, + } + } + } + impl From for MediaSourceReadyState { + fn from(state: i32) -> Self { + match state { + 0 => MediaSourceReadyState::Closed, + 1 => MediaSourceReadyState::Open, + 2 => MediaSourceReadyState::Ended, + _ => unreachable!(), + } + } } struct MediaSourceValues { - pub ready_state: MediaSourceReadyState, + pub ready_state: Cell, pub duration: f64, } struct MediaSourceDom { @@ -292,7 +312,7 @@ mod tests { impl MediaSourceDom { pub fn new() -> Self { let values = Rc::new(MediaSourceValues { - ready_state: MediaSourceReadyState::Closed, + ready_state: Cell::new(MediaSourceReadyState::Closed), duration: 0., }); let media_source_impl = Rc::new(MediaSourceImpl { @@ -311,12 +331,10 @@ mod tests { } impl GeckoMediaSourceImpl for MediaSourceImpl { fn get_ready_state(&self) -> i32 { - match self.values.ready_state { - MediaSourceReadyState::Closed => 0, - MediaSourceReadyState::Open => 1, - MediaSourceReadyState::Ended => 2, - MediaSourceReadyState::Unknown => 3, - } + self.values.ready_state.get().into() + } + fn set_ready_state(&self, state: i32) { + self.values.ready_state.set(state.into()); } fn get_duration(&self) -> f64 { self.values.duration @@ -330,6 +348,14 @@ mod tests { mEnd: 0., } } + fn get_source_buffers(&self) -> *mut usize { + ptr::null_mut() + } + fn get_active_source_buffers(&self) -> *mut usize { + ptr::null_mut() + } + fn clear_source_buffers(&self) {} + fn clear_active_source_buffers(&self) {} } let _ = MediaSourceDom::new();