From 17e81fe12c7583c038958da85979cd11f85cb48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Tue, 5 Dec 2017 09:15:32 +0100 Subject: [PATCH 01/23] Implement SourceBuffer::GetTimeIntervals --- gecko-media/gecko/glue/SourceBuffer.cpp | 6 ++++++ gecko-media/gecko/glue/include/SourceBuffer.h | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 3ce1db3..7b4c1d6 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -54,5 +54,11 @@ SourceBuffer::~SourceBuffer() } } +media::TimeIntervals +SourceBuffer::GetTimeIntervals() +{ + return mTrackBuffersManager->Buffered(); +} + } // namespace dom } // namespace mozilla \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index 101144f..2842a98 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -24,11 +24,7 @@ class SourceBuffer final size_t aParentId, const char* aMimeType); - media::TimeIntervals GetTimeIntervals() - { - // TODO get from mImpl - return media::TimeIntervals::Invalid(); - } + media::TimeIntervals GetTimeIntervals(); void Detach() { /* TODO */} From be18a3cf6c09c55b2a6c405f7b6c0e5e39e05c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Tue, 5 Dec 2017 11:26:26 +0100 Subject: [PATCH 02/23] Remove unused SourceBuffer::Detach method --- gecko-media/gecko/glue/include/SourceBuffer.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index 2842a98..62976ef 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -26,8 +26,6 @@ class SourceBuffer final media::TimeIntervals GetTimeIntervals(); - void Detach() { /* TODO */} - private: ~SourceBuffer(); From fd6e7cdb7f1651903439e5d336562c1f0f0070ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Tue, 5 Dec 2017 13:31:10 +0100 Subject: [PATCH 03/23] Implement SourceBuffer::EvictData --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 15 +++++++++-- gecko-media/gecko/glue/SourceBuffer.cpp | 26 +++++++++++++++---- .../glue/include/GeckoMediaSourceBuffer.h | 8 ++++++ gecko-media/gecko/glue/include/SourceBuffer.h | 2 ++ gecko-media/src/mse/sourcebuffer.rs | 18 +++++++++++++ 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index 57cc97c..9a72bdf 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -37,11 +37,22 @@ GeckoMedia_SourceBuffer_Create(size_t aId, size_t aParentId, const char* aMimeType) { - GeckoMediaSourceBuffer* reflector = - sReflectors.AppendElement(GeckoMediaSourceBuffer(aId, aImpl, aParentId, aMimeType)); + GeckoMediaSourceBuffer* reflector = sReflectors.AppendElement( + GeckoMediaSourceBuffer(aId, aImpl, aParentId, aMimeType)); MOZ_ASSERT(GetReflector(aId) == reflector); } +void +GeckoMedia_SourceBuffer_EvictData(size_t aId, + size_t aParentId, + int64_t aLength, + bool* aBufferFull) +{ + IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) + + reflector->mSourceBuffer->EvictData(aParentId, aLength, aBufferFull); +} + SourceBuffer* GetSourceBuffer(const size_t aId) { diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 7b4c1d6..06e6dac 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -11,15 +11,13 @@ #include "MediaSourceDemuxer.h" #include "mozilla/Logging.h" -extern mozilla::LogModule* GetMediaSourceLog(); +extern mozilla::LogModule* +GetMediaSourceLog(); #define MSE_DEBUG(arg, ...) \ MOZ_LOG(GetMediaSourceLog(), \ mozilla::LogLevel::Debug, \ - ("SourceBuffer(%p)::%s: " arg, \ - this, \ - __func__, \ - ##__VA_ARGS__)) + ("SourceBuffer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) namespace mozilla { namespace dom { @@ -60,5 +58,23 @@ SourceBuffer::GetTimeIntervals() return mTrackBuffersManager->Buffered(); } +void +SourceBuffer::EvictData(size_t aParentId, size_t aLength, bool* aBufferFull) +{ + typedef TrackBuffersManager::EvictDataResult Result; + + auto parent = GetMediaSource(aParentId); + if (NS_WARN_IF(!parent)) { + *aBufferFull = true; + return; + } + + Result evicted = mTrackBuffersManager->EvictData( + media::TimeUnit::FromSeconds(parent->GetDecoder()->GetCurrentTime()), + aLength); + + *aBufferFull = (evicted == Result::BUFFER_FULL); +} + } // namespace dom } // namespace mozilla \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index 72a3a00..16cbf92 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -8,6 +8,7 @@ #define GeckoMediaSourceBuffer_h_ #include +#include namespace mozilla { namespace dom { @@ -27,6 +28,7 @@ mozilla::dom::SourceBuffer* GetSourceBuffer(const size_t aId); extern "C" { +// TODO error handling (i.e. aId or aParentId may not be valid) void GeckoMedia_SourceBuffer_Create(size_t aId, GeckoMediaSourceBufferImpl aImpl, @@ -35,6 +37,12 @@ GeckoMedia_SourceBuffer_Create(size_t aId, void GeckoMedia_SourceBuffer_Shutdown(size_t aId); + +void +GeckoMedia_SourceBuffer_EvictData(size_t aId, + size_t aParentId, + int64_t aLength, + bool* aBufferFull); } #endif // GeckoMediaSourceBuffer_h_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index 62976ef..f459013 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -26,6 +26,8 @@ class SourceBuffer final media::TimeIntervals GetTimeIntervals(); + void EvictData(size_t aParentId, size_t aLength, bool* aBufferFull); + private: ~SourceBuffer(); diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 0df698c..624e14c 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -3,8 +3,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use bindings::{GeckoMediaSourceBufferImpl, GeckoMedia_SourceBuffer_Create}; +use bindings::GeckoMedia_SourceBuffer_EvictData; use std::ffi::CString; use std::rc::Rc; +use std::sync::mpsc; def_gecko_media_struct!(SourceBuffer); @@ -29,6 +31,22 @@ impl SourceBuffer { id, }) } + + pub fn evict_coded_frames(&self, parent_id: usize, len: usize, buffer_full: &mut bool) { + let id = self.id; + let (sender, receiver) = mpsc::channel(); + self.gecko_media.queue_task(move || unsafe { + sender + .send(GeckoMedia_SourceBuffer_EvictData( + id, + parent_id, + len as i64, + buffer_full, + )) + .unwrap(); + }); + receiver.recv().unwrap(); + } } impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); From 889496ae0ac53922c56dc2d5ad777fa9930d81ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 7 Dec 2017 11:40:10 +0100 Subject: [PATCH 04/23] Introduce impl_simple_ffi_setter_wrapper! macro --- gecko-media/src/macros.rs | 18 ++++++++++++++++++ gecko-media/src/mse/mediasource.rs | 8 ++++---- gecko-media/src/mse/sourcebufferlist.rs | 2 +- gecko-media/src/player.rs | 21 ++++++++------------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/gecko-media/src/macros.rs b/gecko-media/src/macros.rs index 8fdd9f4..0dc1656 100644 --- a/gecko-media/src/macros.rs +++ b/gecko-media/src/macros.rs @@ -79,6 +79,15 @@ macro_rules! def_gecko_callbacks_ffi_wrapper ( ); macro_rules! impl_simple_ffi_callback_wrapper ( + ($fn:ident) => ( + unsafe extern "C" fn $fn(ptr: *mut c_void){ + let wrapper = &*(ptr as *mut Wrapper); + wrapper.callbacks.$fn(); + } + ); +); + +macro_rules! impl_simple_ffi_getter_wrapper ( ($fn:ident, $return:ty) => ( unsafe extern "C" fn $fn(ptr: *mut c_void) -> $return { let wrapper = &*(ptr as *mut Wrapper); @@ -86,3 +95,12 @@ macro_rules! impl_simple_ffi_callback_wrapper ( } ); ); + +macro_rules! impl_simple_ffi_setter_wrapper ( + ($fn:ident, $value_type:ty) => ( + unsafe extern "C" fn $fn(ptr: *mut c_void, value: $value_type) -> () { + let wrapper = &*(ptr as *mut Wrapper); + wrapper.callbacks.$fn(value); + } + ); +); diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index e539027..8faed47 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -55,10 +55,10 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { def_gecko_callbacks_ffi_wrapper!(Rc); - impl_simple_ffi_callback_wrapper!(get_ready_state, i32); - impl_simple_ffi_callback_wrapper!(get_duration, f64); - impl_simple_ffi_callback_wrapper!(has_live_seekable_range, bool); - impl_simple_ffi_callback_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); + impl_simple_ffi_getter_wrapper!(get_ready_state, i32); + impl_simple_ffi_getter_wrapper!(get_duration, f64); + impl_simple_ffi_getter_wrapper!(has_live_seekable_range, bool); + impl_simple_ffi_getter_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); GeckoMediaSourceImpl { mContext: Box::into_raw(Box::new(Wrapper { diff --git a/gecko-media/src/mse/sourcebufferlist.rs b/gecko-media/src/mse/sourcebufferlist.rs index 3731804..f651bfc 100644 --- a/gecko-media/src/mse/sourcebufferlist.rs +++ b/gecko-media/src/mse/sourcebufferlist.rs @@ -28,7 +28,7 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSource def_gecko_callbacks_ffi_wrapper!(Rc); - impl_simple_ffi_callback_wrapper!(length, u32); + impl_simple_ffi_getter_wrapper!(length, u32); unsafe extern "C" fn indexed_getter(ptr: *mut c_void, index: u32, id: *mut usize) -> bool { let wrapper = &*(ptr as *mut Wrapper); diff --git a/gecko-media/src/player.rs b/gecko-media/src/player.rs index 686fbb1..99cfb20 100644 --- a/gecko-media/src/player.rs +++ b/gecko-media/src/player.rs @@ -451,11 +451,14 @@ fn to_ffi_callback( drop(Box::from_raw(ptr as *mut Wrapper)); } - impl_simple_ffi_callback_wrapper!(decode_error, ()); - impl_simple_ffi_callback_wrapper!(playback_ended, ()); - impl_simple_ffi_callback_wrapper!(loaded_data, ()); - impl_simple_ffi_callback_wrapper!(seek_started, ()); - impl_simple_ffi_callback_wrapper!(seek_completed, ()); + impl_simple_ffi_callback_wrapper!(decode_error); + impl_simple_ffi_callback_wrapper!(playback_ended); + impl_simple_ffi_callback_wrapper!(loaded_data); + impl_simple_ffi_callback_wrapper!(seek_started); + impl_simple_ffi_callback_wrapper!(seek_completed); + + impl_simple_ffi_setter_wrapper!(duration_changed, f64); + impl_simple_ffi_setter_wrapper!(time_update, f64); unsafe extern "C" fn async_event(ptr: *mut c_void, name: *const c_char) { let wrapper = &*(ptr as *mut Wrapper); @@ -473,14 +476,6 @@ fn to_ffi_callback( } wrapper.callbacks.metadata_loaded(metadata); } - unsafe extern "C" fn duration_changed(ptr: *mut c_void, duration: f64) { - let wrapper = &*(ptr as *mut Wrapper); - wrapper.callbacks.duration_changed(duration); - } - unsafe extern "C" fn time_update(ptr: *mut c_void, time: f64) { - let wrapper = &*(ptr as *mut Wrapper); - wrapper.callbacks.time_update(time); - } unsafe extern "C" fn update_current_images(ptr: *mut c_void, size: usize, elements: *mut GeckoPlanarYCbCrImage) { let wrapper = &*(ptr as *mut Wrapper); let images = to_ffi_planar_ycbycr_images(size, elements, &wrapper.video_frame_allocator); From b875eba59729d1e2e377e9f3cac05ff05c7f26cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 7 Dec 2017 18:01:24 +0100 Subject: [PATCH 05/23] Implement SourceBufferAttributes and GeckoMediaSourceBufferImpl --- gecko-media/data/header_files.json | 1 - .../gecko/glue/GeckoMediaSourceBuffer.cpp | 14 +- gecko-media/gecko/glue/MediaSourceDecoder.cpp | 10 ++ gecko-media/gecko/glue/SourceBuffer.cpp | 12 +- .../gecko/glue/include/GeckoMediaMacros.h | 47 +++++- .../glue/include/GeckoMediaSourceBuffer.h | 42 ++++- gecko-media/gecko/glue/include/SourceBuffer.h | 8 +- .../glue/include/SourceBufferAttributes.h | 123 ++++++++++++++ .../include/mozilla/dom/SourceBufferBinding.h | 18 -- .../gecko/include/SourceBufferAttributes.h | 157 ------------------ gecko-media/gecko/test/test.cpp | 4 +- gecko-media/src/macros.rs | 2 +- gecko-media/src/mse/sourcebuffer.rs | 66 +++++++- gecko-media/src/top.rs | 3 +- 14 files changed, 304 insertions(+), 203 deletions(-) create mode 100644 gecko-media/gecko/glue/include/SourceBufferAttributes.h delete mode 100644 gecko-media/gecko/glue/include/mozilla/dom/SourceBufferBinding.h delete mode 100644 gecko-media/gecko/include/SourceBufferAttributes.h diff --git a/gecko-media/data/header_files.json b/gecko-media/data/header_files.json index d599cc5..de543d6 100644 --- a/gecko-media/data/header_files.json +++ b/gecko-media/data/header_files.json @@ -140,7 +140,6 @@ "SharedBuffer.h": "dom/media/SharedBuffer.h", "SimpleMap.h": "dom/media/platforms/SimpleMap.h", "SinfParser.h": "dom/media/mp4/SinfParser.h", - "SourceBufferAttributes.h": "dom/media/mediasource/SourceBufferAttributes.h", "SourceBufferResource.h": "dom/media/mediasource/SourceBufferResource.h", "SourceBufferTask.h": "dom/media/mediasource/SourceBufferTask.h", "SpecialSystemDirectory.h": "xpcom/io/SpecialSystemDirectory.h", diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index 9a72bdf..face520 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -17,8 +17,10 @@ struct GeckoMediaSourceBuffer GeckoMediaSourceBuffer(size_t aId, GeckoMediaSourceBufferImpl aImpl, size_t aParentId, - const char* aMimeType) - : mSourceBuffer(new SourceBuffer(aImpl, aParentId, aMimeType)) + const char* aMimeType, + bool aGenerateTimestamps) + : mSourceBuffer( + new SourceBuffer(aImpl, aParentId, aMimeType, aGenerateTimestamps)) , mId(aId) { } @@ -35,10 +37,12 @@ void GeckoMedia_SourceBuffer_Create(size_t aId, GeckoMediaSourceBufferImpl aImpl, size_t aParentId, - const char* aMimeType) + const char* aMimeType, + bool aGenerateTimestamps) { - GeckoMediaSourceBuffer* reflector = sReflectors.AppendElement( - GeckoMediaSourceBuffer(aId, aImpl, aParentId, aMimeType)); + GeckoMediaSourceBuffer* reflector = + sReflectors.AppendElement(GeckoMediaSourceBuffer( + aId, aImpl, aParentId, aMimeType, aGenerateTimestamps)); MOZ_ASSERT(GetReflector(aId) == reflector); } diff --git a/gecko-media/gecko/glue/MediaSourceDecoder.cpp b/gecko-media/gecko/glue/MediaSourceDecoder.cpp index 7dc1089..cbd49d2 100644 --- a/gecko-media/gecko/glue/MediaSourceDecoder.cpp +++ b/gecko-media/gecko/glue/MediaSourceDecoder.cpp @@ -354,6 +354,16 @@ MediaSourceDecoder::NotifyInitDataArrived() } } +void +MediaSourceDecoder::NotifyDataArrived() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_DIAGNOSTIC_ASSERT(!IsShutdown()); + AbstractThread::AutoEnter context(AbstractMainThread()); + NotifyReaderDataArrived(); + GetOwner()->DownloadProgressed(); +} + already_AddRefed MediaSourceDecoder::GetCurrentPrincipal() { diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 06e6dac..bb9def6 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -24,8 +24,9 @@ namespace dom { SourceBuffer::SourceBuffer(GeckoMediaSourceBufferImpl aImpl, size_t aParentId, - const char* aMimeType) - : mImpl(aImpl) + const char* aMimeType, + bool aGenerateTimestamps) + : mCurrentAttributes(aImpl, aGenerateTimestamps) { mozilla::Maybe mime = MakeMediaContainerType(aMimeType); if (NS_WARN_IF(!mime)) { @@ -45,13 +46,6 @@ SourceBuffer::SourceBuffer(GeckoMediaSourceBufferImpl aImpl, parent->GetDecoder()->GetDemuxer()->AttachSourceBuffer(mTrackBuffersManager); } -SourceBuffer::~SourceBuffer() -{ - if (mImpl.mContext && mImpl.mFree) { - (*mImpl.mFree)(mImpl.mContext); - } -} - media::TimeIntervals SourceBuffer::GetTimeIntervals() { diff --git a/gecko-media/gecko/glue/include/GeckoMediaMacros.h b/gecko-media/gecko/glue/include/GeckoMediaMacros.h index e687033..67668a6 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaMacros.h +++ b/gecko-media/gecko/glue/include/GeckoMediaMacros.h @@ -63,4 +63,49 @@ IMPL_GECKO_MEDIA_REFLECTOR_CONSTRUCTOR(GMClass, Class, Callbacks) \ IMPL_GECKO_MEDIA_REFLECTOR_SHUTDOWN(GMClass, Class) -#endif +#define DEF_GECKO_MEDIA_CALLBACK(Name, ReturnType) \ + ReturnType Name() \ + { +#define CALLBACK_MEMBER(Name) mImpl.m##Name +#define CALLBACK_GUARD_CONDITION(Name) \ + if (NS_WARN_IF(!mImpl.mContext || !CALLBACK_MEMBER(Name))) { +#define CALLBACK_GUARD(Name, ReturnValue) \ + CALLBACK_GUARD_CONDITION(Name) \ + return ReturnValue; \ + } +#define CALLBACK_GUARD_VOID(Name) \ + CALLBACK_GUARD_CONDITION(Name) \ + return; \ + } +#define CALLBACK_CALL(Name) (*CALLBACK_MEMBER(Name))(mImpl.mContext); +#define CALLBACK_CALL_WITH_PARAM(Name) \ + (*CALLBACK_MEMBER(Name))(mImpl.mContext, aParam); + +#define IMPL_GECKO_MEDIA_CALLBACK(Name) \ + DEF_GECKO_MEDIA_CALLBACK(Name, void) \ + CALLBACK_GUARD_VOID(Name) \ + CALLBACK_CALL(Name) \ + } + +#define IMPL_GECKO_MEDIA_SIMPLE_GETTER(Name, ReturnType, ReturnValue) \ + DEF_GECKO_MEDIA_CALLBACK(Name, ReturnType) \ + CALLBACK_GUARD(Name, ReturnValue) \ + return CALLBACK_CALL(Name) \ + } + +#define IMPL_GECKO_MEDIA_SIMPLE_SETTER(Name, ParamType) \ + void Name(const ParamType aParam) \ + { \ + CALLBACK_GUARD_VOID(Name) \ + CALLBACK_CALL_WITH_PARAM(Name) \ + } + +#define IMPL_GECKO_MEDIA_GETTER_WITH_CAST( \ + Name, ReturnType, ReturnValue, CastType, CastFunction) \ + DEF_GECKO_MEDIA_CALLBACK(Name, ReturnType) \ + CALLBACK_GUARD(Name, CastFunction(ReturnValue)) \ + CastType value = CALLBACK_CALL(Name); \ + return CastFunction(value); \ + } + +#endif // _GECKO_MEDIA_MACROS_H_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index 16cbf92..fc87cd2 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -12,19 +12,56 @@ namespace mozilla { namespace dom { + class SourceBuffer; + +// https://w3c.github.io/media-source/#idl-def-appendmode +enum class SourceBufferAppendMode +{ + Segments, + Sequence, +}; + +// Current state as per Segment Parser Loop Algorithm +// http://w3c.github.io/media-source/index.html#sourcebuffer-segment-parser-loop +// ** This needs to go in capital letters cause it is used by non-glued code ** +enum class AppendState +{ + WAITING_FOR_SEGMENT, + PARSING_INIT_SEGMENT, + PARSING_MEDIA_SEGMENT, +}; } } +using namespace mozilla::dom; + struct GeckoMediaSourceBuffer; struct GeckoMediaSourceBufferImpl { void* mContext; void (*mFree)(void*); + double (*mGetAppendWindowStart)(void*); + void (*mSetAppendWindowStart)(void*, double); + double (*mGetAppendWindowEnd)(void*); + void (*mSetAppendWindowEnd)(void*, double); + double (*mGetTimestampOffset)(void*); + void (*mSetTimestampOffset)(void*, double); + SourceBufferAppendMode (*mGetAppendMode)(void*); + void (*mSetAppendMode)(void*, SourceBufferAppendMode); + void (*mGetGroupStartTimestamp)(void*, double&); + void (*mSetGroupStartTimestamp)(void*, double); + bool (*mHaveGroupStartTimestamp)(void*); + void (*mResetGroupStartTimestamp)(void*); + void (*mRestartGroupStartTimestamp)(void*); + double (*mGetGroupEndTimestamp)(void*); + void (*mSetGroupEndTimestamp)(void*, double); + AppendState (*mGetAppendState)(void*); + void (*mSetAppendState)(void*, AppendState); }; -mozilla::dom::SourceBuffer* +SourceBuffer* GetSourceBuffer(const size_t aId); extern "C" { @@ -33,7 +70,8 @@ void GeckoMedia_SourceBuffer_Create(size_t aId, GeckoMediaSourceBufferImpl aImpl, size_t aParentId, - const char* aMimeType); + const char* aMimeType, + bool aGenerateTimestamps); void GeckoMedia_SourceBuffer_Shutdown(size_t aId); diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index f459013..bf494ff 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -9,6 +9,7 @@ #include "GeckoMediaSourceBuffer.h" #include "nsISupportsImpl.h" +#include "SourceBufferAttributes.h" #include "TimeUnits.h" #include "TrackBuffersManager.h" @@ -22,16 +23,17 @@ class SourceBuffer final SourceBuffer(GeckoMediaSourceBufferImpl aImpl, size_t aParentId, - const char* aMimeType); + const char* aMimeType, + bool aGenerateTimestamps); media::TimeIntervals GetTimeIntervals(); void EvictData(size_t aParentId, size_t aLength, bool* aBufferFull); private: - ~SourceBuffer(); + ~SourceBuffer(){}; - GeckoMediaSourceBufferImpl mImpl; + SourceBufferAttributes mCurrentAttributes; RefPtr mTrackBuffersManager; }; diff --git a/gecko-media/gecko/glue/include/SourceBufferAttributes.h b/gecko-media/gecko/glue/include/SourceBufferAttributes.h new file mode 100644 index 0000000..465bce4 --- /dev/null +++ b/gecko-media/gecko/glue/include/SourceBufferAttributes.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_SourceBufferAttributes_h_ +#define mozilla_SourceBufferAttributes_h_ + +#include "GeckoMediaMacros.h" +#include "GeckoMediaSourceBuffer.h" +#include "TimeUnits.h" +#include "mozilla/Maybe.h" + +namespace mozilla { + +class SourceBufferAttributes +{ +public: + typedef mozilla::dom::AppendState AppendState; + + SourceBufferAttributes(GeckoMediaSourceBufferImpl aImpl, + bool aGenerateTimestamps) + : mImpl(aImpl) + , mGenerateTimestamps(aGenerateTimestamps) + { + } + + SourceBufferAttributes(const SourceBufferAttributes& aOther) = default; + + ~SourceBufferAttributes() + { + if (NS_WARN_IF(!mImpl.mContext || !mImpl.mFree)) { + return; + } + (*mImpl.mFree)(mImpl.mContext); + } + + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetAppendWindowStart, double, 0) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetAppendWindowStart, double) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetAppendWindowEnd, double, 0) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetAppendWindowEnd, double) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetAppendMode, + dom::SourceBufferAppendMode, + dom::SourceBufferAppendMode::Segments) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetAppendMode, dom::SourceBufferAppendMode) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(HaveGroupStartTimestamp, bool, false) + IMPL_GECKO_MEDIA_CALLBACK(ResetGroupStartTimestamp) + IMPL_GECKO_MEDIA_CALLBACK(RestartGroupStartTimestamp) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetAppendState, + AppendState, + AppendState::WAITING_FOR_SEGMENT) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetAppendState, AppendState) + + double GetApparentTimestampOffset() + { + CALLBACK_GUARD(GetTimestampOffset, 0); + return CALLBACK_CALL(GetTimestampOffset); + } + + void SetApparentTimestampOffset(double aTimestampOffset) + { + CALLBACK_GUARD_VOID(SetTimestampOffset); + (*mImpl.mSetTimestampOffset)(mImpl.mContext, aTimestampOffset); + } + + media::TimeUnit GetTimestampOffset() + { + double timestampOffset = GetApparentTimestampOffset(); + return media::TimeUnit::FromSeconds(timestampOffset); + } + + void SetTimestampOffset(const media::TimeUnit& aTimestampOffset) + { + CALLBACK_GUARD_VOID(SetTimestampOffset); + (*mImpl.mSetTimestampOffset)(mImpl.mContext, aTimestampOffset.ToSeconds()); + } + + media::TimeUnit GetGroupStartTimestamp() + { + CALLBACK_GUARD(GetGroupStartTimestamp, media::TimeUnit::FromSeconds(0)); + double timestamp = 0; + (*mImpl.mGetGroupStartTimestamp)(mImpl.mContext, timestamp); + return media::TimeUnit::FromSeconds(timestamp); + } + + void SetGroupStartTimestamp(const media::TimeUnit& aGroupStartTimestamp) + { + CALLBACK_GUARD_VOID(SetGroupStartTimestamp); + (*mImpl.mSetGroupStartTimestamp)(mImpl.mContext, + aGroupStartTimestamp.ToSeconds()); + } + + media::TimeUnit GetGroupEndTimestamp() + { + CALLBACK_GUARD(GetGroupEndTimestamp, media::TimeUnit::FromSeconds(0)); + double timestamp = CALLBACK_CALL(GetGroupEndTimestamp); + return media::TimeUnit::FromSeconds(timestamp); + } + + void SetGroupEndTimestamp(const media::TimeUnit& aGroupEndTimestamp) + { + CALLBACK_GUARD_VOID(SetGroupEndTimestamp); + (*mImpl.mSetGroupEndTimestamp)(mImpl.mContext, + aGroupEndTimestamp.ToSeconds()); + } + + // mGenerateTimestamp isn't mutable once the source buffer has been + // constructed + bool mGenerateTimestamps; + + SourceBufferAttributes& operator=(const SourceBufferAttributes& aOther) = + default; + +private: + SourceBufferAttributes() = delete; + + GeckoMediaSourceBufferImpl mImpl; +}; + +} // end namespace mozilla + +#endif /* mozilla_SourceBufferAttributes_h_ */ diff --git a/gecko-media/gecko/glue/include/mozilla/dom/SourceBufferBinding.h b/gecko-media/gecko/glue/include/mozilla/dom/SourceBufferBinding.h deleted file mode 100644 index 07ff363..0000000 --- a/gecko-media/gecko/glue/include/mozilla/dom/SourceBufferBinding.h +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -namespace mozilla { -namespace dom { - -enum class SourceBufferAppendMode : uint8_t -{ - Segments, - Sequence, - EndGuard_ -}; - -} // namespace dom -} // namespace mozilla \ No newline at end of file diff --git a/gecko-media/gecko/include/SourceBufferAttributes.h b/gecko-media/gecko/include/SourceBufferAttributes.h deleted file mode 100644 index 0af80a4..0000000 --- a/gecko-media/gecko/include/SourceBufferAttributes.h +++ /dev/null @@ -1,157 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef mozilla_SourceBufferAttributes_h_ -#define mozilla_SourceBufferAttributes_h_ - -#include "TimeUnits.h" -#include "mozilla/dom/SourceBufferBinding.h" -#include "mozilla/Maybe.h" - -namespace mozilla { - -class SourceBufferAttributes { -public: - - // Current state as per Segment Parser Loop Algorithm - // http://w3c.github.io/media-source/index.html#sourcebuffer-segment-parser-loop - enum class AppendState - { - WAITING_FOR_SEGMENT, - PARSING_INIT_SEGMENT, - PARSING_MEDIA_SEGMENT, - }; - - explicit SourceBufferAttributes(bool aGenerateTimestamp) - : mGenerateTimestamps(aGenerateTimestamp) - , mAppendWindowStart(0) - , mAppendWindowEnd(PositiveInfinity()) - , mAppendMode(dom::SourceBufferAppendMode::Segments) - , mApparentTimestampOffset(0) - , mAppendState(AppendState::WAITING_FOR_SEGMENT) - {} - - SourceBufferAttributes(const SourceBufferAttributes& aOther) = default; - - double GetAppendWindowStart() const - { - return mAppendWindowStart; - } - - double GetAppendWindowEnd() const - { - return mAppendWindowEnd; - } - - void SetAppendWindowStart(double aWindowStart) - { - mAppendWindowStart = aWindowStart; - } - - void SetAppendWindowEnd(double aWindowEnd) - { - mAppendWindowEnd = aWindowEnd; - } - - double GetApparentTimestampOffset() const - { - return mApparentTimestampOffset; - } - - void SetApparentTimestampOffset(double aTimestampOffset) - { - mApparentTimestampOffset = aTimestampOffset; - mTimestampOffset = media::TimeUnit::FromSeconds(aTimestampOffset); - } - - media::TimeUnit GetTimestampOffset() const - { - return mTimestampOffset; - } - - void SetTimestampOffset(const media::TimeUnit& aTimestampOffset) - { - mTimestampOffset = aTimestampOffset; - mApparentTimestampOffset = aTimestampOffset.ToSeconds(); - } - - dom::SourceBufferAppendMode GetAppendMode() const - { - return mAppendMode; - } - - void SetAppendMode(dom::SourceBufferAppendMode aAppendMode) - { - mAppendMode = aAppendMode; - } - - void SetGroupStartTimestamp(const media::TimeUnit& aGroupStartTimestamp) - { - mGroupStartTimestamp = Some(aGroupStartTimestamp); - } - - media::TimeUnit GetGroupStartTimestamp() const - { - return mGroupStartTimestamp.ref(); - } - - bool HaveGroupStartTimestamp() const - { - return mGroupStartTimestamp.isSome(); - } - - void ResetGroupStartTimestamp() - { - mGroupStartTimestamp.reset(); - } - - void RestartGroupStartTimestamp() - { - mGroupStartTimestamp = Some(mGroupEndTimestamp); - } - - media::TimeUnit GetGroupEndTimestamp() const - { - return mGroupEndTimestamp; - } - - void SetGroupEndTimestamp(const media::TimeUnit& aGroupEndTimestamp) - { - mGroupEndTimestamp = aGroupEndTimestamp; - } - - AppendState GetAppendState() const - { - return mAppendState; - } - - void SetAppendState(AppendState aState) - { - mAppendState = aState; - } - - // mGenerateTimestamp isn't mutable once the source buffer has been constructed - bool mGenerateTimestamps; - - SourceBufferAttributes& operator=(const SourceBufferAttributes& aOther) = default; - -private: - SourceBufferAttributes() = delete; - - double mAppendWindowStart; - double mAppendWindowEnd; - dom::SourceBufferAppendMode mAppendMode; - double mApparentTimestampOffset; - media::TimeUnit mTimestampOffset; - Maybe mGroupStartTimestamp; - media::TimeUnit mGroupEndTimestamp; - // The current append state as per https://w3c.github.io/media-source/#sourcebuffer-append-state - AppendState mAppendState; -}; - -} // end namespace mozilla - -#endif /* mozilla_SourceBufferAttributes_h_ */ diff --git a/gecko-media/gecko/test/test.cpp b/gecko-media/gecko/test/test.cpp index 692a0dd..dc6004d 100644 --- a/gecko-media/gecko/test/test.cpp +++ b/gecko-media/gecko/test/test.cpp @@ -656,7 +656,7 @@ TestGeckoMediaSourceBuffer() &SourceBufferFree /* mFree */ }; MOZ_ASSERT(GetSourceBuffer(test1.id) == nullptr); - GeckoMedia_SourceBuffer_Create(test1.id, test1_impl, 0, "video/mp4"); + 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); @@ -704,7 +704,7 @@ TestGeckoMediaSourceBufferList() &SourceBufferFree /* mFree */ }; MOZ_ASSERT(GetSourceBuffer(test.id) == nullptr); - GeckoMedia_SourceBuffer_Create(test.id, test_impl, 0, "video/mp4"); + GeckoMedia_SourceBuffer_Create(test.id, test_impl, 0, "video/mp4", true); DummySourceBufferList test1{ 3333, /* id */ false, /* released */ test.id /* sourceBuffer id */ }; diff --git a/gecko-media/src/macros.rs b/gecko-media/src/macros.rs index 0dc1656..4dfcbe2 100644 --- a/gecko-media/src/macros.rs +++ b/gecko-media/src/macros.rs @@ -80,7 +80,7 @@ macro_rules! def_gecko_callbacks_ffi_wrapper ( macro_rules! impl_simple_ffi_callback_wrapper ( ($fn:ident) => ( - unsafe extern "C" fn $fn(ptr: *mut c_void){ + unsafe extern "C" fn $fn(ptr: *mut c_void) { let wrapper = &*(ptr as *mut Wrapper); wrapper.callbacks.$fn(); } diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 624e14c..7c409d7 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -2,8 +2,8 @@ // 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/. -use bindings::{GeckoMediaSourceBufferImpl, GeckoMedia_SourceBuffer_Create}; use bindings::GeckoMedia_SourceBuffer_EvictData; +use bindings::{GeckoMediaSourceBufferImpl, GeckoMedia_SourceBuffer_Create}; use std::ffi::CString; use std::rc::Rc; use std::sync::mpsc; @@ -17,6 +17,7 @@ impl SourceBuffer { callbacks: Rc, parent_id: usize, mime: &str, + generate_timestamps: bool, ) -> Result { let callbacks = to_ffi_callbacks(callbacks); let mime = match CString::new(mime.as_bytes()) { @@ -24,7 +25,7 @@ impl SourceBuffer { _ => return Err(()), }; gecko_media.queue_task(move || unsafe { - GeckoMedia_SourceBuffer_Create(id, callbacks, parent_id, mime.as_ptr()); + GeckoMedia_SourceBuffer_Create(id, callbacks, parent_id, mime.as_ptr(), generate_timestamps); }); Ok(Self { gecko_media, @@ -51,7 +52,27 @@ impl SourceBuffer { impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); -pub trait SourceBufferImpl {} +/// Users of SourceBuffer pass in an implementation of this trait when creating +/// SourceBuffer objects. +pub trait SourceBufferImpl { + fn get_append_window_start(&self) -> f64; + fn set_append_window_start(&self, f64); + fn get_append_window_end(&self) -> f64; + fn set_append_window_end(&self, f64); + fn get_timestamp_offset(&self) -> f64; + fn set_timestamp_offset(&self, f64); + fn get_append_mode(&self) -> i32; + fn set_append_mode(&self, i32); + fn get_group_start_timestamp(&self, *mut f64); + fn set_group_start_timestamp(&self, f64); + fn have_group_start_timestamp(&self) -> bool; + fn reset_group_start_timestamp(&self); + fn restart_group_start_timestamp(&self); + fn get_group_end_timestamp(&self) -> f64; + fn set_group_end_timestamp(&self, f64); + fn get_append_state(&self) -> i32; + fn set_append_state(&self, i32); +} pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBufferImpl { // Can't cast from *c_void to a Trait, so wrap in a concrete type @@ -59,10 +80,49 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff def_gecko_callbacks_ffi_wrapper!(Rc); + impl_simple_ffi_getter_wrapper!(get_append_window_start, f64); + impl_simple_ffi_setter_wrapper!(set_append_window_start, f64); + impl_simple_ffi_getter_wrapper!(get_append_window_end, f64); + impl_simple_ffi_setter_wrapper!(set_append_window_end, f64); + impl_simple_ffi_getter_wrapper!(get_timestamp_offset, f64); + impl_simple_ffi_setter_wrapper!(set_timestamp_offset, f64); + impl_simple_ffi_getter_wrapper!(get_append_mode, i32); + impl_simple_ffi_setter_wrapper!(set_append_mode, i32); + impl_simple_ffi_setter_wrapper!(set_group_start_timestamp, f64); + impl_simple_ffi_getter_wrapper!(have_group_start_timestamp, bool); + impl_simple_ffi_callback_wrapper!(reset_group_start_timestamp); + impl_simple_ffi_callback_wrapper!(restart_group_start_timestamp); + impl_simple_ffi_getter_wrapper!(get_group_end_timestamp, f64); + impl_simple_ffi_setter_wrapper!(set_group_end_timestamp, f64); + impl_simple_ffi_getter_wrapper!(get_append_state, i32); + impl_simple_ffi_setter_wrapper!(set_append_state, i32); + + unsafe extern "C" fn get_group_start_timestamp(ptr: *mut c_void, timestamp: *mut f64) { + let wrapper = &*(ptr as *mut Wrapper); + wrapper.callbacks.get_group_start_timestamp(timestamp); + } + GeckoMediaSourceBufferImpl { mContext: Box::into_raw(Box::new(Wrapper { callbacks, })) as *mut c_void, mFree: Some(free), + mGetAppendWindowStart: Some(get_append_window_start), + mSetAppendWindowStart: Some(set_append_window_start), + mGetAppendWindowEnd: Some(get_append_window_end), + mSetAppendWindowEnd: Some(set_append_window_end), + mGetTimestampOffset: Some(get_timestamp_offset), + mSetTimestampOffset: Some(set_timestamp_offset), + mGetAppendMode: Some(get_append_mode), + mSetAppendMode: Some(set_append_mode), + mGetGroupStartTimestamp: Some(get_group_start_timestamp), + mSetGroupStartTimestamp: Some(set_group_start_timestamp), + mHaveGroupStartTimestamp: Some(have_group_start_timestamp), + mResetGroupStartTimestamp: Some(reset_group_start_timestamp), + mRestartGroupStartTimestamp: Some(restart_group_start_timestamp), + mGetGroupEndTimestamp: Some(get_group_end_timestamp), + mSetGroupEndTimestamp: Some(set_group_end_timestamp), + mGetAppendState: Some(get_append_state), + mSetAppendState: Some(set_append_state), } } diff --git a/gecko-media/src/top.rs b/gecko-media/src/top.rs index 5aab3bf..5cfc7cb 100644 --- a/gecko-media/src/top.rs +++ b/gecko-media/src/top.rs @@ -154,11 +154,12 @@ impl GeckoMedia { callbacks: Weak, parent_id: usize, mime: &str, + generate_timestamps: bool, ) -> Result { let handle = GeckoMedia::get()?; let id = NEXT_SOURCE_BUFFER_ID.fetch_add(1, Ordering::SeqCst); if let Some(callbacks) = callbacks.upgrade() { - SourceBuffer::new(handle, id, callbacks, parent_id, mime) + SourceBuffer::new(handle, id, callbacks, parent_id, mime, generate_timestamps) } else { panic!("Callbacks gone") } From 438b61fada6ef4026678b50131ac7a28ce79e0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 13 Dec 2017 21:36:17 +0100 Subject: [PATCH 06/23] Cache source buffer parent --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 3 +-- gecko-media/gecko/glue/SourceBuffer.cpp | 25 ++++++++----------- .../glue/include/GeckoMediaSourceBuffer.h | 1 - gecko-media/gecko/glue/include/SourceBuffer.h | 5 +++- gecko-media/src/mse/sourcebuffer.rs | 3 +-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index face520..a2e6bc8 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -48,13 +48,12 @@ GeckoMedia_SourceBuffer_Create(size_t aId, void GeckoMedia_SourceBuffer_EvictData(size_t aId, - size_t aParentId, int64_t aLength, bool* aBufferFull) { IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) - reflector->mSourceBuffer->EvictData(aParentId, aLength, aBufferFull); + reflector->mSourceBuffer->EvictData(aLength, aBufferFull); } SourceBuffer* diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index bb9def6..b37664d 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -7,7 +7,6 @@ #include "SourceBuffer.h" #include "GeckoMediaSource.h" -#include "MediaSource.h" #include "MediaSourceDemuxer.h" #include "mozilla/Logging.h" @@ -33,38 +32,34 @@ SourceBuffer::SourceBuffer(GeckoMediaSourceBufferImpl aImpl, return; } - auto parent = GetMediaSource(aParentId); - if (NS_WARN_IF(!parent)) { - return; - } + mMediaSource = GetMediaSource(aParentId); + MOZ_ASSERT(mMediaSource); mTrackBuffersManager = - new TrackBuffersManager(parent->GetDecoder(), mime.value()); + new TrackBuffersManager(mMediaSource->GetDecoder(), mime.value()); MSE_DEBUG("Create mTrackBuffersManager=%p", mTrackBuffersManager.get()); - parent->GetDecoder()->GetDemuxer()->AttachSourceBuffer(mTrackBuffersManager); + mMediaSource->GetDecoder()->GetDemuxer()->AttachSourceBuffer(mTrackBuffersManager); } media::TimeIntervals SourceBuffer::GetTimeIntervals() { + MOZ_ASSERT(NS_IsMainThread()); return mTrackBuffersManager->Buffered(); } void -SourceBuffer::EvictData(size_t aParentId, size_t aLength, bool* aBufferFull) +SourceBuffer::EvictData(size_t aLength, bool* aBufferFull) { - typedef TrackBuffersManager::EvictDataResult Result; + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mMediaSource); - auto parent = GetMediaSource(aParentId); - if (NS_WARN_IF(!parent)) { - *aBufferFull = true; - return; - } + typedef TrackBuffersManager::EvictDataResult Result; Result evicted = mTrackBuffersManager->EvictData( - media::TimeUnit::FromSeconds(parent->GetDecoder()->GetCurrentTime()), + media::TimeUnit::FromSeconds(mMediaSource->GetDecoder()->GetCurrentTime()), aLength); *aBufferFull = (evicted == Result::BUFFER_FULL); diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index fc87cd2..db9bc7f 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -78,7 +78,6 @@ GeckoMedia_SourceBuffer_Shutdown(size_t aId); void GeckoMedia_SourceBuffer_EvictData(size_t aId, - size_t aParentId, int64_t aLength, bool* aBufferFull); } diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index bf494ff..c6fb621 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -8,6 +8,7 @@ #define mozilla_dom_SourceBuffer_h_ #include "GeckoMediaSourceBuffer.h" +#include "MediaSource.h" #include "nsISupportsImpl.h" #include "SourceBufferAttributes.h" #include "TimeUnits.h" @@ -28,13 +29,15 @@ class SourceBuffer final media::TimeIntervals GetTimeIntervals(); - void EvictData(size_t aParentId, size_t aLength, bool* aBufferFull); + void EvictData(size_t aLength, bool* aBufferFull); private: ~SourceBuffer(){}; SourceBufferAttributes mCurrentAttributes; + RefPtr mMediaSource; + RefPtr mTrackBuffersManager; }; diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 7c409d7..76246f7 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -33,14 +33,13 @@ impl SourceBuffer { }) } - pub fn evict_coded_frames(&self, parent_id: usize, len: usize, buffer_full: &mut bool) { + pub fn evict_coded_frames(&self, len: usize, buffer_full: &mut bool) { let id = self.id; let (sender, receiver) = mpsc::channel(); self.gecko_media.queue_task(move || unsafe { sender .send(GeckoMedia_SourceBuffer_EvictData( id, - parent_id, len as i64, buffer_full, )) From 8e0cad04e648bb9646cd250ef7c868672b334674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 13 Dec 2017 23:42:17 +0100 Subject: [PATCH 07/23] Implement MediaSource::SourceBuffers and MediaSource::ActiveSourceBuffers --- gecko-media/gecko/glue/MediaSource.cpp | 49 +++++++++++++------ .../gecko/glue/include/GeckoMediaSource.h | 2 + gecko-media/gecko/glue/include/MediaSource.h | 9 +--- gecko-media/src/mse/mediasource.rs | 10 ++++ 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index ee4b48b..12665f0 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -10,6 +10,7 @@ #include "AOMDecoder.h" #endif #include "DecoderTraits.h" +#include "GeckoMediaMacros.h" #include "MediaContainerType.h" #include "MediaMIMETypes.h" #include "mozilla/Logging.h" @@ -79,14 +80,40 @@ MediaSource::~MediaSource() (*mImpl.mFree)(mImpl.mContext); } +SourceBufferList* +MediaSource::SourceBuffers() +{ + MOZ_ASSERT(NS_IsMainThread()); + + CALLBACK_GUARD(GetSourceBuffers, nullptr); + size_t* id = (*mImpl.mGetSourceBuffers)(mImpl.mContext); + if (NS_WARN_IF(!id)) { + return nullptr; + } + + return GetSourceBufferList(*id); +} + +SourceBufferList* +MediaSource::ActiveSourceBuffers() +{ + MOZ_ASSERT(NS_IsMainThread()); + + CALLBACK_GUARD(GetActiveSourceBuffers, nullptr); + size_t* id = (*mImpl.mGetActiveSourceBuffers)(mImpl.mContext); + if (NS_WARN_IF(!id)) { + return nullptr; + } + + return GetSourceBufferList(*id); +} + double MediaSource::Duration() { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mImpl.mContext || !mImpl.mGetDuration)) { - return 0; - } - return (*mImpl.mGetDuration)(mImpl.mContext); + CALLBACK_GUARD(GetDuration, 0); + return CALLBACK_CALL(GetDuration); } void @@ -104,22 +131,16 @@ MediaSourceReadyState MediaSource::ReadyState() { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mImpl.mContext || !mImpl.mGetReadyState)) { - return MediaSourceReadyState::Unknown; - } - - return (*mImpl.mGetReadyState)(mImpl.mContext); + CALLBACK_GUARD(GetReadyState, MediaSourceReadyState::Unknown); + return CALLBACK_CALL(GetReadyState); } bool MediaSource::HasLiveSeekableRange() { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mImpl.mContext || !mImpl.mHasLiveSeekableRange)) { - return false; - } - - return (*mImpl.mHasLiveSeekableRange)(mImpl.mContext); + CALLBACK_GUARD(HasLiveSeekableRange, false); + return CALLBACK_CALL(HasLiveSeekableRange); } media::TimeInterval diff --git a/gecko-media/gecko/glue/include/GeckoMediaSource.h b/gecko-media/gecko/glue/include/GeckoMediaSource.h index 24b8045..2fff745 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSource.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSource.h @@ -38,6 +38,8 @@ struct GeckoMediaSourceImpl double (*mGetDuration)(void*); bool (*mHasLiveSeekableRange)(void*); GeckoMediaTimeInterval (*mGetLiveSeekableRange)(void*); + size_t* (*mGetSourceBuffers)(void*); + size_t* (*mGetActiveSourceBuffers)(void*); }; mozilla::dom::MediaSource* diff --git a/gecko-media/gecko/glue/include/MediaSource.h b/gecko-media/gecko/glue/include/MediaSource.h index 19a198e..e47ea84 100644 --- a/gecko-media/gecko/glue/include/MediaSource.h +++ b/gecko-media/gecko/glue/include/MediaSource.h @@ -23,13 +23,8 @@ class MediaSource final MediaSource(GeckoMediaSourceImpl aGeckoMediaSourceImpl); - SourceBufferList* ActiveSourceBuffers() - { - // TODO get from mImpl. - RefPtr list = - new SourceBufferList(GeckoMediaSourceBufferListImpl()); - return list; - } + SourceBufferList* SourceBuffers(); + SourceBufferList* ActiveSourceBuffers(); double Duration(); void DurationChange(const double aDuration); diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index 8faed47..56cbc1d 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -47,6 +47,12 @@ pub trait MediaSourceImpl { fn has_live_seekable_range(&self) -> bool; /// MediaSource live seekable range getter. fn get_live_seekable_range(&self) -> GeckoMediaTimeInterval; + /// Get the list of SourceBuffer objects associated with this MediaSource. + fn get_source_buffers(&self) -> *mut usize; + /// Get the subset of sourceBuffers that are providing the selected video + /// track, the enabled audio track(s), and the "showing" or "hidden" text + /// track(s). + fn get_active_source_buffers(&self) -> *mut usize; } fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { @@ -59,6 +65,8 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { impl_simple_ffi_getter_wrapper!(get_duration, f64); impl_simple_ffi_getter_wrapper!(has_live_seekable_range, bool); impl_simple_ffi_getter_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); + impl_simple_ffi_getter_wrapper!(get_source_buffers, *mut usize); + impl_simple_ffi_getter_wrapper!(get_active_source_buffers, *mut usize); GeckoMediaSourceImpl { mContext: Box::into_raw(Box::new(Wrapper { @@ -69,5 +77,7 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { mGetDuration: Some(get_duration), mHasLiveSeekableRange: Some(has_live_seekable_range), mGetLiveSeekableRange: Some(get_live_seekable_range), + mGetSourceBuffers: Some(get_source_buffers), + mGetActiveSourceBuffers: Some(get_active_source_buffers), } } From f719a66831d85404c1a765cc43080b0879623b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Sun, 17 Dec 2017 01:41:24 +0100 Subject: [PATCH 08/23] Implement MediaSource::SetReadyState --- gecko-media/gecko/.DS_Store | Bin 0 -> 6148 bytes gecko-media/gecko/glue/MediaSource.cpp | 1 - .../gecko/glue/include/GeckoMediaSource.h | 1 + gecko-media/gecko/glue/include/MediaSource.h | 3 +++ gecko-media/gecko/test/test.cpp | 7 +++++++ gecko-media/src/mse/mediasource.rs | 4 ++++ 6 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 gecko-media/gecko/.DS_Store diff --git a/gecko-media/gecko/.DS_Store b/gecko-media/gecko/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3246446b21042644cbd92d0b4ce9086acdca977b GIT binary patch literal 6148 zcmeHK%Sr=55Ue%<1G(hrael!+7((&|`2iu3AcPf!c;1uW<)>NwKnxo%LM~Db-8IwG zHOtmvdmDhQ&xc1~1zmo?*J;X10VyB_q<|EV0)JG%doOLVOjMKtQa}oPE8yRUMtAImV`6+d7-9q<&Y2G5 zI%WxC^8~RMj)~0BEUCn#T8$W%bmm*t^};bR>986;te$K&p;$bf_qQmA^+ZJ}AO$WJ znB{im{eMgUW&Xb;X(t7wz`s(!=9}GS%~z`4I(a$owT=En_nJ?-8`nW$h;~ejcFc{p e mDecoder; diff --git a/gecko-media/gecko/test/test.cpp b/gecko-media/gecko/test/test.cpp index dc6004d..7360159 100644 --- a/gecko-media/gecko/test/test.cpp +++ b/gecko-media/gecko/test/test.cpp @@ -580,6 +580,12 @@ GetReadyState(void* aContext) return static_cast(aContext)->readyState; } +void +SetReadyState(void* aContext, MediaSourceReadyState aState) +{ + static_cast(aContext)->readyState = aState; +} + bool HasLiveSeekableRange(void* aContext) { @@ -616,6 +622,7 @@ TestGeckoMediaSource() &test1, /* mContext */ &MediaSourceFree, /* mFree */ &GetReadyState, /* mGetReadyState */ + &SetReadyState, /* mSetReadyState */ &GetDuration, /* mGetDuration */ &HasLiveSeekableRange, /* mHasLiveSeekableRange */ &LiveSeekableRange /* mGetLiveSeekableRange */ diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index 56cbc1d..5b39492 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -41,6 +41,8 @@ impl MediaSource { pub trait MediaSourceImpl { /// MediaSource ready state getter. fn get_ready_state(&self) -> i32; + /// MediaSource ready state setter. + fn set_ready_state(&self, i32); /// MediaSource duration getter. fn get_duration(&self) -> f64; /// Tell whether a media source has live seekable range or not. @@ -62,6 +64,7 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { def_gecko_callbacks_ffi_wrapper!(Rc); impl_simple_ffi_getter_wrapper!(get_ready_state, i32); + impl_simple_ffi_setter_wrapper!(set_ready_state, i32); impl_simple_ffi_getter_wrapper!(get_duration, f64); impl_simple_ffi_getter_wrapper!(has_live_seekable_range, bool); impl_simple_ffi_getter_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); @@ -74,6 +77,7 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { })) as *mut c_void, mFree: Some(free), mGetReadyState: Some(get_ready_state), + mSetReadyState: Some(set_ready_state), mGetDuration: Some(get_duration), mHasLiveSeekableRange: Some(has_live_seekable_range), mGetLiveSeekableRange: Some(get_live_seekable_range), From 5eba8086f576742f3edbe91bb288f08aa5da6ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Sun, 17 Dec 2017 03:47:24 +0100 Subject: [PATCH 09/23] Implement MediaSource::Attach and MediaSource::Detach --- gecko-media/gecko/glue/MediaSource.cpp | 37 +++++++++++++++++++ .../gecko/glue/include/GeckoMediaSource.h | 2 + gecko-media/gecko/glue/include/MediaSource.h | 5 ++- gecko-media/src/mse/mediasource.rs | 8 ++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index 741c31b..527a0cd 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -134,6 +134,43 @@ MediaSource::ReadyState() return CALLBACK_CALL(GetReadyState); } +bool +MediaSource::Attach(MediaSourceDecoder* aDecoder) +{ + MOZ_ASSERT(NS_IsMainThread()); + MSE_DEBUG("Attach(aDecoder=%p) owner=%p", aDecoder, aDecoder->GetOwner()); + MOZ_ASSERT(aDecoder); + MOZ_ASSERT(aDecoder->GetOwner()); + if (ReadyState() != MediaSourceReadyState::Closed) { + return false; + } + MOZ_ASSERT(!mDecoder); + mDecoder = aDecoder; + mDecoder->AttachMediaSource(this); + SetReadyState(MediaSourceReadyState::Open); + return true; +} + +void +MediaSource::Detach() +{ + MOZ_ASSERT(NS_IsMainThread()); + // TODO MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty()); + MSE_DEBUG("mDecoder=%p owner=%p", + mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr); + if (!mDecoder) { + MOZ_ASSERT(ReadyState() == MediaSourceReadyState::Closed); + MOZ_ASSERT(ActiveSourceBuffers()->Length() ==0 && + SourceBuffers()->Length() == 0); + return; + } + SetReadyState(MediaSourceReadyState::Closed); +// mImpl.ClearActiveSourceBuffers(); +// mImpl.ClearSourceBuffers(); + mDecoder->DetachMediaSource(); + mDecoder = nullptr; +} + bool MediaSource::HasLiveSeekableRange() { diff --git a/gecko-media/gecko/glue/include/GeckoMediaSource.h b/gecko-media/gecko/glue/include/GeckoMediaSource.h index 450a302..b499237 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSource.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSource.h @@ -41,6 +41,8 @@ struct GeckoMediaSourceImpl GeckoMediaTimeInterval (*mGetLiveSeekableRange)(void*); size_t* (*mGetSourceBuffers)(void*); size_t* (*mGetActiveSourceBuffers)(void*); + void (*mClearSourceBuffers)(void*); + void (*mClearActiveSourceBuffers)(void*); }; mozilla::dom::MediaSource* diff --git a/gecko-media/gecko/glue/include/MediaSource.h b/gecko-media/gecko/glue/include/MediaSource.h index 8acddcf..b995cb5 100644 --- a/gecko-media/gecko/glue/include/MediaSource.h +++ b/gecko-media/gecko/glue/include/MediaSource.h @@ -32,7 +32,10 @@ class MediaSource final MediaSourceReadyState ReadyState(); - void Detach() { /* TODO */} + // 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(); bool HasLiveSeekableRange(); media::TimeInterval LiveSeekableRange(); diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index 5b39492..b9e4b00 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -55,6 +55,10 @@ pub trait MediaSourceImpl { /// track, the enabled audio track(s), and the "showing" or "hidden" text /// track(s). fn get_active_source_buffers(&self) -> *mut usize; + // Clear the list of SourceBuffer objects. + fn clear_source_buffers(&self); + // Clear the list of active SourceBuffer objects. + fn clear_active_source_buffers(&self); } fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { @@ -70,6 +74,8 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { impl_simple_ffi_getter_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); impl_simple_ffi_getter_wrapper!(get_source_buffers, *mut usize); impl_simple_ffi_getter_wrapper!(get_active_source_buffers, *mut usize); + impl_simple_ffi_callback_wrapper!(clear_source_buffers); + impl_simple_ffi_callback_wrapper!(clear_active_source_buffers); GeckoMediaSourceImpl { mContext: Box::into_raw(Box::new(Wrapper { @@ -83,5 +89,7 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { mGetLiveSeekableRange: Some(get_live_seekable_range), mGetSourceBuffers: Some(get_source_buffers), mGetActiveSourceBuffers: Some(get_active_source_buffers), + mClearSourceBuffers: Some(clear_source_buffers), + mClearActiveSourceBuffers: Some(clear_active_source_buffers), } } From 18dd922010403235103b4ebcc6ec94c4fda9a172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Mon, 18 Dec 2017 17:44:41 +0100 Subject: [PATCH 10/23] Make MediaSourceDecoder inherit from GeckoMediaDecoder --- gecko-media/data/header_files.json | 1 - gecko-media/gecko/glue/MediaSourceDecoder.cpp | 4 ++-- gecko-media/gecko/{ => glue}/include/MediaSourceDecoder.h | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) rename gecko-media/gecko/{ => glue}/include/MediaSourceDecoder.h (97%) diff --git a/gecko-media/data/header_files.json b/gecko-media/data/header_files.json index de543d6..b7e877e 100644 --- a/gecko-media/data/header_files.json +++ b/gecko-media/data/header_files.json @@ -103,7 +103,6 @@ "MediaResult.h": "dom/media/MediaResult.h", "MediaSegment.h": "dom/media/MediaSegment.h", "MediaShutdownManager.h": "dom/media/MediaShutdownManager.h", - "MediaSourceDecoder.h": "dom/media/mediasource/MediaSourceDecoder.h", "MediaSourceDemuxer.h": "dom/media/mediasource/MediaSourceDemuxer.h", "MediaSourceUtils.h": "dom/media/mediasource/MediaSourceUtils.h", "MediaStatistics.h": "dom/media/MediaStatistics.h", diff --git a/gecko-media/gecko/glue/MediaSourceDecoder.cpp b/gecko-media/gecko/glue/MediaSourceDecoder.cpp index cbd49d2..6c15943 100644 --- a/gecko-media/gecko/glue/MediaSourceDecoder.cpp +++ b/gecko-media/gecko/glue/MediaSourceDecoder.cpp @@ -27,7 +27,7 @@ using namespace mozilla::media; namespace mozilla { MediaSourceDecoder::MediaSourceDecoder(MediaDecoderInit& aInit) - : MediaDecoder(aInit) + : GeckoMediaDecoder(aInit) , mMediaSource(nullptr) , mEnded(false) { @@ -177,7 +177,7 @@ MediaSourceDecoder::Shutdown() } mDemuxer = nullptr; - MediaDecoder::Shutdown(); + GeckoMediaDecoder::Shutdown(); } void diff --git a/gecko-media/gecko/include/MediaSourceDecoder.h b/gecko-media/gecko/glue/include/MediaSourceDecoder.h similarity index 97% rename from gecko-media/gecko/include/MediaSourceDecoder.h rename to gecko-media/gecko/glue/include/MediaSourceDecoder.h index a4825ff..572bb33 100644 --- a/gecko-media/gecko/include/MediaSourceDecoder.h +++ b/gecko-media/gecko/glue/include/MediaSourceDecoder.h @@ -7,7 +7,7 @@ #ifndef MOZILLA_MEDIASOURCEDECODER_H_ #define MOZILLA_MEDIASOURCEDECODER_H_ -#include "MediaDecoder.h" +#include "GeckoMediaDecoder.h" #include "mozilla/RefPtr.h" namespace mozilla { @@ -24,7 +24,7 @@ class MediaSource; DDLoggedTypeDeclNameAndBase(MediaSourceDecoder, MediaDecoder); class MediaSourceDecoder - : public MediaDecoder + : public GeckoMediaDecoder , public DecoderDoctorLifeLogger { public: From d5e7c190e96c48d351753ac35b744a9359036715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Mon, 18 Dec 2017 19:02:47 +0100 Subject: [PATCH 11/23] Clear source buffers when detaching from media decoder --- gecko-media/gecko/glue/MediaSource.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index 527a0cd..e2ea847 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -155,6 +155,8 @@ void MediaSource::Detach() { MOZ_ASSERT(NS_IsMainThread()); + CALLBACK_GUARD_VOID(ClearActiveSourceBuffers); + CALLBACK_GUARD_VOID(ClearSourceBuffers); // TODO MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty()); MSE_DEBUG("mDecoder=%p owner=%p", mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr); @@ -165,8 +167,8 @@ MediaSource::Detach() return; } SetReadyState(MediaSourceReadyState::Closed); -// mImpl.ClearActiveSourceBuffers(); -// mImpl.ClearSourceBuffers(); + CALLBACK_CALL(ClearActiveSourceBuffers); + CALLBACK_CALL(ClearSourceBuffers); mDecoder->DetachMediaSource(); mDecoder = nullptr; } From 8d22f4752872519590da50d7187109951e5aa049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Tue, 19 Dec 2017 13:04:52 +0100 Subject: [PATCH 12/23] Fix tests --- gecko-media/gecko/glue/GeckoMediaDecoder.cpp | 1 - gecko-media/gecko/glue/MediaSource.cpp | 2 +- gecko-media/gecko/glue/SourceBuffer.cpp | 10 +- gecko-media/gecko/glue/include/MediaSource.h | 1 - gecko-media/gecko/test/test.cpp | 383 +++++++++++++------ gecko-media/src/lib.rs | 139 ++++--- 6 files changed, 362 insertions(+), 174 deletions(-) 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 7360159..b36bed1 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" @@ -559,31 +562,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 @@ -595,147 +768,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 @@ -744,6 +896,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 c3cd6f9..5d7eeff 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -46,10 +46,13 @@ pub use timestamp::TimeStamp; #[cfg(test)] mod tests { use player::{Metadata, PlanarYCbCrImage, Player, PlayerEventSink}; + use std::cell::Cell; use std::ffi::CString; use std::fs::File; use std::io::prelude::*; use std::ops::Range; + use std::ptr; + use std::rc::Rc; use std::sync::{Mutex, mpsc}; use {CanPlayType, GeckoMedia, GeckoMediaSource, GeckoMediaSourceImpl, GeckoMediaTimeInterval}; @@ -94,6 +97,7 @@ mod tests { Buffered(Vec>), Seekable(Vec>), } + fn create_test_player(path: &str, mime: &str) -> (Player, mpsc::Receiver) { let (sender, receiver) = mpsc::channel(); struct Sink { @@ -278,68 +282,95 @@ mod tests { assert!(reached_seekable); } - fn test_media_source() { - use std::rc::Rc; - #[derive(Clone, Copy)] - #[allow(dead_code)] - enum MediaSourceReadyState { - Closed, - Open, - Ended, - Unknown, - } - struct MediaSourceValues { - pub ready_state: MediaSourceReadyState, - pub duration: f64, + #[derive(Clone, Copy)] + #[allow(dead_code)] + enum MediaSourceReadyState { + Closed, + Open, + Ended, + } + + impl From for i32 { + fn from(state: MediaSourceReadyState) -> Self { + match state { + MediaSourceReadyState::Closed => 0, + MediaSourceReadyState::Open => 1, + MediaSourceReadyState::Ended => 2, + } } - struct MediaSourceDom { - pub values: Rc, - pub media_source_impl: Rc, - pub gecko_media_source: GeckoMediaSource, + } + + impl From for MediaSourceReadyState { + fn from(state: i32) -> Self { + match state { + 0 => MediaSourceReadyState::Closed, + 1 => MediaSourceReadyState::Open, + 2 => MediaSourceReadyState::Ended, + _ => unreachable!(), + } } - impl MediaSourceDom { - pub fn new() -> Self { - let values = Rc::new(MediaSourceValues { - ready_state: MediaSourceReadyState::Closed, - duration: 0., - }); - let media_source_impl = Rc::new(MediaSourceImpl { - values: values.clone(), - }); - let weak_impl = Rc::downgrade(&media_source_impl); - Self { - values, - media_source_impl, - gecko_media_source: GeckoMedia::create_media_source(weak_impl).unwrap(), - } + } + + struct MediaSourceValues { + pub ready_state: Cell, + pub duration: f64, + } + + struct MediaSourceDom { + pub values: Rc, + pub media_source_impl: Rc, + pub gecko_media_source: GeckoMediaSource, + } + impl MediaSourceDom { + pub fn new() -> Self { + let values = Rc::new(MediaSourceValues { + ready_state: Cell::new(MediaSourceReadyState::Closed), + duration: 0., + }); + let media_source_impl = Rc::new(MediaSourceImpl { + values: values.clone(), + }); + let weak_impl = Rc::downgrade(&media_source_impl); + Self { + values, + media_source_impl, + gecko_media_source: GeckoMedia::create_media_source(weak_impl).unwrap(), } } - struct MediaSourceImpl { - pub values: Rc, + } + struct MediaSourceImpl { + pub values: Rc, + } + impl GeckoMediaSourceImpl for MediaSourceImpl { + fn get_ready_state(&self) -> i32 { + self.values.ready_state.get().into() } - 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, - } - } - fn get_duration(&self) -> f64 { - self.values.duration - } - fn has_live_seekable_range(&self) -> bool { - false - } - fn get_live_seekable_range(&self) -> GeckoMediaTimeInterval { - GeckoMediaTimeInterval { - mStart: 0., - mEnd: 0., - } + fn set_ready_state(&self, state: i32) { + self.values.ready_state.set(state.into()); + } + fn get_duration(&self) -> f64 { + self.values.duration + } + fn has_live_seekable_range(&self) -> bool { + false + } + fn get_live_seekable_range(&self) -> GeckoMediaTimeInterval { + GeckoMediaTimeInterval { + mStart: 0., + 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) {} + } + fn test_media_source() { let _ = MediaSourceDom::new(); let gecko_media = GeckoMedia::get().unwrap(); From f218752ed2b161aabebbe9cd11c8f5bdcbc28032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 20 Dec 2017 09:19:26 +0100 Subject: [PATCH 13/23] Implement SourceBuffer::AppendData --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 19 ++- gecko-media/gecko/glue/MediaSource.cpp | 41 ++++++- gecko-media/gecko/glue/SourceBuffer.cpp | 114 ++++++++++++++++++ .../glue/include/GeckoMediaSourceBuffer.h | 18 ++- gecko-media/gecko/glue/include/MediaSource.h | 19 ++- gecko-media/gecko/glue/include/SourceBuffer.h | 26 ++++ .../glue/include/SourceBufferAttributes.h | 4 + gecko-media/src/lib.rs | 99 ++++++++++++++- gecko-media/src/mse/mediasource.rs | 2 +- gecko-media/src/mse/sourcebuffer.rs | 57 ++++++++- 10 files changed, 383 insertions(+), 16 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index a2e6bc8..69f0713 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -47,15 +47,28 @@ GeckoMedia_SourceBuffer_Create(size_t aId, } void -GeckoMedia_SourceBuffer_EvictData(size_t aId, - int64_t aLength, - bool* aBufferFull) +GeckoMedia_SourceBuffer_EvictData(size_t aId, size_t aLength, bool* aBufferFull) { IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) reflector->mSourceBuffer->EvictData(aLength, aBufferFull); } +void +GeckoMedia_SourceBuffer_AppendData(size_t aId, + const uint8_t* aData, + size_t aLength, + success_callback_t aSuccessCb, + void* aSuccessCbContext, + error_callback_t aErrorCb, + void* aErrorCbContext) +{ + IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) + + reflector->mSourceBuffer->AppendData( + aData, aLength, aSuccessCb, aSuccessCbContext, aErrorCb, aErrorCbContext); +} + SourceBuffer* GetSourceBuffer(const size_t aId) { diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index 9cef7cc..81efd07 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -82,6 +82,7 @@ MediaSource::~MediaSource() SourceBufferList* MediaSource::SourceBuffers() { + // TODO cache in mSourceBuffers MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD(GetSourceBuffers, nullptr); @@ -96,6 +97,7 @@ MediaSource::SourceBuffers() SourceBufferList* MediaSource::ActiveSourceBuffers() { + // TODO cache in mActiveSourceBuffers MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD(GetActiveSourceBuffers, nullptr); @@ -157,12 +159,13 @@ MediaSource::Detach() MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD_VOID(ClearActiveSourceBuffers); CALLBACK_GUARD_VOID(ClearSourceBuffers); - // TODO MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty()); + MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty()); MSE_DEBUG("mDecoder=%p owner=%p", - mDecoder.get(), mDecoder ? mDecoder->GetOwner() : nullptr); + mDecoder.get(), + mDecoder ? mDecoder->GetOwner() : nullptr); if (!mDecoder) { MOZ_ASSERT(ReadyState() == MediaSourceReadyState::Closed); - MOZ_ASSERT(ActiveSourceBuffers()->Length() ==0 && + MOZ_ASSERT(ActiveSourceBuffers()->Length() == 0 && SourceBuffers()->Length() == 0); return; } @@ -226,6 +229,38 @@ MediaSource::EndOfStreamError(const GeckoMediaEndOfStreamError aError) } } +RefPtr +MediaSource::SourceBufferIsActive(SourceBuffer* aSourceBuffer) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // TODO: append active source buffer + + if (!mDecoder) { + return ActiveCompletionPromise::CreateAndResolve(true, __func__); + } + + mDecoder->NotifyInitDataArrived(); + + // Add our promise to the queue. + // It will be resolved once the HTMLMediaElement modifies its readyState. + MozPromiseHolder holder; + RefPtr promise = holder.Ensure(__func__); + mCompletionPromises.AppendElement(Move(holder)); + return promise; +} + +void +MediaSource::CompletePendingTransactions() +{ + MOZ_ASSERT(NS_IsMainThread()); + MSE_DEBUG("Resolving %u promises", unsigned(mCompletionPromises.Length())); + for (auto& promise : mCompletionPromises) { + promise.Resolve(true, __func__); + } + mCompletionPromises.Clear(); +} + /* static */ bool MediaSource::IsTypeSupported(const char* aType) diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 54deb0f..6cce6c4 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -6,6 +6,7 @@ #include "SourceBuffer.h" +#include "AbstractThread.h" #include "GeckoMediaSource.h" #include "MediaSourceDemuxer.h" #include "mozilla/Logging.h" @@ -69,5 +70,118 @@ SourceBuffer::EvictData(size_t aLength, bool* aBufferFull) *aBufferFull = (evicted == Result::BUFFER_FULL); } +void +SourceBuffer::AppendData(const uint8_t* aData, + size_t aLength, + success_callback_t aSuccessCb, + void* aSuccessCbContext, + error_callback_t aErrorCb, + void* aErrorCbContext) +{ + MSE_DEBUG("AppendData(aLength=%zu)", aLength); + + RefPtr data = new MediaByteBuffer(); + if (NS_WARN_IF(!data->AppendElements(aData, aLength, fallible))) { + return (*aErrorCb)(aErrorCbContext, + uint32_t(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR)); + } + + if (NS_WARN_IF(!mTrackBuffersManager)) { + return (*aErrorCb)(aErrorCbContext, uint32_t(NS_ERROR_NOT_INITIALIZED)); + } + + RefPtr self = this; + mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes) + ->Then(AbstractThread::MainThread(), + __func__, + [self, this, aSuccessCb, aSuccessCbContext]( + const SourceBufferTask::AppendBufferResult& aResult) { + AppendDataCompletedWithSuccess( + aResult, aSuccessCb, aSuccessCbContext); + }, + [self, this, aErrorCb, aErrorCbContext](const MediaResult& aError) { + AppendDataErrored(aError, aErrorCb, aErrorCbContext); + }) + ->Track(mPendingAppend); +} + +void +SourceBuffer::AppendDataCompletedWithSuccess( + const SourceBufferTask::AppendBufferResult& aResult, + success_callback_t aSuccessCb, + void* aSuccessCbContext) +{ + MOZ_ASSERT(mCurrentAttributes.GetUpdating()); + mPendingAppend.Complete(); + + if (aResult.first()) { + if (!mCurrentAttributes.GetActive()) { + mCurrentAttributes.SetActive(true); + MSE_DEBUG("Init segment received"); + RefPtr self = this; + mMediaSource->SourceBufferIsActive(this) + ->Then(AbstractThread::MainThread(), + __func__, + [self, this, aSuccessCb, aSuccessCbContext]() { + MSE_DEBUG("Complete AppendBuffer operation"); + mCompletionPromise.Complete(); + (*aSuccessCb)(aSuccessCbContext); + }) + ->Track(mCompletionPromise); + } + } + if (mCurrentAttributes.GetActive()) { + // Tell our parent decoder that we have received new data + // and send progress event. + mMediaSource->GetDecoder()->NotifyDataArrived(); + } + + mCurrentAttributes = aResult.second(); + + CheckEndTime(); + + if (!mCompletionPromise.Exists()) { + (*aSuccessCb)(aSuccessCbContext); + } + +} + +void +SourceBuffer::AppendDataErrored(const MediaResult& aError, + error_callback_t aErrorCb, + void* aErrorCbContext) +{ + MOZ_ASSERT(mCurrentAttributes.GetUpdating()); + mPendingAppend.Complete(); + + switch (aError.Code()) { + case NS_ERROR_DOM_MEDIA_CANCELED: + // Nothing further to do as the trackbuffer has been shutdown. + // or append was aborted and abort() has handled all the events. + break; + default: + (*aErrorCb)(aErrorCbContext, uint32_t(NS_ERROR_DOM_MEDIA_CANCELED)); + break; + } +} + +void +SourceBuffer::CheckEndTime() +{ + MOZ_ASSERT(NS_IsMainThread()); + // Check if we need to update mMediaSource duration + double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds(); + double duration = mMediaSource->Duration(); + if (endTime > duration) { + mMediaSource->DurationChange(endTime); + } +} + +void +SourceBuffer::ResetParserState() +{ + mTrackBuffersManager->ResetParserState(mCurrentAttributes); +} + } // namespace dom } // namespace mozilla \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index db9bc7f..ec23cc7 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -59,11 +59,18 @@ struct GeckoMediaSourceBufferImpl void (*mSetGroupEndTimestamp)(void*, double); AppendState (*mGetAppendState)(void*); void (*mSetAppendState)(void*, AppendState); + bool (*mGetUpdating)(void*); + void (*mSetUpdating)(void*, bool); + bool (*mGetActive)(void*); + void (*mSetActive)(void*, bool); }; SourceBuffer* GetSourceBuffer(const size_t aId); +typedef void (*success_callback_t)(void*); +typedef void (*error_callback_t)(void*, uint32_t); + extern "C" { // TODO error handling (i.e. aId or aParentId may not be valid) void @@ -78,8 +85,17 @@ GeckoMedia_SourceBuffer_Shutdown(size_t aId); void GeckoMedia_SourceBuffer_EvictData(size_t aId, - int64_t aLength, + size_t aLength, bool* aBufferFull); + +void +GeckoMedia_SourceBuffer_AppendData(size_t aId, + const uint8_t* aData, + size_t aLength, + success_callback_t aSuccessCb, + void* aSuccessCbContext, + error_callback_t aErrorCb, + void* aErrorCbContext); } #endif // GeckoMediaSourceBuffer_h_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/MediaSource.h b/gecko-media/gecko/glue/include/MediaSource.h index beb9798..fcd7d60 100644 --- a/gecko-media/gecko/glue/include/MediaSource.h +++ b/gecko-media/gecko/glue/include/MediaSource.h @@ -32,7 +32,8 @@ class MediaSource final MediaSourceReadyState ReadyState(); - // Attach this MediaSource to Decoder aDecoder. Returns false if already attached. + // Attach this MediaSource to Decoder aDecoder. Returns false if already + // attached. bool Attach(MediaSourceDecoder* aDecoder); void Detach(); @@ -45,16 +46,32 @@ class MediaSource final MediaSourceDecoder* GetDecoder() { return mDecoder; } + // Resolve all CompletionPromise pending. + void CompletePendingTransactions(); + static bool IsTypeSupported(const char* aType); private: + // SourceBuffer uses SourceBufferIsActive. + friend class mozilla::dom::SourceBuffer; + ~MediaSource(); IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetReadyState, MediaSourceReadyState); + typedef MozPromise + ActiveCompletionPromise; + // Return a MozPromise that will be resolved once all related operations are + // completed, or can't progress any further. + // Such as, transition of readyState from HAVE_NOTHING to HAVE_METADATA. + RefPtr SourceBufferIsActive( + SourceBuffer* aSourceBuffer); + GeckoMediaSourceImpl mImpl; RefPtr mDecoder; + + nsTArray> mCompletionPromises; }; } // namespace dom diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index c6fb621..c384357 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -31,14 +31,40 @@ class SourceBuffer final void EvictData(size_t aLength, bool* aBufferFull); + void AppendData(const uint8_t* aData, + size_t aLength, + success_callback_t aSuccessCb, + void* aSuccessCbContext, + error_callback_t aErrorCb, + void* aErrorCbContext); + private: ~SourceBuffer(){}; + void ResetParserState(); + + // If the media segment contains data beyond the current duration, + // then run the duration change algorithm with new duration set to the + // maximum of the current duration and the group end timestamp. + void CheckEndTime(); + + void AppendDataCompletedWithSuccess( + const SourceBufferTask::AppendBufferResult& aResult, + success_callback_t aSuccessCb, + void* aSuccessCbContext); + void AppendDataErrored(const MediaResult& aError, + error_callback_t aErrorCb, + void* aErrorCbContext); + SourceBufferAttributes mCurrentAttributes; RefPtr mMediaSource; RefPtr mTrackBuffersManager; + + MozPromiseRequestHolder mPendingAppend; + MozPromiseRequestHolder + mCompletionPromise; }; } // namespace dom diff --git a/gecko-media/gecko/glue/include/SourceBufferAttributes.h b/gecko-media/gecko/glue/include/SourceBufferAttributes.h index 465bce4..60cbcf8 100644 --- a/gecko-media/gecko/glue/include/SourceBufferAttributes.h +++ b/gecko-media/gecko/glue/include/SourceBufferAttributes.h @@ -51,6 +51,10 @@ class SourceBufferAttributes AppendState, AppendState::WAITING_FOR_SEGMENT) IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetAppendState, AppendState) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetUpdating, bool, false) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetUpdating, bool) + IMPL_GECKO_MEDIA_SIMPLE_GETTER(GetActive, bool, false) + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetActive, bool) double GetApparentTimestampOffset() { diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 5d7eeff..8c68826 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -54,7 +54,9 @@ mod tests { use std::ptr; use std::rc::Rc; use std::sync::{Mutex, mpsc}; - use {CanPlayType, GeckoMedia, GeckoMediaSource, GeckoMediaSourceImpl, GeckoMediaTimeInterval}; + use {CanPlayType, GeckoMedia, GeckoMediaTimeInterval}; + use {GeckoMediaSource, GeckoMediaSourceImpl}; + use {GeckoMediaSourceBuffer, GeckoMediaSourceBufferImpl}; fn test_can_play_type() { let gecko_media = GeckoMedia::get().unwrap(); @@ -337,6 +339,10 @@ mod tests { gecko_media_source: GeckoMedia::create_media_source(weak_impl).unwrap(), } } + + pub fn id(&self) -> usize { + self.gecko_media_source.get_id() + } } struct MediaSourceImpl { pub values: Rc, @@ -392,6 +398,96 @@ mod tests { assert_eq!(gecko_media.is_type_supported("audio/wav"), false); } + struct SourceBufferAttributes {} + + impl SourceBufferAttributes { + pub fn new() -> Self { + SourceBufferAttributes {} + } + } + + impl GeckoMediaSourceBufferImpl for SourceBufferAttributes { + fn get_append_window_start(&self) -> f64 { + 0. + } + fn set_append_window_start(&self, _: f64) {} + fn get_append_window_end(&self) -> f64 { + 0. + } + fn set_append_window_end(&self, _: f64) {} + fn get_timestamp_offset(&self) -> f64 { + 0. + } + fn set_timestamp_offset(&self, _: f64) {} + fn get_append_mode(&self) -> i32 { + 0 + } + fn set_append_mode(&self, _: i32) {} + fn get_group_start_timestamp(&self, _: *mut f64) {} + fn set_group_start_timestamp(&self, _: f64) {} + fn have_group_start_timestamp(&self) -> bool { + false + } + fn reset_group_start_timestamp(&self) {} + fn restart_group_start_timestamp(&self) {} + fn get_group_end_timestamp(&self) -> f64 { + 0. + } + fn set_group_end_timestamp(&self, _: f64) {} + fn get_append_state(&self) -> i32 { + 0 + } + fn set_append_state(&self, _: i32) {} + fn get_updating(&self) -> bool { + false + } + fn set_updating(&self, _: bool) {} + fn get_active(&self) -> bool { + false + } + fn set_active(&self, _: bool) {} + } + + struct SourceBufferDom { + pub attributes: Rc, + pub gecko_media: GeckoMediaSourceBuffer, + } + + impl SourceBufferDom { + pub fn new(parent_media_source: &MediaSourceDom, mime: &str) -> Self { + let attributes = Rc::new(SourceBufferAttributes::new()); + let weak_attributes = Rc::downgrade(&(&attributes)); + Self { + attributes, + gecko_media: GeckoMedia::create_source_buffer(weak_attributes, parent_media_source.id(), mime, false) + .unwrap(), + } + } + + pub fn append_data(&self, data: *const u8, len: usize, success_cb: S, error_cb: E) + where S: Fn(), E: Fn(u32) + { + self.gecko_media.append_data( + data, + len, + success_cb, + error_cb, + ); + } + } + + fn test_source_buffer() { + let media_source = MediaSourceDom::new(); + let source_buffer = SourceBufferDom::new(&media_source, "video/mp4"); + let empty: [u8; 0] = []; + // TODO For now this only tests that the mechanism to send closures through FFI works. + // Should throw error because no decoder is attached yet. + source_buffer.append_data(empty.as_ptr(), empty.len(), || { unreachable!() }, |_| { + // TODO check error code. + assert!(true); + }); + } + #[test] fn run_tests() { GeckoMedia::get().unwrap(); @@ -405,6 +501,7 @@ mod tests { test_basic_playback(); test_seeking(); test_media_source(); + test_source_buffer(); GeckoMedia::shutdown().unwrap(); } } diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index b9e4b00..006fa0f 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -32,7 +32,7 @@ impl MediaSource { let id = self.id; self.gecko_media.queue_task(move || unsafe { GeckoMedia_MediaSource_EndOfStreamError(id, error); - }) + }); } } diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 76246f7..d167005 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -2,9 +2,10 @@ // 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/. -use bindings::GeckoMedia_SourceBuffer_EvictData; use bindings::{GeckoMediaSourceBufferImpl, GeckoMedia_SourceBuffer_Create}; +use bindings::{GeckoMedia_SourceBuffer_AppendData, GeckoMedia_SourceBuffer_EvictData}; use std::ffi::CString; +use std::os::raw::c_void; use std::rc::Rc; use std::sync::mpsc; @@ -38,15 +39,47 @@ impl SourceBuffer { let (sender, receiver) = mpsc::channel(); self.gecko_media.queue_task(move || unsafe { sender - .send(GeckoMedia_SourceBuffer_EvictData( - id, - len as i64, - buffer_full, - )) + .send(GeckoMedia_SourceBuffer_EvictData(id, len, buffer_full)) .unwrap(); }); receiver.recv().unwrap(); } + + pub fn append_data(&self, data: *const u8, len: usize, success_cb: F, error_cb: E) + where + F: FnOnce(), + E: FnOnce(u32), + { + let id = self.id; + let success_cb = &success_cb as *const _ as *mut c_void; + let error_cb = &error_cb as *const _ as *mut c_void; + self.gecko_media.queue_task(move || unsafe { + GeckoMedia_SourceBuffer_AppendData(id, data, len, + Some(success_callback_wrapper::), success_cb, + Some(error_callback_wrapper::), error_cb); + }); + + extern "C" fn success_callback_wrapper(closure: *mut c_void) + where + F: FnOnce(), + { + let opt_closure = closure as *mut Option; + unsafe { + (*opt_closure).take().unwrap()(); + } + } + + extern "C" fn error_callback_wrapper(closure: *mut c_void, error: u32) + where + F: FnOnce(u32), + { + let opt_closure = closure as *mut Option; + unsafe { + (*opt_closure).take().unwrap()(error); + } + } + + } } impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); @@ -71,6 +104,10 @@ pub trait SourceBufferImpl { fn set_group_end_timestamp(&self, f64); fn get_append_state(&self) -> i32; fn set_append_state(&self, i32); + fn get_updating(&self) -> bool; + fn set_updating(&self, bool); + fn get_active(&self) -> bool; + fn set_active(&self, bool); } pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBufferImpl { @@ -95,6 +132,10 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff impl_simple_ffi_setter_wrapper!(set_group_end_timestamp, f64); impl_simple_ffi_getter_wrapper!(get_append_state, i32); impl_simple_ffi_setter_wrapper!(set_append_state, i32); + impl_simple_ffi_getter_wrapper!(get_updating, bool); + impl_simple_ffi_setter_wrapper!(set_updating, bool); + impl_simple_ffi_getter_wrapper!(get_active, bool); + impl_simple_ffi_setter_wrapper!(set_active, bool); unsafe extern "C" fn get_group_start_timestamp(ptr: *mut c_void, timestamp: *mut f64) { let wrapper = &*(ptr as *mut Wrapper); @@ -123,5 +164,9 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff mSetGroupEndTimestamp: Some(set_group_end_timestamp), mGetAppendState: Some(get_append_state), mSetAppendState: Some(set_append_state), + mGetUpdating: Some(get_updating), + mSetUpdating: Some(set_updating), + mGetActive: Some(get_active), + mSetActive: Some(set_active), } } From 2ad67d128105d29f73234745d6f741510aeb6c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 20 Dec 2017 11:12:05 +0100 Subject: [PATCH 14/23] Ignore .DS_Store --- .gitignore | 3 ++- gecko-media/gecko/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 gecko-media/gecko/.DS_Store diff --git a/.gitignore b/.gitignore index 04f76d5..40b1c60 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Cargo.lock glue_diffs/ *~ .cargo/ -/NDK/ \ No newline at end of file +/NDK/ +**/.DS_Store diff --git a/gecko-media/gecko/.DS_Store b/gecko-media/gecko/.DS_Store deleted file mode 100644 index 3246446b21042644cbd92d0b4ce9086acdca977b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%Sr=55Ue%<1G(hrael!+7((&|`2iu3AcPf!c;1uW<)>NwKnxo%LM~Db-8IwG zHOtmvdmDhQ&xc1~1zmo?*J;X10VyB_q<|EV0)JG%doOLVOjMKtQa}oPE8yRUMtAImV`6+d7-9q<&Y2G5 zI%WxC^8~RMj)~0BEUCn#T8$W%bmm*t^};bR>986;te$K&p;$bf_qQmA^+ZJ}AO$WJ znB{im{eMgUW&Xb;X(t7wz`s(!=9}GS%~z`4I(a$owT=En_nJ?-8`nW$h;~ejcFc{p e Date: Wed, 20 Dec 2017 11:48:14 +0100 Subject: [PATCH 15/23] Add append and clear hooks to SourceBufferList --- gecko-media/gecko/glue/MediaSource.cpp | 8 +-- gecko-media/gecko/glue/SourceBufferList.cpp | 17 ++---- .../gecko/glue/include/GeckoMediaSource.h | 2 - .../glue/include/GeckoMediaSourceBuffer.h | 1 + .../glue/include/GeckoMediaSourceBufferList.h | 2 + .../gecko/glue/include/SourceBufferList.h | 15 +++-- gecko-media/gecko/test/test.cpp | 59 ++++++++++--------- gecko-media/src/lib.rs | 6 +- gecko-media/src/mse/mediasource.rs | 8 --- gecko-media/src/mse/sourcebuffer.rs | 3 + gecko-media/src/mse/sourcebufferlist.rs | 13 ++++ 11 files changed, 72 insertions(+), 62 deletions(-) diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index 81efd07..db0e937 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -82,7 +82,6 @@ MediaSource::~MediaSource() SourceBufferList* MediaSource::SourceBuffers() { - // TODO cache in mSourceBuffers MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD(GetSourceBuffers, nullptr); @@ -97,7 +96,6 @@ MediaSource::SourceBuffers() SourceBufferList* MediaSource::ActiveSourceBuffers() { - // TODO cache in mActiveSourceBuffers MOZ_ASSERT(NS_IsMainThread()); CALLBACK_GUARD(GetActiveSourceBuffers, nullptr); @@ -157,8 +155,6 @@ void MediaSource::Detach() { MOZ_ASSERT(NS_IsMainThread()); - CALLBACK_GUARD_VOID(ClearActiveSourceBuffers); - CALLBACK_GUARD_VOID(ClearSourceBuffers); MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty()); MSE_DEBUG("mDecoder=%p owner=%p", mDecoder.get(), @@ -170,8 +166,8 @@ MediaSource::Detach() return; } SetReadyState(MediaSourceReadyState::Closed); - CALLBACK_CALL(ClearActiveSourceBuffers); - CALLBACK_CALL(ClearSourceBuffers); + SourceBuffers()->Clear(); + ActiveSourceBuffers()->Clear(); mDecoder->DetachMediaSource(); mDecoder = nullptr; } diff --git a/gecko-media/gecko/glue/SourceBufferList.cpp b/gecko-media/gecko/glue/SourceBufferList.cpp index 6f677af..6de3264 100644 --- a/gecko-media/gecko/glue/SourceBufferList.cpp +++ b/gecko-media/gecko/glue/SourceBufferList.cpp @@ -22,11 +22,8 @@ SourceBufferList::~SourceBufferList() { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mImpl.mContext || !mImpl.mFree)) { - return; - } - - (*mImpl.mFree)(mImpl.mContext); + CALLBACK_GUARD_VOID(Free); + CALLBACK_CALL(Free); } SourceBuffer* @@ -55,16 +52,14 @@ SourceBufferList::IndexedGetter(uint32_t aIndex, bool& aFound) return sourceBuffer; } -uint32_t -SourceBufferList::Length() +void +SourceBufferList::Append(SourceBuffer* aSourceBuffer, bool aNotify) { MOZ_ASSERT(NS_IsMainThread()); - if (NS_WARN_IF(!mImpl.mContext || !mImpl.mLength)) { - return 0; - } + CALLBACK_GUARD_VOID(Append); - return (*mImpl.mLength)(mImpl.mContext); + (*mImpl.mAppend)(mImpl.mContext, (void*)(aSourceBuffer), aNotify); } } // namespace dom diff --git a/gecko-media/gecko/glue/include/GeckoMediaSource.h b/gecko-media/gecko/glue/include/GeckoMediaSource.h index b499237..450a302 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSource.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSource.h @@ -41,8 +41,6 @@ struct GeckoMediaSourceImpl GeckoMediaTimeInterval (*mGetLiveSeekableRange)(void*); size_t* (*mGetSourceBuffers)(void*); size_t* (*mGetActiveSourceBuffers)(void*); - void (*mClearSourceBuffers)(void*); - void (*mClearActiveSourceBuffers)(void*); }; mozilla::dom::MediaSource* diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index ec23cc7..7801f8c 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -42,6 +42,7 @@ struct GeckoMediaSourceBufferImpl { void* mContext; void (*mFree)(void*); + void* (*mOwner)(void*); double (*mGetAppendWindowStart)(void*); void (*mSetAppendWindowStart)(void*, double); double (*mGetAppendWindowEnd)(void*); diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBufferList.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBufferList.h index 8357788..5da559a 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBufferList.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBufferList.h @@ -19,6 +19,8 @@ struct GeckoMediaSourceBufferListImpl void (*mFree)(void*); bool (*mIndexedGetter)(void*, uint32_t, size_t*); uint32_t (*mLength)(void*); + void (*mAppend)(void*, void*, bool); + void (*mClear)(void*); }; namespace mozilla { diff --git a/gecko-media/gecko/glue/include/SourceBufferList.h b/gecko-media/gecko/glue/include/SourceBufferList.h index c30ced9..20cfdcc 100644 --- a/gecko-media/gecko/glue/include/SourceBufferList.h +++ b/gecko-media/gecko/glue/include/SourceBufferList.h @@ -7,13 +7,12 @@ #ifndef mozilla_dom_SourceBufferList_h_ #define mozilla_dom_SourceBufferList_h_ +#include "GeckoMediaMacros.h" #include "GeckoMediaSourceBufferList.h" #include "nsISupportsImpl.h" -namespace mozilla -{ -namespace dom -{ +namespace mozilla { +namespace dom { class SourceBuffer; @@ -26,11 +25,17 @@ class SourceBufferList final SourceBuffer* IndexedGetter(uint32_t aIndex, bool& aFound); - uint32_t Length(); + IMPL_GECKO_MEDIA_SIMPLE_GETTER(Length, uint32_t, 0); private: + friend class MediaSource; + ~SourceBufferList(); + void Append(SourceBuffer* aSourceBuffer, bool aNotify); + + IMPL_GECKO_MEDIA_CALLBACK(Clear); + GeckoMediaSourceBufferListImpl mImpl; }; diff --git a/gecko-media/gecko/test/test.cpp b/gecko-media/gecko/test/test.cpp index b36bed1..bb7de47 100644 --- a/gecko-media/gecko/test/test.cpp +++ b/gecko-media/gecko/test/test.cpp @@ -566,6 +566,8 @@ TestGeckoDecoder() void SourceBufferFree(void* aContext); +void* +SourceBufferOwner(void* aContext); class DummySourceBuffer { @@ -579,6 +581,7 @@ class DummySourceBuffer mImpl = { this, &SourceBufferFree, + &SourceBufferOwner, }; GeckoMedia_SourceBuffer_Create(mId, mImpl, aParentId, "video/mp4", true); } @@ -597,6 +600,12 @@ class DummySourceBuffer }; }; +void* +SourceBufferOwner(void* aContext) +{ + return aContext; +} + void SourceBufferFree(void* aContext) { @@ -605,12 +614,16 @@ SourceBufferFree(void* aContext) /** SourceBufferList **/ +void +SourceBufferListFree(void* aContext); bool IndexedGetter(void* aContext, uint32_t aIndex, size_t* aId); uint32_t Length(void* aContext); void -SourceBufferListFree(void* aContext); +Append(void* aContext, void* aSourceBuffer, bool aNotify); +void +Clear(void* aContext); class DummySourceBufferList { @@ -622,10 +635,7 @@ class DummySourceBufferList , mReleased(false) { mImpl = { - this, - &SourceBufferListFree, - &IndexedGetter, - &Length, + this, &SourceBufferListFree, &IndexedGetter, &Length, &Append, &Clear, }; GeckoMedia_SourceBufferList_Create(mId, mImpl); } @@ -645,6 +655,13 @@ class DummySourceBufferList }; }; +void +SourceBufferListFree(void* aContext) +{ + DummySourceBufferList* sbl = static_cast(aContext); + sbl->mReleased = true; +} + bool IndexedGetter(void* aContext, uint32_t aIndex, size_t* aId) { @@ -663,10 +680,16 @@ Length(void* aContext) } void -SourceBufferListFree(void* aContext) +Append(void* aContext, void* aSourceBuffer, bool aNotify) { - DummySourceBufferList* sbl = static_cast(aContext); - sbl->mReleased = true; + static_cast(aContext)->mSourceBuffers.AppendElement( + static_cast(aSourceBuffer)); +} + +void +Clear(void* aContext) +{ + static_cast(aContext)->mSourceBuffers.Clear(); } /** MediaSource **/ @@ -687,10 +710,6 @@ size_t* GetSourceBuffers(void* aContext); size_t* GetActiveSourceBuffers(void* aContext); -void -ClearSourceBuffers(void* aContext); -void -ClearActiveSourceBuffers(void* aContext); class DummyMediaSource { @@ -716,8 +735,6 @@ class DummyMediaSource &LiveSeekableRange, &GetSourceBuffers, &GetActiveSourceBuffers, - &ClearSourceBuffers, - &ClearActiveSourceBuffers, }; GeckoMedia_MediaSource_Create(mId, mImpl); } @@ -789,20 +806,6 @@ GetActiveSourceBuffers(void* aContext) return &(static_cast(aContext)->mActiveSourceBuffers->mId); } -void -ClearSourceBuffers(void* aContext) -{ - static_cast(aContext) - ->mSourceBuffers->mSourceBuffers.Clear(); -} - -void -ClearActiveSourceBuffers(void* aContext) -{ - static_cast(aContext) - ->mActiveSourceBuffers->mSourceBuffers.Clear(); -} - void TestGeckoMediaSource() { diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 8c68826..81469d4 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -51,6 +51,7 @@ mod tests { use std::fs::File; use std::io::prelude::*; use std::ops::Range; + use std::os::raw::c_void; use std::ptr; use std::rc::Rc; use std::sync::{Mutex, mpsc}; @@ -372,8 +373,6 @@ mod tests { fn get_active_source_buffers(&self) -> *mut usize { ptr::null_mut() } - fn clear_source_buffers(&self) {} - fn clear_active_source_buffers(&self) {} } fn test_media_source() { @@ -407,6 +406,9 @@ mod tests { } impl GeckoMediaSourceBufferImpl for SourceBufferAttributes { + fn owner(&self) -> *mut c_void { + ptr::null_mut() + } fn get_append_window_start(&self) -> f64 { 0. } diff --git a/gecko-media/src/mse/mediasource.rs b/gecko-media/src/mse/mediasource.rs index 006fa0f..b7d9843 100644 --- a/gecko-media/src/mse/mediasource.rs +++ b/gecko-media/src/mse/mediasource.rs @@ -55,10 +55,6 @@ pub trait MediaSourceImpl { /// track, the enabled audio track(s), and the "showing" or "hidden" text /// track(s). fn get_active_source_buffers(&self) -> *mut usize; - // Clear the list of SourceBuffer objects. - fn clear_source_buffers(&self); - // Clear the list of active SourceBuffer objects. - fn clear_active_source_buffers(&self); } fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { @@ -74,8 +70,6 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { impl_simple_ffi_getter_wrapper!(get_live_seekable_range, GeckoMediaTimeInterval); impl_simple_ffi_getter_wrapper!(get_source_buffers, *mut usize); impl_simple_ffi_getter_wrapper!(get_active_source_buffers, *mut usize); - impl_simple_ffi_callback_wrapper!(clear_source_buffers); - impl_simple_ffi_callback_wrapper!(clear_active_source_buffers); GeckoMediaSourceImpl { mContext: Box::into_raw(Box::new(Wrapper { @@ -89,7 +83,5 @@ fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceImpl { mGetLiveSeekableRange: Some(get_live_seekable_range), mGetSourceBuffers: Some(get_source_buffers), mGetActiveSourceBuffers: Some(get_active_source_buffers), - mClearSourceBuffers: Some(clear_source_buffers), - mClearActiveSourceBuffers: Some(clear_active_source_buffers), } } diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index d167005..58c521b 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -87,6 +87,7 @@ impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); /// Users of SourceBuffer pass in an implementation of this trait when creating /// SourceBuffer objects. pub trait SourceBufferImpl { + fn owner(&self) -> *mut c_void; fn get_append_window_start(&self) -> f64; fn set_append_window_start(&self, f64); fn get_append_window_end(&self) -> f64; @@ -116,6 +117,7 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff def_gecko_callbacks_ffi_wrapper!(Rc); + impl_simple_ffi_getter_wrapper!(owner, *mut c_void); impl_simple_ffi_getter_wrapper!(get_append_window_start, f64); impl_simple_ffi_setter_wrapper!(set_append_window_start, f64); impl_simple_ffi_getter_wrapper!(get_append_window_end, f64); @@ -147,6 +149,7 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff callbacks, })) as *mut c_void, mFree: Some(free), + mOwner: Some(owner), mGetAppendWindowStart: Some(get_append_window_start), mSetAppendWindowStart: Some(set_append_window_start), mGetAppendWindowEnd: Some(get_append_window_end), diff --git a/gecko-media/src/mse/sourcebufferlist.rs b/gecko-media/src/mse/sourcebufferlist.rs index f651bfc..5efe909 100644 --- a/gecko-media/src/mse/sourcebufferlist.rs +++ b/gecko-media/src/mse/sourcebufferlist.rs @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use bindings::GeckoMediaSourceBufferListImpl; +use std::os::raw::c_void; use std::rc::Rc; def_gecko_media_struct!(SourceBufferList); @@ -20,6 +21,10 @@ pub trait SourceBufferListImpl { fn indexed_getter(&self, index: u32, source_buffer: *mut usize) -> bool; /// Get the number of SourceBuffer objects in the list. fn length(&self) -> u32; + /// Append a new SourceBuffer object to the list and optionaly trigger the addsourcebuffer event. + fn append(&self, source_buffer: *mut c_void, notify: bool); + /// Clear the list of SourceBuffer objects. + fn clear(&self); } pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBufferListImpl { @@ -29,12 +34,18 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSource def_gecko_callbacks_ffi_wrapper!(Rc); impl_simple_ffi_getter_wrapper!(length, u32); + impl_simple_ffi_callback_wrapper!(clear); unsafe extern "C" fn indexed_getter(ptr: *mut c_void, index: u32, id: *mut usize) -> bool { let wrapper = &*(ptr as *mut Wrapper); wrapper.callbacks.indexed_getter(index, id) } + unsafe extern "C" fn append(ptr: *mut c_void, source_buffer: *mut c_void, notify: bool) { + let wrapper = &*(ptr as *mut Wrapper); + wrapper.callbacks.append(source_buffer, notify); + } + GeckoMediaSourceBufferListImpl { mContext: Box::into_raw(Box::new(Wrapper { callbacks, @@ -42,5 +53,7 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSource mFree: Some(free), mIndexedGetter: Some(indexed_getter), mLength: Some(length), + mAppend: Some(append), + mClear: Some(clear), } } From e54d1337f6ce012bac3ff2a6286735e1b560944f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 21 Dec 2017 12:39:03 +0100 Subject: [PATCH 16/23] Set source buffer as active when init segment is received --- gecko-media/gecko/glue/MediaSource.cpp | 43 ++++++++++++++++--- gecko-media/gecko/glue/include/MediaSource.h | 8 +++- gecko-media/gecko/glue/include/SourceBuffer.h | 5 +++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/gecko-media/gecko/glue/MediaSource.cpp b/gecko-media/gecko/glue/MediaSource.cpp index db0e937..d57f49e 100644 --- a/gecko-media/gecko/glue/MediaSource.cpp +++ b/gecko-media/gecko/glue/MediaSource.cpp @@ -15,6 +15,7 @@ #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "nsThreadManager.h" +#include "SourceBuffer.h" mozilla::LogModule* GetMediaSourceLog() @@ -61,6 +62,8 @@ namespace dom { MediaSource::MediaSource(GeckoMediaSourceImpl aImpl) : mImpl(aImpl) + , mSourceBuffers(GetSourceBuffers()) + , mActiveSourceBuffers(GetActiveSourceBuffers()) , mDecoder(nullptr) { } @@ -81,6 +84,14 @@ MediaSource::~MediaSource() SourceBufferList* MediaSource::SourceBuffers() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(ReadyState() == MediaSourceReadyState::Closed, mSourceBuffers->Length() == 0); + return mSourceBuffers; +} + +SourceBufferList* +MediaSource::GetSourceBuffers() { MOZ_ASSERT(NS_IsMainThread()); @@ -95,6 +106,14 @@ MediaSource::SourceBuffers() SourceBufferList* MediaSource::ActiveSourceBuffers() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(ReadyState() == MediaSourceReadyState::Closed, mActiveSourceBuffers->Length() == 0); + return mActiveSourceBuffers; +} + +SourceBufferList* +MediaSource::GetActiveSourceBuffers() { MOZ_ASSERT(NS_IsMainThread()); @@ -161,13 +180,13 @@ MediaSource::Detach() mDecoder ? mDecoder->GetOwner() : nullptr); if (!mDecoder) { MOZ_ASSERT(ReadyState() == MediaSourceReadyState::Closed); - MOZ_ASSERT(ActiveSourceBuffers()->Length() == 0 && - SourceBuffers()->Length() == 0); + MOZ_ASSERT(mActiveSourceBuffers->Length() == 0 && + mSourceBuffers->Length() == 0); return; } SetReadyState(MediaSourceReadyState::Closed); - SourceBuffers()->Clear(); - ActiveSourceBuffers()->Clear(); + mSourceBuffers->Clear(); + mActiveSourceBuffers->Clear(); mDecoder->DetachMediaSource(); mDecoder = nullptr; } @@ -230,9 +249,21 @@ MediaSource::SourceBufferIsActive(SourceBuffer* aSourceBuffer) { MOZ_ASSERT(NS_IsMainThread()); - // TODO: append active source buffer + bool initMissing = false; + bool found = false; + for (uint32_t i = 0; i < mSourceBuffers->Length() ; i++) { + SourceBuffer* sourceBuffer = mSourceBuffers->IndexedGetter(i, found); + MOZ_ALWAYS_TRUE(found); + if (sourceBuffer == aSourceBuffer) { + sourceBuffer->SetActive(true); + } else if (!sourceBuffer->GetActive()) { + // Some source buffers haven't yet received an init segment. + // There's nothing more we can do at this stage. + initMissing = true; + } + } - if (!mDecoder) { + if (initMissing || !mDecoder) { return ActiveCompletionPromise::CreateAndResolve(true, __func__); } diff --git a/gecko-media/gecko/glue/include/MediaSource.h b/gecko-media/gecko/glue/include/MediaSource.h index fcd7d60..f1564de 100644 --- a/gecko-media/gecko/glue/include/MediaSource.h +++ b/gecko-media/gecko/glue/include/MediaSource.h @@ -53,10 +53,13 @@ class MediaSource final private: // SourceBuffer uses SourceBufferIsActive. - friend class mozilla::dom::SourceBuffer; + friend class SourceBuffer; ~MediaSource(); + SourceBufferList* GetSourceBuffers(); + SourceBufferList* GetActiveSourceBuffers(); + IMPL_GECKO_MEDIA_SIMPLE_SETTER(SetReadyState, MediaSourceReadyState); typedef MozPromise @@ -69,6 +72,9 @@ class MediaSource final GeckoMediaSourceImpl mImpl; + RefPtr mSourceBuffers; + RefPtr mActiveSourceBuffers; + RefPtr mDecoder; nsTArray> mCompletionPromises; diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index c384357..f4c72d5 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -39,6 +39,8 @@ class SourceBuffer final void* aErrorCbContext); private: + friend class MediaSource; + ~SourceBuffer(){}; void ResetParserState(); @@ -56,6 +58,9 @@ class SourceBuffer final error_callback_t aErrorCb, void* aErrorCbContext); + bool GetActive() { return mCurrentAttributes.GetActive(); } + void SetActive(bool aActive) { mCurrentAttributes.SetActive(aActive); } + SourceBufferAttributes mCurrentAttributes; RefPtr mMediaSource; From b879db4d8cdbf17e3a154b08a7225582d80424ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 21 Dec 2017 18:00:35 +0100 Subject: [PATCH 17/23] Reset parser state on append data error --- gecko-media/gecko/glue/SourceBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 6cce6c4..216f55b 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -160,6 +160,7 @@ SourceBuffer::AppendDataErrored(const MediaResult& aError, // or append was aborted and abort() has handled all the events. break; default: + ResetParserState(); (*aErrorCb)(aErrorCbContext, uint32_t(NS_ERROR_DOM_MEDIA_CANCELED)); break; } From 644bae83f8baded0ed3ab72bd8b81df83b777498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 21 Dec 2017 18:19:51 +0100 Subject: [PATCH 18/23] Implement SourceBuffer::AbortBufferAppend and expose ResetParserState through FFI --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 16 +++++++++++++++ gecko-media/gecko/glue/SourceBuffer.cpp | 16 +++++++++++++++ .../glue/include/GeckoMediaSourceBuffer.h | 6 ++++++ gecko-media/gecko/glue/include/SourceBuffer.h | 6 ++++-- gecko-media/src/mse/sourcebuffer.rs | 20 +++++++++++++++++-- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index 69f0713..bf55b1c 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -69,6 +69,22 @@ GeckoMedia_SourceBuffer_AppendData(size_t aId, aData, aLength, aSuccessCb, aSuccessCbContext, aErrorCb, aErrorCbContext); } +void +GeckoMedia_SourceBuffer_AbortBufferAppend(size_t aId) +{ + IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) + + reflector->mSourceBuffer->AbortBufferAppend(); +} + +void +GeckoMedia_SourceBuffer_ResetParserState(size_t aId) +{ + IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) + + reflector->mSourceBuffer->ResetParserState(); +} + SourceBuffer* GetSourceBuffer(const size_t aId) { diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 216f55b..34a8861 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -178,6 +178,22 @@ SourceBuffer::CheckEndTime() } } +void +SourceBuffer::AbortBufferAppend() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mCurrentAttributes.GetUpdating()) { + mCompletionPromise.DisconnectIfExists(); + if (mPendingAppend.Exists()) { + mPendingAppend.Disconnect(); + mTrackBuffersManager->AbortAppendData(); + } + + mCurrentAttributes.SetUpdating(false); + } +} + void SourceBuffer::ResetParserState() { diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index 7801f8c..3bb8a90 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -97,6 +97,12 @@ GeckoMedia_SourceBuffer_AppendData(size_t aId, void* aSuccessCbContext, error_callback_t aErrorCb, void* aErrorCbContext); + +void +GeckoMedia_SourceBuffer_AbortBufferAppend(size_t aId); + +void +GeckoMedia_SourceBuffer_ResetParserState(size_t aId); } #endif // GeckoMediaSourceBuffer_h_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index f4c72d5..9bcf8e5 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -38,13 +38,15 @@ class SourceBuffer final error_callback_t aErrorCb, void* aErrorCbContext); + void AbortBufferAppend(); + + void ResetParserState(); + private: friend class MediaSource; ~SourceBuffer(){}; - void ResetParserState(); - // If the media segment contains data beyond the current duration, // then run the duration change algorithm with new duration set to the // maximum of the current duration and the group end timestamp. diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 58c521b..77a234b 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -2,8 +2,10 @@ // 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/. -use bindings::{GeckoMediaSourceBufferImpl, GeckoMedia_SourceBuffer_Create}; -use bindings::{GeckoMedia_SourceBuffer_AppendData, GeckoMedia_SourceBuffer_EvictData}; +use bindings::GeckoMediaSourceBufferImpl; +use bindings::GeckoMedia_SourceBuffer_ResetParserState; +use bindings::{GeckoMedia_SourceBuffer_AbortBufferAppend, GeckoMedia_SourceBuffer_AppendData}; +use bindings::{GeckoMedia_SourceBuffer_Create, GeckoMedia_SourceBuffer_EvictData}; use std::ffi::CString; use std::os::raw::c_void; use std::rc::Rc; @@ -80,6 +82,20 @@ impl SourceBuffer { } } + + pub fn abort_buffer_append(&self) { + let id = self.id; + self.gecko_media.queue_task(move || unsafe { + GeckoMedia_SourceBuffer_AbortBufferAppend(id); + }); + } + + pub fn reset_parser_state(&self) { + let id = self.id; + self.gecko_media.queue_task(move || unsafe { + GeckoMedia_SourceBuffer_ResetParserState(id); + }); + } } impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); From 9547ee8281640c9da253424301b0f13a15f9b321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 21 Dec 2017 18:50:01 +0100 Subject: [PATCH 19/23] rustfmt --- gecko-media/src/lib.rs | 6 ++++-- gecko-media/src/mse/sourcebuffer.rs | 12 +++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 81469d4..3640c99 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -467,7 +467,9 @@ mod tests { } pub fn append_data(&self, data: *const u8, len: usize, success_cb: S, error_cb: E) - where S: Fn(), E: Fn(u32) + where + S: Fn(), + E: Fn(u32), { self.gecko_media.append_data( data, @@ -484,7 +486,7 @@ mod tests { let empty: [u8; 0] = []; // TODO For now this only tests that the mechanism to send closures through FFI works. // Should throw error because no decoder is attached yet. - source_buffer.append_data(empty.as_ptr(), empty.len(), || { unreachable!() }, |_| { + source_buffer.append_data(empty.as_ptr(), empty.len(), || unreachable!(), |_| { // TODO check error code. assert!(true); }); diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 77a234b..6cc6d76 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -56,9 +56,15 @@ impl SourceBuffer { let success_cb = &success_cb as *const _ as *mut c_void; let error_cb = &error_cb as *const _ as *mut c_void; self.gecko_media.queue_task(move || unsafe { - GeckoMedia_SourceBuffer_AppendData(id, data, len, - Some(success_callback_wrapper::), success_cb, - Some(error_callback_wrapper::), error_cb); + GeckoMedia_SourceBuffer_AppendData( + id, + data, + len, + Some(success_callback_wrapper::), + success_cb, + Some(error_callback_wrapper::), + error_cb, + ); }); extern "C" fn success_callback_wrapper(closure: *mut c_void) From 1c6a2ea397f625844aa229f426495b97553d59f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Fri, 22 Dec 2017 09:11:47 +0100 Subject: [PATCH 20/23] Implement SourceBuffer::RangeRemoval --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 12 ++++ gecko-media/gecko/glue/SourceBuffer.cpp | 24 +++++++- .../glue/include/GeckoMediaSourceBuffer.h | 7 +++ gecko-media/gecko/glue/include/SourceBuffer.h | 6 ++ gecko-media/src/mse/sourcebuffer.rs | 60 ++++++++++++------- 5 files changed, 86 insertions(+), 23 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index bf55b1c..ae72118 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -85,6 +85,18 @@ GeckoMedia_SourceBuffer_ResetParserState(size_t aId) reflector->mSourceBuffer->ResetParserState(); } +void +GeckoMedia_SourceBuffer_RangeRemoval(size_t aId, + double aStart, + double aEnd, + success_callback_t aSuccessCb, + void* aSuccessCbContext) +{ + IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) + + reflector->mSourceBuffer->RangeRemoval(aStart, aEnd, aSuccessCb, aSuccessCbContext); +} + SourceBuffer* GetSourceBuffer(const size_t aId) { diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 34a8861..6e65d87 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -22,6 +22,8 @@ GetMediaSourceLog(); namespace mozilla { namespace dom { +using namespace media; + SourceBuffer::SourceBuffer(GeckoMediaSourceBufferImpl aImpl, size_t aParentId, const char* aMimeType, @@ -143,7 +145,6 @@ SourceBuffer::AppendDataCompletedWithSuccess( if (!mCompletionPromise.Exists()) { (*aSuccessCb)(aSuccessCbContext); } - } void @@ -200,5 +201,26 @@ SourceBuffer::ResetParserState() mTrackBuffersManager->ResetParserState(mCurrentAttributes); } +void +SourceBuffer::RangeRemoval(double aStart, + double aEnd, + success_callback_t aSuccessCb, + void* aSuccessCbContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr self = this; + mTrackBuffersManager + ->RangeRemoval(TimeUnit::FromSeconds(aStart), TimeUnit::FromSeconds(aEnd)) + ->Then(AbstractThread::MainThread(), + __func__, + [self, aSuccessCb, aSuccessCbContext](bool) { + self->mPendingRemoval.Complete(); + (*aSuccessCb)(aSuccessCbContext); + }, + []() { MOZ_ASSERT(false); }) + ->Track(mPendingRemoval); +} + } // namespace dom } // namespace mozilla \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index 3bb8a90..435bd1f 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -103,6 +103,13 @@ GeckoMedia_SourceBuffer_AbortBufferAppend(size_t aId); void GeckoMedia_SourceBuffer_ResetParserState(size_t aId); + +void +GeckoMedia_SourceBuffer_RangeRemoval(size_t aId, + double aStart, + double aEnd, + success_callback_t aSuccessCb, + void* aSuccessCbContext); } #endif // GeckoMediaSourceBuffer_h_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index 9bcf8e5..cc2f6f9 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -42,6 +42,11 @@ class SourceBuffer final void ResetParserState(); + void RangeRemoval(double aStart, + double aEnd, + success_callback_t aSuccessCb, + void* aSuccessCbContext); + private: friend class MediaSource; @@ -70,6 +75,7 @@ class SourceBuffer final RefPtr mTrackBuffersManager; MozPromiseRequestHolder mPendingAppend; + MozPromiseRequestHolder mPendingRemoval; MozPromiseRequestHolder mCompletionPromise; }; diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index 6cc6d76..ac8675b 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -3,9 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. use bindings::GeckoMediaSourceBufferImpl; -use bindings::GeckoMedia_SourceBuffer_ResetParserState; use bindings::{GeckoMedia_SourceBuffer_AbortBufferAppend, GeckoMedia_SourceBuffer_AppendData}; use bindings::{GeckoMedia_SourceBuffer_Create, GeckoMedia_SourceBuffer_EvictData}; +use bindings::{GeckoMedia_SourceBuffer_RangeRemoval, GeckoMedia_SourceBuffer_ResetParserState}; use std::ffi::CString; use std::os::raw::c_void; use std::rc::Rc; @@ -66,27 +66,6 @@ impl SourceBuffer { error_cb, ); }); - - extern "C" fn success_callback_wrapper(closure: *mut c_void) - where - F: FnOnce(), - { - let opt_closure = closure as *mut Option; - unsafe { - (*opt_closure).take().unwrap()(); - } - } - - extern "C" fn error_callback_wrapper(closure: *mut c_void, error: u32) - where - F: FnOnce(u32), - { - let opt_closure = closure as *mut Option; - unsafe { - (*opt_closure).take().unwrap()(error); - } - } - } pub fn abort_buffer_append(&self) { @@ -102,6 +81,23 @@ impl SourceBuffer { GeckoMedia_SourceBuffer_ResetParserState(id); }); } + + pub fn range_removal(&self, start: f64, end: f64, success_cb: F) + where + F: FnOnce(), + { + let id = self.id; + let success_cb = &success_cb as *const _ as *mut c_void; + self.gecko_media.queue_task(move || unsafe { + GeckoMedia_SourceBuffer_RangeRemoval( + id, + start, + end, + Some(success_callback_wrapper::), + success_cb, + ); + }); + } } impl_drop_gecko_media_struct!(SourceBuffer, GeckoMedia_SourceBuffer_Shutdown); @@ -195,3 +191,23 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff mSetActive: Some(set_active), } } + +extern "C" fn success_callback_wrapper(closure: *mut c_void) +where + F: FnOnce(), +{ + let opt_closure = closure as *mut Option; + unsafe { + (*opt_closure).take().unwrap()(); + } +} + +extern "C" fn error_callback_wrapper(closure: *mut c_void, error: u32) +where + F: FnOnce(u32), +{ + let opt_closure = closure as *mut Option; + unsafe { + (*opt_closure).take().unwrap()(error); + } +} From 71490b2f0a89fac2cc2b27dd35882196411318d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Fri, 22 Dec 2017 11:06:24 +0100 Subject: [PATCH 21/23] Fix tests: do not send more messages through Sink channel after playback has ended --- gecko-media/src/lib.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 3640c99..430c6d0 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -105,15 +105,23 @@ mod tests { let (sender, receiver) = mpsc::channel(); struct Sink { sender: Mutex>, + ended: Mutex>, } impl PlayerEventSink for Sink { fn playback_ended(&self) { + self.ended.lock().unwrap().set(true); self.sender.lock().unwrap().send(Status::Ended).unwrap(); } fn decode_error(&self) { + if self.ended.lock().unwrap().get() { + return; + } self.sender.lock().unwrap().send(Status::Error).unwrap(); } fn async_event(&self, name: &str) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -121,6 +129,9 @@ mod tests { .unwrap(); } fn metadata_loaded(&self, metadata: Metadata) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -128,6 +139,9 @@ mod tests { .unwrap(); } fn duration_changed(&self, duration: f64) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -135,6 +149,9 @@ mod tests { .unwrap(); } fn loaded_data(&self) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -142,6 +159,9 @@ mod tests { .unwrap(); } fn time_update(&self, time: f64) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -149,6 +169,9 @@ mod tests { .unwrap(); } fn seek_started(&self) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -156,6 +179,9 @@ mod tests { .unwrap(); } fn seek_completed(&self) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -163,6 +189,9 @@ mod tests { .unwrap(); } fn update_current_images(&self, images: Vec) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -170,6 +199,9 @@ mod tests { .unwrap(); } fn buffered(&self, ranges: Vec>) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -177,6 +209,9 @@ mod tests { .unwrap(); } fn seekable(&self, ranges: Vec>) { + if self.ended.lock().unwrap().get() { + return; + } self.sender .lock() .unwrap() @@ -186,6 +221,7 @@ mod tests { } let sink = Box::new(Sink { sender: Mutex::new(sender), + ended: Mutex::new(Cell::new(false)), }); let mut file = File::open(path).unwrap(); let mut bytes = vec![]; From b5d3c9148ec194fd69c7df58766e589a6c53762d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Fri, 22 Dec 2017 16:26:07 +0100 Subject: [PATCH 22/23] Use struct callbacks instead of closures for AppendData and RangeRemoval --- .../gecko/glue/GeckoMediaSourceBuffer.cpp | 15 ++--- gecko-media/gecko/glue/SourceBuffer.cpp | 55 ++++++---------- .../glue/include/GeckoMediaSourceBuffer.h | 15 ++--- gecko-media/gecko/glue/include/SourceBuffer.h | 23 ++----- .../glue/include/SourceBufferAttributes.h | 12 ++++ gecko-media/src/lib.rs | 42 +++++++------ gecko-media/src/mse/sourcebuffer.rs | 62 +++++-------------- 7 files changed, 82 insertions(+), 142 deletions(-) diff --git a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp index ae72118..41a3dfa 100644 --- a/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp +++ b/gecko-media/gecko/glue/GeckoMediaSourceBuffer.cpp @@ -57,16 +57,11 @@ GeckoMedia_SourceBuffer_EvictData(size_t aId, size_t aLength, bool* aBufferFull) void GeckoMedia_SourceBuffer_AppendData(size_t aId, const uint8_t* aData, - size_t aLength, - success_callback_t aSuccessCb, - void* aSuccessCbContext, - error_callback_t aErrorCb, - void* aErrorCbContext) + size_t aLength) { IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) - reflector->mSourceBuffer->AppendData( - aData, aLength, aSuccessCb, aSuccessCbContext, aErrorCb, aErrorCbContext); + reflector->mSourceBuffer->AppendData(aData, aLength); } void @@ -88,13 +83,11 @@ GeckoMedia_SourceBuffer_ResetParserState(size_t aId) void GeckoMedia_SourceBuffer_RangeRemoval(size_t aId, double aStart, - double aEnd, - success_callback_t aSuccessCb, - void* aSuccessCbContext) + double aEnd) { IMPL_GECKO_MEDIA_REFLECTOR_GET(GeckoMediaSourceBuffer) - reflector->mSourceBuffer->RangeRemoval(aStart, aEnd, aSuccessCb, aSuccessCbContext); + reflector->mSourceBuffer->RangeRemoval(aStart, aEnd); } SourceBuffer* diff --git a/gecko-media/gecko/glue/SourceBuffer.cpp b/gecko-media/gecko/glue/SourceBuffer.cpp index 6e65d87..06828f2 100644 --- a/gecko-media/gecko/glue/SourceBuffer.cpp +++ b/gecko-media/gecko/glue/SourceBuffer.cpp @@ -73,45 +73,33 @@ SourceBuffer::EvictData(size_t aLength, bool* aBufferFull) } void -SourceBuffer::AppendData(const uint8_t* aData, - size_t aLength, - success_callback_t aSuccessCb, - void* aSuccessCbContext, - error_callback_t aErrorCb, - void* aErrorCbContext) +SourceBuffer::AppendData(const uint8_t* aData, size_t aLength) { MSE_DEBUG("AppendData(aLength=%zu)", aLength); RefPtr data = new MediaByteBuffer(); if (NS_WARN_IF(!data->AppendElements(aData, aLength, fallible))) { - return (*aErrorCb)(aErrorCbContext, - uint32_t(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR)); + return mCurrentAttributes.OnDataAppended( + uint32_t(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR)); } if (NS_WARN_IF(!mTrackBuffersManager)) { - return (*aErrorCb)(aErrorCbContext, uint32_t(NS_ERROR_NOT_INITIALIZED)); + return mCurrentAttributes.OnDataAppended( + uint32_t(NS_ERROR_NOT_INITIALIZED)); } - RefPtr self = this; mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes) ->Then(AbstractThread::MainThread(), __func__, - [self, this, aSuccessCb, aSuccessCbContext]( - const SourceBufferTask::AppendBufferResult& aResult) { - AppendDataCompletedWithSuccess( - aResult, aSuccessCb, aSuccessCbContext); - }, - [self, this, aErrorCb, aErrorCbContext](const MediaResult& aError) { - AppendDataErrored(aError, aErrorCb, aErrorCbContext); - }) + this, + &SourceBuffer::AppendDataCompletedWithSuccess, + &SourceBuffer::AppendDataErrored) ->Track(mPendingAppend); } void SourceBuffer::AppendDataCompletedWithSuccess( - const SourceBufferTask::AppendBufferResult& aResult, - success_callback_t aSuccessCb, - void* aSuccessCbContext) + const SourceBufferTask::AppendBufferResult& aResult) { MOZ_ASSERT(mCurrentAttributes.GetUpdating()); mPendingAppend.Complete(); @@ -120,14 +108,13 @@ SourceBuffer::AppendDataCompletedWithSuccess( if (!mCurrentAttributes.GetActive()) { mCurrentAttributes.SetActive(true); MSE_DEBUG("Init segment received"); - RefPtr self = this; mMediaSource->SourceBufferIsActive(this) ->Then(AbstractThread::MainThread(), __func__, - [self, this, aSuccessCb, aSuccessCbContext]() { + [this]() { MSE_DEBUG("Complete AppendBuffer operation"); mCompletionPromise.Complete(); - (*aSuccessCb)(aSuccessCbContext); + mCurrentAttributes.OnDataAppended(0 /* success */); }) ->Track(mCompletionPromise); } @@ -143,14 +130,12 @@ SourceBuffer::AppendDataCompletedWithSuccess( CheckEndTime(); if (!mCompletionPromise.Exists()) { - (*aSuccessCb)(aSuccessCbContext); + mCurrentAttributes.OnDataAppended(0 /* success */); } } void -SourceBuffer::AppendDataErrored(const MediaResult& aError, - error_callback_t aErrorCb, - void* aErrorCbContext) +SourceBuffer::AppendDataErrored(const MediaResult& aError) { MOZ_ASSERT(mCurrentAttributes.GetUpdating()); mPendingAppend.Complete(); @@ -162,7 +147,7 @@ SourceBuffer::AppendDataErrored(const MediaResult& aError, break; default: ResetParserState(); - (*aErrorCb)(aErrorCbContext, uint32_t(NS_ERROR_DOM_MEDIA_CANCELED)); + mCurrentAttributes.OnDataAppended(uint32_t(NS_ERROR_DOM_MEDIA_CANCELED)); break; } } @@ -202,21 +187,17 @@ SourceBuffer::ResetParserState() } void -SourceBuffer::RangeRemoval(double aStart, - double aEnd, - success_callback_t aSuccessCb, - void* aSuccessCbContext) +SourceBuffer::RangeRemoval(double aStart, double aEnd) { MOZ_ASSERT(NS_IsMainThread()); - RefPtr self = this; mTrackBuffersManager ->RangeRemoval(TimeUnit::FromSeconds(aStart), TimeUnit::FromSeconds(aEnd)) ->Then(AbstractThread::MainThread(), __func__, - [self, aSuccessCb, aSuccessCbContext](bool) { - self->mPendingRemoval.Complete(); - (*aSuccessCb)(aSuccessCbContext); + [this](bool) { + mPendingRemoval.Complete(); + mCurrentAttributes.OnRangeRemoved(); }, []() { MOZ_ASSERT(false); }) ->Track(mPendingRemoval); diff --git a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h index 435bd1f..85ced0d 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h +++ b/gecko-media/gecko/glue/include/GeckoMediaSourceBuffer.h @@ -64,14 +64,13 @@ struct GeckoMediaSourceBufferImpl void (*mSetUpdating)(void*, bool); bool (*mGetActive)(void*); void (*mSetActive)(void*, bool); + void (*mOnDataAppended)(void*, uint32_t); + void (*mOnRangeRemoved)(void*); }; SourceBuffer* GetSourceBuffer(const size_t aId); -typedef void (*success_callback_t)(void*); -typedef void (*error_callback_t)(void*, uint32_t); - extern "C" { // TODO error handling (i.e. aId or aParentId may not be valid) void @@ -92,11 +91,7 @@ GeckoMedia_SourceBuffer_EvictData(size_t aId, void GeckoMedia_SourceBuffer_AppendData(size_t aId, const uint8_t* aData, - size_t aLength, - success_callback_t aSuccessCb, - void* aSuccessCbContext, - error_callback_t aErrorCb, - void* aErrorCbContext); + size_t aLength); void GeckoMedia_SourceBuffer_AbortBufferAppend(size_t aId); @@ -107,9 +102,7 @@ GeckoMedia_SourceBuffer_ResetParserState(size_t aId); void GeckoMedia_SourceBuffer_RangeRemoval(size_t aId, double aStart, - double aEnd, - success_callback_t aSuccessCb, - void* aSuccessCbContext); + double aEnd); } #endif // GeckoMediaSourceBuffer_h_ \ No newline at end of file diff --git a/gecko-media/gecko/glue/include/SourceBuffer.h b/gecko-media/gecko/glue/include/SourceBuffer.h index cc2f6f9..c892114 100644 --- a/gecko-media/gecko/glue/include/SourceBuffer.h +++ b/gecko-media/gecko/glue/include/SourceBuffer.h @@ -31,21 +31,13 @@ class SourceBuffer final void EvictData(size_t aLength, bool* aBufferFull); - void AppendData(const uint8_t* aData, - size_t aLength, - success_callback_t aSuccessCb, - void* aSuccessCbContext, - error_callback_t aErrorCb, - void* aErrorCbContext); + void AppendData(const uint8_t* aData, size_t aLength); void AbortBufferAppend(); void ResetParserState(); - void RangeRemoval(double aStart, - double aEnd, - success_callback_t aSuccessCb, - void* aSuccessCbContext); + void RangeRemoval(double aStart, double aEnd); private: friend class MediaSource; @@ -58,12 +50,8 @@ class SourceBuffer final void CheckEndTime(); void AppendDataCompletedWithSuccess( - const SourceBufferTask::AppendBufferResult& aResult, - success_callback_t aSuccessCb, - void* aSuccessCbContext); - void AppendDataErrored(const MediaResult& aError, - error_callback_t aErrorCb, - void* aErrorCbContext); + const SourceBufferTask::AppendBufferResult& aResult); + void AppendDataErrored(const MediaResult& aError); bool GetActive() { return mCurrentAttributes.GetActive(); } void SetActive(bool aActive) { mCurrentAttributes.SetActive(aActive); } @@ -75,7 +63,8 @@ class SourceBuffer final RefPtr mTrackBuffersManager; MozPromiseRequestHolder mPendingAppend; - MozPromiseRequestHolder mPendingRemoval; + MozPromiseRequestHolder + mPendingRemoval; MozPromiseRequestHolder mCompletionPromise; }; diff --git a/gecko-media/gecko/glue/include/SourceBufferAttributes.h b/gecko-media/gecko/glue/include/SourceBufferAttributes.h index 60cbcf8..b0d8de1 100644 --- a/gecko-media/gecko/glue/include/SourceBufferAttributes.h +++ b/gecko-media/gecko/glue/include/SourceBufferAttributes.h @@ -109,6 +109,18 @@ class SourceBufferAttributes aGroupEndTimestamp.ToSeconds()); } + void OnDataAppended(uint32_t aResult) + { + CALLBACK_GUARD_VOID(OnDataAppended); + (*mImpl.mOnDataAppended)(mImpl.mContext, aResult); + } + + void OnRangeRemoved() + { + CALLBACK_GUARD_VOID(OnRangeRemoved); + (*mImpl.mOnRangeRemoved)(mImpl.mContext); + } + // mGenerateTimestamp isn't mutable once the source buffer has been // constructed bool mGenerateTimestamps; diff --git a/gecko-media/src/lib.rs b/gecko-media/src/lib.rs index 430c6d0..a162a51 100644 --- a/gecko-media/src/lib.rs +++ b/gecko-media/src/lib.rs @@ -433,11 +433,15 @@ mod tests { assert_eq!(gecko_media.is_type_supported("audio/wav"), false); } - struct SourceBufferAttributes {} + struct SourceBufferAttributes { + pub expect_data_appended_success: Cell, + } impl SourceBufferAttributes { pub fn new() -> Self { - SourceBufferAttributes {} + SourceBufferAttributes { + expect_data_appended_success: Cell::new(true), + } } } @@ -484,6 +488,14 @@ mod tests { false } fn set_active(&self, _: bool) {} + fn on_data_appended(&self, result: u32) { + if self.expect_data_appended_success.get() { + assert!(result == 0); + } else { + assert!(result != 0); + } + } + fn on_range_removed(&self) {} } struct SourceBufferDom { @@ -502,30 +514,24 @@ mod tests { } } - pub fn append_data(&self, data: *const u8, len: usize, success_cb: S, error_cb: E) - where - S: Fn(), - E: Fn(u32), - { - self.gecko_media.append_data( - data, - len, - success_cb, - error_cb, + pub fn append_data(&self, data: *const u8, len: usize, expect_success: bool) { + self.attributes.expect_data_appended_success.set( + expect_success, ); + self.gecko_media.append_data(data, len); } } fn test_source_buffer() { let media_source = MediaSourceDom::new(); - let source_buffer = SourceBufferDom::new(&media_source, "video/mp4"); + let source_buffer = Box::new(SourceBufferDom::new(&media_source, "video/mp4")); let empty: [u8; 0] = []; - // TODO For now this only tests that the mechanism to send closures through FFI works. // Should throw error because no decoder is attached yet. - source_buffer.append_data(empty.as_ptr(), empty.len(), || unreachable!(), |_| { - // TODO check error code. - assert!(true); - }); + source_buffer.append_data( + empty.as_ptr(), + empty.len(), + false, /* expect error: no decoder */ + ); } #[test] diff --git a/gecko-media/src/mse/sourcebuffer.rs b/gecko-media/src/mse/sourcebuffer.rs index ac8675b..e7b112f 100644 --- a/gecko-media/src/mse/sourcebuffer.rs +++ b/gecko-media/src/mse/sourcebuffer.rs @@ -47,24 +47,10 @@ impl SourceBuffer { receiver.recv().unwrap(); } - pub fn append_data(&self, data: *const u8, len: usize, success_cb: F, error_cb: E) - where - F: FnOnce(), - E: FnOnce(u32), - { + pub fn append_data(&self, data: *const u8, len: usize) { let id = self.id; - let success_cb = &success_cb as *const _ as *mut c_void; - let error_cb = &error_cb as *const _ as *mut c_void; self.gecko_media.queue_task(move || unsafe { - GeckoMedia_SourceBuffer_AppendData( - id, - data, - len, - Some(success_callback_wrapper::), - success_cb, - Some(error_callback_wrapper::), - error_cb, - ); + GeckoMedia_SourceBuffer_AppendData(id, data, len); }); } @@ -82,20 +68,10 @@ impl SourceBuffer { }); } - pub fn range_removal(&self, start: f64, end: f64, success_cb: F) - where - F: FnOnce(), - { + pub fn range_removal(&self, start: f64, end: f64) { let id = self.id; - let success_cb = &success_cb as *const _ as *mut c_void; self.gecko_media.queue_task(move || unsafe { - GeckoMedia_SourceBuffer_RangeRemoval( - id, - start, - end, - Some(success_callback_wrapper::), - success_cb, - ); + GeckoMedia_SourceBuffer_RangeRemoval(id, start, end); }); } } @@ -127,6 +103,8 @@ pub trait SourceBufferImpl { fn set_updating(&self, bool); fn get_active(&self) -> bool; fn set_active(&self, bool); + fn on_data_appended(&self, u32); + fn on_range_removed(&self); } pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBufferImpl { @@ -156,12 +134,18 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff impl_simple_ffi_setter_wrapper!(set_updating, bool); impl_simple_ffi_getter_wrapper!(get_active, bool); impl_simple_ffi_setter_wrapper!(set_active, bool); + impl_simple_ffi_callback_wrapper!(on_range_removed); unsafe extern "C" fn get_group_start_timestamp(ptr: *mut c_void, timestamp: *mut f64) { let wrapper = &*(ptr as *mut Wrapper); wrapper.callbacks.get_group_start_timestamp(timestamp); } + unsafe extern "C" fn on_data_appended(ptr: *mut c_void, result: u32) { + let wrapper = &*(ptr as *mut Wrapper); + wrapper.callbacks.on_data_appended(result); + } + GeckoMediaSourceBufferImpl { mContext: Box::into_raw(Box::new(Wrapper { callbacks, @@ -189,25 +173,7 @@ pub fn to_ffi_callbacks(callbacks: Rc) -> GeckoMediaSourceBuff mSetUpdating: Some(set_updating), mGetActive: Some(get_active), mSetActive: Some(set_active), - } -} - -extern "C" fn success_callback_wrapper(closure: *mut c_void) -where - F: FnOnce(), -{ - let opt_closure = closure as *mut Option; - unsafe { - (*opt_closure).take().unwrap()(); - } -} - -extern "C" fn error_callback_wrapper(closure: *mut c_void, error: u32) -where - F: FnOnce(u32), -{ - let opt_closure = closure as *mut Option; - unsafe { - (*opt_closure).take().unwrap()(error); + mOnDataAppended: Some(on_data_appended), + mOnRangeRemoved: Some(on_range_removed), } } From 136ddcec26226a50baa53362daba82fcac7f0cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Thu, 28 Dec 2017 13:51:04 +0100 Subject: [PATCH 23/23] Introduce create_media_source_player --- gecko-media/gecko/glue/Player.cpp | 41 ++++++++++++- .../glue/include/GeckoMediaDecoderOwner.h | 2 +- gecko-media/gecko/glue/include/Player.h | 6 ++ gecko-media/src/player.rs | 58 +++++++++++++------ gecko-media/src/top.rs | 7 +++ 5 files changed, 94 insertions(+), 20 deletions(-) diff --git a/gecko-media/gecko/glue/Player.cpp b/gecko-media/gecko/glue/Player.cpp index 75c3411..1d3fa1a 100644 --- a/gecko-media/gecko/glue/Player.cpp +++ b/gecko-media/gecko/glue/Player.cpp @@ -10,6 +10,9 @@ #include "GeckoMediaDecoder.h" #include "GeckoMediaDecoderOwner.h" #include "GeckoMediaMacros.h" +#include "GeckoMediaSource.h" +#include "MediaSource.h" +#include "MediaSourceDecoder.h" #include "RustMediaResource.h" #include "UniquePtr.h" #include "mozilla/RefPtr.h" @@ -111,6 +114,42 @@ GeckoMedia_Player_CreateNetworkPlayer(size_t aId, resource->SetDecoder(reflector->mDecoder); } +bool +GeckoMedia_Player_CreateMediaSourcePlayer(size_t aId, + size_t aMediaSourceId, + PlayerCallbackObject aCallback, + FrameAllocatorObject aAllocator) +{ + Player* reflector = + sReflectors.AppendElement(Player(aId, aCallback, aAllocator)); + MOZ_ASSERT(GetReflector(aId) == reflector); + + MediaDecoderInit decoderInit( + reflector->mDecoderOwner.get(), + 1.0, // volume + true, // mPreservesPitch + 1.0, // mPlaybackRate + false, // mMinimizePreroll + false, // mHasSuspendTaint + false, // mLooping + MediaContainerType(MEDIAMIMETYPE("application/x.mediasource"))); + RefPtr decoder = new MediaSourceDecoder(decoderInit); + reflector->mDecoder = static_cast(decoder); + reflector->mDecoderOwner->SetDecoder(reflector->mDecoder); + + RefPtr mediaSource = + GetMediaSource(aMediaSourceId); + if (NS_WARN_IF(!mediaSource || !mediaSource->Attach(decoder))) { + return false; + } + + if (NS_WARN_IF(reflector->mDecoder->Load(nullptr) != NS_OK)) { + return false; + } + + return true; +} + void GeckoMedia_Player_Play(size_t aId) { @@ -144,5 +183,5 @@ void GeckoMedia_Player_SetPlaybackRate(size_t aId, double rate) { IMPL_GECKO_MEDIA_REFLECTOR_GET(Player) - reflector->mDecoder->SetPlaybackRate(rate); + reflector->mDecoder->SetPlaybackRate(rate); } diff --git a/gecko-media/gecko/glue/include/GeckoMediaDecoderOwner.h b/gecko-media/gecko/glue/include/GeckoMediaDecoderOwner.h index ff23872..a43d2bf 100644 --- a/gecko-media/gecko/glue/include/GeckoMediaDecoderOwner.h +++ b/gecko-media/gecko/glue/include/GeckoMediaDecoderOwner.h @@ -165,7 +165,7 @@ class GeckoMediaDecoderOwner : public MediaDecoderOwner void NotifyWaitingForKey() override; /* - * Methods that are used only in Gecko go here. We provide defaul + * Methods that are used only in Gecko go here. We provide default * implementations so they can compile in Servo without modification. */ // Return an abstract thread on which to run main thread runnables. diff --git a/gecko-media/gecko/glue/include/Player.h b/gecko-media/gecko/glue/include/Player.h index cabe1f5..6e529e5 100644 --- a/gecko-media/gecko/glue/include/Player.h +++ b/gecko-media/gecko/glue/include/Player.h @@ -132,6 +132,12 @@ GeckoMedia_Player_CreateNetworkPlayer(size_t aId, PlayerCallbackObject aCallback, FrameAllocatorObject aAllocator); +bool +GeckoMedia_Player_CreateMediaSourcePlayer(size_t aId, + size_t aMediaSourceId, + PlayerCallbackObject aCallback, + FrameAllocatorObject aAllocator); + void GeckoMedia_Player_Play(size_t aId); diff --git a/gecko-media/src/player.rs b/gecko-media/src/player.rs index 99cfb20..bef25a2 100644 --- a/gecko-media/src/player.rs +++ b/gecko-media/src/player.rs @@ -2,6 +2,7 @@ // 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/. +use bindings::GeckoMedia_Player_CreateMediaSourcePlayer; use bindings::NetworkResourceObject; use bindings::{CachedRangesObserverObject, FrameAllocatorObject, GeckoImagePlane}; use bindings::{GeckoMediaByteRange, GeckoPlanarYCbCrImage, GeckoPlanarYCbCrImageData}; @@ -16,7 +17,7 @@ use std::mem; use std::ops::Range; use std::os::raw::c_char; use std::slice; -use std::sync::{Arc, Mutex}; +use std::sync::{mpsc, Arc, Mutex}; use timestamp::TimeStamp; /// Holds useful metadata extracted from a media resource during loading. @@ -287,10 +288,7 @@ impl Player { _ => return Err(()), }; let ffi_frame_allocator = to_ffi_frame_allocator(video_frame_allocator.clone()); - let player = Player { - gecko_media, - id, - }; + let player = Player { gecko_media, id }; player.gecko_media.queue_task(move || unsafe { GeckoMedia_Player_CreateBlobPlayer( id, @@ -318,10 +316,7 @@ impl Player { let callback = to_ffi_callback(sink, video_frame_allocator.clone()); let ffi_frame_allocator = to_ffi_frame_allocator(video_frame_allocator.clone()); let resource = to_ffi_resource(resource); - let player = Player { - gecko_media, - id, - }; + let player = Player { gecko_media, id }; player.gecko_media.queue_task(move || unsafe { GeckoMedia_Player_CreateNetworkPlayer( id, @@ -334,6 +329,34 @@ impl Player { Ok(player) } + pub fn from_media_source( + gecko_media: GeckoMedia, + id: usize, + media_source_id: usize, + sink: Box, + ) -> Result { + let video_frame_allocator = Arc::new(Mutex::new(VideoFrameAllocator::new())); + let callback = to_ffi_callback(sink, video_frame_allocator.clone()); + let ffi_frame_allocator = to_ffi_frame_allocator(video_frame_allocator.clone()); + let player = Player { gecko_media, id }; + let (sender, receiver) = mpsc::channel(); + player.gecko_media.queue_task(move || unsafe { + sender + .send(GeckoMedia_Player_CreateMediaSourcePlayer( + id, + media_source_id, + callback, + ffi_frame_allocator, + )) + .unwrap(); + }); + if receiver.recv().unwrap() { + Ok(player) + } else { + Err(()) + } + } + /// Starts playback of the media resource. While playing, /// PlayerEventSink::time_update() will be called once per frame, /// or every 40 milliseconds if there is no video. @@ -405,9 +428,10 @@ impl CachedRangesSink { }); } unsafe { - self.observer.mUpdate.as_ref().map(|f| { - f(self.observer.mResourceID, data.as_ptr(), data.len()) - }); + self.observer + .mUpdate + .as_ref() + .map(|f| f(self.observer.mResourceID, data.as_ptr(), data.len())); } } } @@ -546,9 +570,9 @@ fn to_ffi_resource(callbacks: Box) -> NetworkResourceObject { unsafe extern "C" fn set_ranges_observer(ptr: *mut c_void, observer: CachedRangesObserverObject) { let wrapper = &*(ptr as *mut Wrapper); - wrapper.callbacks.set_cached_ranges_sink(CachedRangesSink { - observer, - }) + wrapper + .callbacks + .set_cached_ranges_sink(CachedRangesSink { observer }) } unsafe extern "C" fn read_at( @@ -599,9 +623,7 @@ fn to_ffi_resource(callbacks: Box) -> NetworkResourceObject { mLength: Some(length), mReadFromCache: Some(read_from_cache), mFree: Some(free), - mData: Box::into_raw(Box::new(Wrapper { - callbacks, - })) as *mut c_void, + mData: Box::into_raw(Box::new(Wrapper { callbacks })) as *mut c_void, } } diff --git a/gecko-media/src/top.rs b/gecko-media/src/top.rs index 5cfc7cb..d2c976e 100644 --- a/gecko-media/src/top.rs +++ b/gecko-media/src/top.rs @@ -141,6 +141,13 @@ impl GeckoMedia { Player::from_network_resource(handle, id, resource, mime_type, sink) } + /// Creates a Player to play from a MediaSource. + pub fn create_media_source_player(media_source_id: usize, sink: Box) -> Result { + let handle = GeckoMedia::get()?; + let id = NEXT_PLAYER_ID.fetch_add(1, Ordering::SeqCst); + Player::from_media_source(handle, id, media_source_id, sink) + } + /// Creates a MediaSource instance and its corresponding GeckoMediaSource reflector. impl_gecko_media_struct_constructor!( create_media_source,