commit cb40a26 Author: Alessandro Decina Date: Fri Jan 03 08:16:54 2014 -0800 Bug 806917 - support GStreamer 1.0 --- configure.in | 66 ++- content/media/gstreamer/GStreamerAllocator.cpp | 197 +++++++ content/media/gstreamer/GStreamerAllocator.h | 25 + content/media/gstreamer/GStreamerFormatHelper.cpp | 13 +- content/media/gstreamer/GStreamerFunctionList.h | 93 +++- content/media/gstreamer/GStreamerLoader.cpp | 48 +- content/media/gstreamer/GStreamerLoader.h | 8 + content/media/gstreamer/GStreamerReader-0.10.cpp | 200 +++++++ content/media/gstreamer/GStreamerReader.cpp | 632 ++++++++++++++-------- content/media/gstreamer/GStreamerReader.h | 42 +- content/media/gstreamer/moz.build | 11 +- content/media/test/manifest.js | 6 +- 12 files changed, 1057 insertions(+), 284 deletions(-) diff --git configure.in configure.in index 9776b8d..0b1698d 100644 --- mozilla/configure.in +++ mozilla/configure.in @@ -3988,6 +3988,7 @@ MOZ_SAMPLE_TYPE_FLOAT32= MOZ_SAMPLE_TYPE_S16= MOZ_OPUS=1 MOZ_WEBM=1 +MOZ_GSTREAMER= MOZ_DIRECTSHOW= MOZ_WMF= MOZ_FMP4= @@ -5634,44 +5635,61 @@ WINNT|Darwin|Android) ;; *) MOZ_GSTREAMER=1 + GST_API_VERSION=0.10 ;; esac -MOZ_ARG_ENABLE_BOOL(gstreamer, -[ --enable-gstreamer Enable GStreamer support], -MOZ_GSTREAMER=1, -MOZ_GSTREAMER=) - -if test "$MOZ_GSTREAMER"; then - # API version, eg 0.10, 1.0 etc +MOZ_ARG_ENABLE_STRING(gstreamer, +[ --enable-gstreamer[=0.10] Enable GStreamer support], +[ MOZ_GSTREAMER=1 + # API version, eg 0.10, 1.0 etc + if test -z "$enableval" -o "$enableval" = "yes"; then GST_API_VERSION=0.10 + elif test "$enableval" = "no"; then + MOZ_GSTREAMER= + else + GST_API_VERSION=$enableval + fi], +) + +if test -n "$MOZ_GSTREAMER"; then # core/base release number - GST_VERSION=0.10.25 + if test "$GST_API_VERSION" = "1.0"; then + GST_VERSION=1.0 + else + GST_VERSION=0.10.25 + fi + PKG_CHECK_MODULES(GSTREAMER, gstreamer-$GST_API_VERSION >= $GST_VERSION gstreamer-app-$GST_API_VERSION - gstreamer-plugins-base-$GST_API_VERSION, , - AC_MSG_ERROR([gstreamer and gstreamer-plugins-base development packages are needed to build gstreamer backend. Install them or disable gstreamer support with --disable-gstreamer])) - if test -n "$GSTREAMER_LIBS"; then - _SAVE_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS $GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" - AC_TRY_LINK(,[return 0;],_HAVE_LIBGSTVIDEO=1,_HAVE_LIBGSTVIDEO=) - if test -n "$_HAVE_LIBGSTVIDEO" ; then - GSTREAMER_LIBS="$GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" - else - AC_MSG_ERROR([gstreamer-plugins-base found, but no libgstvideo. Something has gone terribly wrong. Try reinstalling gstreamer-plugins-base; failing that, disable the gstreamer backend with --disable-gstreamer.]) - fi - LDFLAGS=$_SAVE_LDFLAGS + gstreamer-plugins-base-$GST_API_VERSION, + [_HAVE_GSTREAMER=1], + [_HAVE_GSTREAMER=]) + if test -z "$_HAVE_GSTREAMER"; then + AC_MSG_ERROR([gstreamer and gstreamer-plugins-base development packages are needed to build gstreamer backend. Install them or disable gstreamer support with --disable-gstreamer]) + fi + + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" + AC_TRY_LINK(,[return 0;],_HAVE_LIBGSTVIDEO=1,_HAVE_LIBGSTVIDEO=) + if test -n "$_HAVE_LIBGSTVIDEO" ; then + GSTREAMER_LIBS="$GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" else - AC_MSG_ERROR([gstreamer and gstreamer-plugins-base development packages are needed to build gstreamer backend. Install them or disable gstreamer support with --disable-gstreamer]) + AC_MSG_ERROR([gstreamer-plugins-base found, but no libgstvideo. Something has gone terribly wrong. Try reinstalling gstreamer-plugins-base; failing that, disable the gstreamer backend with --disable-gstreamer.]) fi + LDFLAGS=$_SAVE_LDFLAGS + + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) fi -AC_SUBST(GSTREAMER_CFLAGS) -AC_SUBST(GSTREAMER_LIBS) + AC_SUBST(MOZ_GSTREAMER) +AC_SUBST(GST_API_VERSION) if test -n "$MOZ_GSTREAMER"; then - AC_DEFINE(MOZ_GSTREAMER) + AC_DEFINE(MOZ_GSTREAMER) + AC_DEFINE_UNQUOTED(GST_API_VERSION, "$GST_API_VERSION") fi diff --git content/media/gstreamer/GStreamerAllocator.cpp content/media/gstreamer/GStreamerAllocator.cpp new file mode 100644 index 0000000..69d0385 --- /dev/null +++ mozilla/content/media/gstreamer/GStreamerAllocator.cpp @@ -0,0 +1,197 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "GStreamerAllocator.h" + +#include +#include + +#include "GStreamerLoader.h" + +using namespace mozilla::layers; + +namespace mozilla { + +typedef struct +{ + GstAllocator parent; + GStreamerReader *reader; +} MozGfxMemoryAllocator; + +typedef struct +{ + GstAllocatorClass parent; +} MozGfxMemoryAllocatorClass; + +typedef struct +{ + GstMemory memory; + PlanarYCbCrImage* image; + guint8* data; +} MozGfxMemory; + +typedef struct +{ + GstMeta meta; +} MozGfxMeta; + +typedef struct +{ + GstVideoBufferPoolClass parent_class; +} MozGfxBufferPoolClass; + +typedef struct +{ + GstVideoBufferPool pool; +} MozGfxBufferPool; + +G_DEFINE_TYPE(MozGfxMemoryAllocator, moz_gfx_memory_allocator, GST_TYPE_ALLOCATOR); +G_DEFINE_TYPE(MozGfxBufferPool, moz_gfx_buffer_pool, GST_TYPE_VIDEO_BUFFER_POOL); + +void +moz_gfx_memory_reset(MozGfxMemory *mem) +{ + if (mem->image) + mem->image->Release(); + + ImageContainer* container = ((MozGfxMemoryAllocator*) mem->memory.allocator)->reader->GetImageContainer(); + mem->image = reinterpret_cast(container->CreateImage(ImageFormat::PLANAR_YCBCR).get()); + mem->data = mem->image->AllocateAndGetNewBuffer(mem->memory.size); +} + +static GstMemory* +moz_gfx_memory_allocator_alloc(GstAllocator* aAllocator, gsize aSize, + GstAllocationParams* aParams) +{ + MozGfxMemory* mem = g_slice_new (MozGfxMemory); + gsize maxsize = aSize + aParams->prefix + aParams->padding; + gst_memory_init(GST_MEMORY_CAST (mem), + (GstMemoryFlags)aParams->flags, + aAllocator, NULL, maxsize, aParams->align, + aParams->prefix, aSize); + mem->image = NULL; + moz_gfx_memory_reset(mem); + + return (GstMemory *) mem; +} + +static void +moz_gfx_memory_allocator_free (GstAllocator * allocator, GstMemory * gmem) +{ + MozGfxMemory *mem = (MozGfxMemory *) gmem; + + if (mem->memory.parent) + goto sub_mem; + + if (mem->image) + mem->image->Release(); + +sub_mem: + g_slice_free (MozGfxMemory, mem); +} + +static gpointer +moz_gfx_memory_map (MozGfxMemory * mem, gsize maxsize, GstMapFlags flags) +{ + // check that the allocation didn't fail + if (mem->data == nullptr) + return nullptr; + + return mem->data + mem->memory.offset; +} + +static gboolean +moz_gfx_memory_unmap (MozGfxMemory * mem) +{ + return TRUE; +} + +static MozGfxMemory * +moz_gfx_memory_share (MozGfxMemory * mem, gssize offset, gsize size) +{ + MozGfxMemory *sub; + GstMemory *parent; + + /* find the real parent */ + if ((parent = mem->memory.parent) == NULL) + parent = (GstMemory *) mem; + + if (size == (gsize) -1) + size = mem->memory.size - offset; + + /* the shared memory is always readonly */ + sub = g_slice_new (MozGfxMemory); + + gst_memory_init (GST_MEMORY_CAST (sub), + (GstMemoryFlags) (GST_MINI_OBJECT_FLAGS (parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY), + mem->memory.allocator, &mem->memory, mem->memory.maxsize, mem->memory.align, + mem->memory.offset + offset, size); + + sub->image = mem->image; + sub->data = mem->data; + + return sub; +} + +static void +moz_gfx_memory_allocator_class_init (MozGfxMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = moz_gfx_memory_allocator_alloc; + allocator_class->free = moz_gfx_memory_allocator_free; +} + +static void +moz_gfx_memory_allocator_init (MozGfxMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = "moz-gfx-image"; + alloc->mem_map = (GstMemoryMapFunction) moz_gfx_memory_map; + alloc->mem_unmap = (GstMemoryUnmapFunction) moz_gfx_memory_unmap; + alloc->mem_share = (GstMemoryShareFunction) moz_gfx_memory_share; + /* fallback copy and is_span */ +} + +void +moz_gfx_memory_allocator_set_reader(GstAllocator* aAllocator, GStreamerReader* aReader) +{ + MozGfxMemoryAllocator *allocator = (MozGfxMemoryAllocator *) aAllocator; + allocator->reader = aReader; +} + +nsRefPtr +moz_gfx_memory_get_image(GstMemory *aMemory) +{ + NS_ASSERTION(GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(aMemory->allocator), "Should be a gfx image"); + + return ((MozGfxMemory *) aMemory)->image; +} + +void +moz_gfx_buffer_pool_reset_buffer (GstBufferPool* aPool, GstBuffer* aBuffer) +{ + GstMemory* mem = gst_buffer_peek_memory(aBuffer, 0); + + NS_ASSERTION(GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(mem->allocator), "Should be a gfx image"); + moz_gfx_memory_reset((MozGfxMemory *) mem); + GST_BUFFER_POOL_CLASS(moz_gfx_buffer_pool_parent_class)->reset_buffer(aPool, aBuffer); +} + +static void +moz_gfx_buffer_pool_class_init (MozGfxBufferPoolClass * klass) +{ + GstBufferPoolClass *pool_class = (GstBufferPoolClass *) klass; + pool_class->reset_buffer = moz_gfx_buffer_pool_reset_buffer; +} + +static void +moz_gfx_buffer_pool_init (MozGfxBufferPool * pool) +{ +} + +} // namespace mozilla diff --git content/media/gstreamer/GStreamerAllocator.h content/media/gstreamer/GStreamerAllocator.h new file mode 100644 index 0000000..05a4412 --- /dev/null +++ mozilla/content/media/gstreamer/GStreamerAllocator.h @@ -0,0 +1,25 @@ +/* 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/. */ + +#if !defined(GStreamerAllocator_h_) +#define GStreamerAllocator_h_ + +#include "GStreamerReader.h" + +#define GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR (moz_gfx_memory_allocator_get_type()) +#define GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR)) +#define GST_TYPE_MOZ_GFX_BUFFER_POOL (moz_gfx_buffer_pool_get_type()) +#define GST_IS_MOZ_GFX_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_GFX_BUFFER_POOL)) + +namespace mozilla { + +GType moz_gfx_memory_allocator_get_type(); +void moz_gfx_memory_allocator_set_reader(GstAllocator *aAllocator, GStreamerReader* aReader); +nsRefPtr moz_gfx_memory_get_image(GstMemory *aMemory); + +GType moz_gfx_buffer_pool_get_type(); + +} // namespace mozilla + +#endif diff --git content/media/gstreamer/GStreamerFormatHelper.cpp content/media/gstreamer/GStreamerFormatHelper.cpp index be71331..a5e5db8 100644 --- mozilla/content/media/gstreamer/GStreamerFormatHelper.cpp +++ mozilla/content/media/gstreamer/GStreamerFormatHelper.cpp @@ -294,12 +294,23 @@ bool GStreamerFormatHelper::CanHandleCodecCaps(GstCaps* aCaps) GList* GStreamerFormatHelper::GetFactories() { NS_ASSERTION(sLoadOK, "GStreamer library not linked"); - uint32_t cookie = gst_default_registry_get_feature_list_cookie (); +#if GST_VERSION_MAJOR >= 1 + uint32_t cookie = gst_registry_get_feature_list_cookie(gst_registry_get()); +#else + uint32_t cookie = gst_default_registry_get_feature_list_cookie(); +#endif if (cookie != mCookie) { g_list_free(mFactories); +#if GST_VERSION_MAJOR >= 1 + mFactories = + gst_registry_feature_filter(gst_registry_get(), + (GstPluginFeatureFilter)FactoryFilter, + false, nullptr); +#else mFactories = gst_default_registry_feature_filter((GstPluginFeatureFilter)FactoryFilter, false, nullptr); +#endif mCookie = cookie; } diff --git content/media/gstreamer/GStreamerFunctionList.h content/media/gstreamer/GStreamerFunctionList.h index 56877c0..e169449 100644 --- mozilla/content/media/gstreamer/GStreamerFunctionList.h +++ mozilla/content/media/gstreamer/GStreamerFunctionList.h @@ -9,7 +9,6 @@ * List of symbol names we need to dlsym from the gstreamer library. */ GST_FUNC(LIBGSTAPP, gst_app_sink_get_type) -GST_FUNC(LIBGSTAPP, gst_app_sink_pull_buffer) GST_FUNC(LIBGSTAPP, gst_app_sink_set_callbacks) GST_FUNC(LIBGSTAPP, gst_app_src_end_of_stream) GST_FUNC(LIBGSTAPP, gst_app_src_get_size) @@ -22,10 +21,8 @@ GST_FUNC(LIBGSTAPP, gst_app_src_set_stream_type) GST_FUNC(LIBGSTREAMER, gst_bin_get_by_name) GST_FUNC(LIBGSTREAMER, gst_bin_get_type) GST_FUNC(LIBGSTREAMER, gst_bin_iterate_recurse) -GST_FUNC(LIBGSTREAMER, gst_buffer_copy_metadata) GST_FUNC(LIBGSTREAMER, gst_buffer_get_type) GST_FUNC(LIBGSTREAMER, gst_buffer_new) -GST_FUNC(LIBGSTREAMER, gst_buffer_new_and_alloc) GST_FUNC(LIBGSTREAMER, gst_bus_set_sync_handler) GST_FUNC(LIBGSTREAMER, gst_bus_timed_pop_filtered) GST_FUNC(LIBGSTREAMER, gst_caps_append) @@ -37,47 +34,37 @@ GST_FUNC(LIBGSTREAMER, gst_caps_new_any) GST_FUNC(LIBGSTREAMER, gst_caps_new_empty) GST_FUNC(LIBGSTREAMER, gst_caps_new_full) GST_FUNC(LIBGSTREAMER, gst_caps_new_simple) -GST_FUNC(LIBGSTREAMER, gst_caps_unref) -GST_FUNC(LIBGSTREAMER, gst_element_factory_get_klass) +GST_FUNC(LIBGSTREAMER, gst_caps_set_simple) GST_FUNC(LIBGSTREAMER, gst_element_factory_get_static_pad_templates) GST_FUNC(LIBGSTREAMER, gst_element_factory_get_type) GST_FUNC(LIBGSTREAMER, gst_element_factory_make) GST_FUNC(LIBGSTREAMER, gst_element_get_factory) -GST_FUNC(LIBGSTREAMER, gst_element_get_pad) +GST_FUNC(LIBGSTREAMER, gst_element_get_static_pad) GST_FUNC(LIBGSTREAMER, gst_element_get_type) GST_FUNC(LIBGSTREAMER, gst_element_query_convert) GST_FUNC(LIBGSTREAMER, gst_element_query_duration) GST_FUNC(LIBGSTREAMER, gst_element_seek_simple) GST_FUNC(LIBGSTREAMER, gst_element_set_state) -GST_FUNC(LIBGSTREAMER, gst_event_parse_new_segment) GST_FUNC(LIBGSTREAMER, gst_flow_get_name) GST_FUNC(LIBGSTREAMER, gst_init) GST_FUNC(LIBGSTREAMER, gst_init_check) GST_FUNC(LIBGSTREAMER, gst_iterator_next) GST_FUNC(LIBGSTREAMER, gst_message_parse_error) GST_FUNC(LIBGSTREAMER, gst_message_type_get_name) -GST_FUNC(LIBGSTREAMER, gst_mini_object_get_type) -GST_FUNC(LIBGSTREAMER, gst_mini_object_new) GST_FUNC(LIBGSTREAMER, gst_mini_object_ref) GST_FUNC(LIBGSTREAMER, gst_mini_object_unref) GST_FUNC(LIBGSTREAMER, gst_object_get_name) GST_FUNC(LIBGSTREAMER, gst_object_get_parent) GST_FUNC(LIBGSTREAMER, gst_object_unref) -GST_FUNC(LIBGSTREAMER, gst_pad_add_event_probe) -GST_FUNC(LIBGSTREAMER, gst_pad_alloc_buffer) GST_FUNC(LIBGSTREAMER, gst_pad_get_element_private) -GST_FUNC(LIBGSTREAMER, gst_pad_get_negotiated_caps) -GST_FUNC(LIBGSTREAMER, gst_pad_set_bufferalloc_function) GST_FUNC(LIBGSTREAMER, gst_pad_set_element_private) GST_FUNC(LIBGSTREAMER, gst_parse_bin_from_description) GST_FUNC(LIBGSTREAMER, gst_pipeline_get_bus) GST_FUNC(LIBGSTREAMER, gst_pipeline_get_type) GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_rank) GST_FUNC(LIBGSTREAMER, gst_registry_feature_filter) -GST_FUNC(LIBGSTREAMER, gst_registry_get_default) GST_FUNC(LIBGSTREAMER, gst_registry_get_feature_list_cookie) GST_FUNC(LIBGSTREAMER, gst_segment_init) -GST_FUNC(LIBGSTREAMER, gst_segment_set_newsegment) GST_FUNC(LIBGSTREAMER, gst_segment_to_stream_time) GST_FUNC(LIBGSTREAMER, gst_static_caps_get) GST_FUNC(LIBGSTREAMER, gst_structure_copy) @@ -85,11 +72,82 @@ GST_FUNC(LIBGSTREAMER, gst_structure_get_int) GST_FUNC(LIBGSTREAMER, gst_structure_get_value) GST_FUNC(LIBGSTREAMER, gst_structure_new) GST_FUNC(LIBGSTREAMER, gst_util_uint64_scale) + +#if GST_VERSION_MAJOR == 0 +GST_FUNC(LIBGSTAPP, gst_app_sink_pull_buffer) +GST_FUNC(LIBGSTREAMER, gst_buffer_copy_metadata) +GST_FUNC(LIBGSTREAMER, gst_buffer_new_and_alloc) +GST_FUNC(LIBGSTREAMER, gst_caps_unref) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_klass) +GST_FUNC(LIBGSTREAMER, gst_element_get_pad) +GST_FUNC(LIBGSTREAMER, gst_event_parse_new_segment) +GST_FUNC(LIBGSTREAMER, gst_mini_object_get_type) +GST_FUNC(LIBGSTREAMER, gst_mini_object_new) +GST_FUNC(LIBGSTREAMER, gst_pad_add_event_probe) +GST_FUNC(LIBGSTREAMER, gst_pad_alloc_buffer) +GST_FUNC(LIBGSTREAMER, gst_pad_get_negotiated_caps) +GST_FUNC(LIBGSTREAMER, gst_pad_set_bufferalloc_function) +GST_FUNC(LIBGSTREAMER, gst_registry_get_default) +GST_FUNC(LIBGSTREAMER, gst_segment_set_newsegment) GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_height) GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_offset) GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_width) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_pixel_stride) GST_FUNC(LIBGSTVIDEO, gst_video_format_get_row_stride) GST_FUNC(LIBGSTVIDEO, gst_video_format_parse_caps) +#else + +GST_FUNC(LIBGSTAPP, gst_app_sink_pull_sample) +GST_FUNC(LIBGSTREAMER, _gst_caps_any) +GST_FUNC(LIBGSTREAMER, gst_allocator_get_type) +GST_FUNC(LIBGSTREAMER, gst_buffer_copy_into) +GST_FUNC(LIBGSTREAMER, gst_buffer_extract) +GST_FUNC(LIBGSTREAMER, gst_buffer_get_meta) +GST_FUNC(LIBGSTREAMER, gst_buffer_get_size) +GST_FUNC(LIBGSTREAMER, gst_buffer_map) +GST_FUNC(LIBGSTREAMER, gst_buffer_new_allocate) +GST_FUNC(LIBGSTREAMER, gst_buffer_n_memory) +GST_FUNC(LIBGSTREAMER, gst_buffer_peek_memory) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_acquire_buffer) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_config_set_allocator) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_config_set_params) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_get_config) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_get_type) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_is_active) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_set_active) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_set_config) +GST_FUNC(LIBGSTREAMER, gst_buffer_set_size) +GST_FUNC(LIBGSTREAMER, gst_buffer_unmap) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_metadata) +GST_FUNC(LIBGSTREAMER, gst_event_parse_segment) +GST_FUNC(LIBGSTREAMER, gst_memory_init) +GST_FUNC(LIBGSTREAMER, gst_memory_map) +GST_FUNC(LIBGSTREAMER, gst_memory_unmap) +GST_FUNC(LIBGSTREAMER, gst_object_get_type) +GST_FUNC(LIBGSTREAMER, gst_pad_add_probe) +GST_FUNC(LIBGSTREAMER, gst_pad_get_current_caps) +GST_FUNC(LIBGSTREAMER, gst_pad_probe_info_get_query) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_meta) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_param) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_pool) +GST_FUNC(LIBGSTREAMER, gst_query_parse_allocation) +GST_FUNC(LIBGSTREAMER, gst_registry_get) +GST_FUNC(LIBGSTREAMER, gst_sample_get_buffer) +GST_FUNC(LIBGSTREAMER, gst_segment_copy_into) +GST_FUNC(LIBGSTREAMER, gst_structure_free) +GST_FUNC(LIBGSTVIDEO, gst_buffer_pool_config_get_video_alignment) +GST_FUNC(LIBGSTVIDEO, gst_buffer_pool_has_option) +GST_FUNC(LIBGSTVIDEO, gst_video_buffer_pool_get_type) +GST_FUNC(LIBGSTVIDEO, gst_video_frame_map) +GST_FUNC(LIBGSTVIDEO, gst_video_frame_unmap) +GST_FUNC(LIBGSTVIDEO, gst_video_info_align) +GST_FUNC(LIBGSTVIDEO, gst_video_info_from_caps) +GST_FUNC(LIBGSTVIDEO, gst_video_info_init) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_api_get_type) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_map) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_unmap) + +#endif /* * Functions that have been defined in the header file. We replace them so that @@ -99,6 +157,11 @@ GST_FUNC(LIBGSTVIDEO, gst_video_format_parse_caps) REPLACE_FUNC(gst_buffer_ref); REPLACE_FUNC(gst_buffer_unref); REPLACE_FUNC(gst_message_unref); + +#if GST_VERSION_MAJOR == 1 +REPLACE_FUNC(gst_caps_unref); +REPLACE_FUNC(gst_sample_unref); +#endif #endif #endif // !defined(__APPLE__) diff --git content/media/gstreamer/GStreamerLoader.cpp content/media/gstreamer/GStreamerLoader.cpp index 5961b23..e6457e0 100644 --- mozilla/content/media/gstreamer/GStreamerLoader.cpp +++ mozilla/content/media/gstreamer/GStreamerLoader.cpp @@ -6,13 +6,21 @@ #include #include -#include "GStreamerLoader.h" +#include "nsDebug.h" #include "mozilla/NullPtr.h" +#include "GStreamerLoader.h" + #define LIBGSTREAMER 0 #define LIBGSTAPP 1 #define LIBGSTVIDEO 2 +#ifdef __OpenBSD__ +#define LIB_GST_SUFFIX ".so" +#else +#define LIB_GST_SUFFIX ".so.0" +#endif + namespace mozilla { /* @@ -32,6 +40,11 @@ namespace mozilla { GstBuffer * gst_buffer_ref_impl(GstBuffer *buf); void gst_buffer_unref_impl(GstBuffer *buf); void gst_message_unref_impl(GstMessage *msg); +void gst_caps_unref_impl(GstCaps *caps); + +#if GST_VERSION_MAJOR == 1 +void gst_sample_unref_impl(GstSample *sample); +#endif bool load_gstreamer() @@ -58,32 +71,25 @@ load_gstreamer() if (major == GST_VERSION_MAJOR && minor == GST_VERSION_MINOR) { gstreamerLib = RTLD_DEFAULT; } else { -#ifdef __OpenBSD__ - gstreamerLib = dlopen("libgstreamer-0.10.so", RTLD_NOW | RTLD_LOCAL); -#else - gstreamerLib = dlopen("libgstreamer-0.10.so.0", RTLD_NOW | RTLD_LOCAL); -#endif + gstreamerLib = dlopen("libgstreamer-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL); } - void *handles[] = { + void *handles[3] = { gstreamerLib, -#ifdef __OpenBSD__ - dlopen("libgstapp-0.10.so", RTLD_NOW | RTLD_LOCAL), - dlopen("libgstvideo-0.10.so", RTLD_NOW | RTLD_LOCAL) -#else - dlopen("libgstapp-0.10.so.0", RTLD_NOW | RTLD_LOCAL), - dlopen("libgstvideo-0.10.so.0", RTLD_NOW | RTLD_LOCAL) -#endif + dlopen("libgstapp-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL), + dlopen("libgstvideo-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL) }; for (size_t i = 0; i < sizeof(handles) / sizeof(handles[0]); i++) { if (!handles[i]) { + NS_WARNING("Couldn't link gstreamer libraries"); goto fail; } } #define GST_FUNC(lib, symbol) \ if (!(symbol = (typeof(symbol))dlsym(handles[lib], #symbol))) { \ + NS_WARNING("Couldn't link symbol " #symbol); \ goto fail; \ } #define REPLACE_FUNC(symbol) symbol = symbol##_impl; @@ -123,4 +129,18 @@ gst_message_unref_impl(GstMessage *msg) gst_mini_object_unref(GST_MINI_OBJECT_CAST(msg)); } +#if GST_VERSION_MAJOR == 1 +void +gst_sample_unref_impl(GstSample *sample) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(sample)); +} +#endif + +void +gst_caps_unref_impl(GstCaps *caps) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(caps)); +} + } diff --git content/media/gstreamer/GStreamerLoader.h content/media/gstreamer/GStreamerLoader.h index 2d801722..cd7fe6d 100644 --- mozilla/content/media/gstreamer/GStreamerLoader.h +++ mozilla/content/media/gstreamer/GStreamerLoader.h @@ -22,6 +22,11 @@ #include #pragma GCC diagnostic pop +#if GST_VERSION_MAJOR == 1 +#include +#include +#endif + namespace mozilla { /* @@ -42,4 +47,7 @@ bool load_gstreamer(); } +#undef GST_CAPS_ANY +#define GST_CAPS_ANY (*_gst_caps_any) + #endif // GStreamerLoader_h_ diff --git content/media/gstreamer/GStreamerReader-0.10.cpp content/media/gstreamer/GStreamerReader-0.10.cpp new file mode 100644 index 0000000..fb98bde --- /dev/null +++ mozilla/content/media/gstreamer/GStreamerReader-0.10.cpp @@ -0,0 +1,200 @@ +#include "nsError.h" +#include "MediaDecoderStateMachine.h" +#include "AbstractMediaDecoder.h" +#include "MediaResource.h" +#include "GStreamerReader.h" +#include "GStreamerMozVideoBuffer.h" +#include "GStreamerFormatHelper.h" +#include "VideoUtils.h" +#include "mozilla/dom/TimeRanges.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; +using mozilla::layers::PlanarYCbCrImage; +using mozilla::layers::ImageContainer; + +GstFlowReturn GStreamerReader::AllocateVideoBufferCb(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf) +{ + GStreamerReader* reader = reinterpret_cast(gst_pad_get_element_private(aPad)); + return reader->AllocateVideoBuffer(aPad, aOffset, aSize, aCaps, aBuf); +} + +GstFlowReturn GStreamerReader::AllocateVideoBuffer(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf) +{ + nsRefPtr image; + return AllocateVideoBufferFull(aPad, aOffset, aSize, aCaps, aBuf, image); +} + +GstFlowReturn GStreamerReader::AllocateVideoBufferFull(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf, + nsRefPtr& aImage) +{ + /* allocate an image using the container */ + ImageContainer* container = mDecoder->GetImageContainer(); + if (container == nullptr) { + return GST_FLOW_ERROR; + } + PlanarYCbCrImage* img = reinterpret_cast(container->CreateImage(ImageFormat::PLANAR_YCBCR).get()); + nsRefPtr image = dont_AddRef(img); + + /* prepare a GstBuffer pointing to the underlying PlanarYCbCrImage buffer */ + GstBuffer* buf = GST_BUFFER(gst_moz_video_buffer_new()); + GST_BUFFER_SIZE(buf) = aSize; + /* allocate the actual YUV buffer */ + GST_BUFFER_DATA(buf) = image->AllocateAndGetNewBuffer(aSize); + + aImage = image; + + /* create a GstMozVideoBufferData to hold the image */ + GstMozVideoBufferData* bufferdata = new GstMozVideoBufferData(image); + + /* Attach bufferdata to our GstMozVideoBuffer, it will take care to free it */ + gst_moz_video_buffer_set_data(GST_MOZ_VIDEO_BUFFER(buf), bufferdata); + + *aBuf = buf; + return GST_FLOW_OK; +} + +gboolean GStreamerReader::EventProbe(GstPad* aPad, GstEvent* aEvent) +{ + GstElement* parent = GST_ELEMENT(gst_pad_get_parent(aPad)); + switch(GST_EVENT_TYPE(aEvent)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + gdouble rate; + GstFormat format; + gint64 start, stop, position; + GstSegment* segment; + + /* Store the segments so we can convert timestamps to stream time, which + * is what the upper layers sync on. + */ + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + gst_event_parse_new_segment(aEvent, &update, &rate, &format, + &start, &stop, &position); + if (parent == GST_ELEMENT(mVideoAppSink)) + segment = &mVideoSegment; + else + segment = &mAudioSegment; + gst_segment_set_newsegment(segment, update, rate, format, + start, stop, position); + break; + } + case GST_EVENT_FLUSH_STOP: + /* Reset on seeks */ + ResetDecode(); + break; + default: + break; + } + gst_object_unref(parent); + + return TRUE; +} + +gboolean GStreamerReader::EventProbeCb(GstPad* aPad, + GstEvent* aEvent, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + return reader->EventProbe(aPad, aEvent); +} + +nsRefPtr GStreamerReader::GetImageFromBuffer(GstBuffer* aBuffer) +{ + if (!GST_IS_MOZ_VIDEO_BUFFER (aBuffer)) + return nullptr; + + nsRefPtr image; + GstMozVideoBufferData* bufferdata = reinterpret_cast(gst_moz_video_buffer_get_data(GST_MOZ_VIDEO_BUFFER(aBuffer))); + image = bufferdata->mImage; + + PlanarYCbCrImage::Data data; + data.mPicX = data.mPicY = 0; + data.mPicSize = gfx::IntSize(mPicture.width, mPicture.height); + data.mStereoMode = StereoMode::MONO; + + data.mYChannel = GST_BUFFER_DATA(aBuffer); + data.mYStride = gst_video_format_get_row_stride(mFormat, 0, mPicture.width); + data.mYSize = gfx::IntSize(data.mYStride, + gst_video_format_get_component_height(mFormat, 0, mPicture.height)); + data.mYSkip = 0; + data.mCbCrStride = gst_video_format_get_row_stride(mFormat, 1, mPicture.width); + data.mCbCrSize = gfx::IntSize(data.mCbCrStride, + gst_video_format_get_component_height(mFormat, 1, mPicture.height)); + data.mCbChannel = data.mYChannel + gst_video_format_get_component_offset(mFormat, 1, + mPicture.width, mPicture.height); + data.mCrChannel = data.mYChannel + gst_video_format_get_component_offset(mFormat, 2, + mPicture.width, mPicture.height); + data.mCbSkip = 0; + data.mCrSkip = 0; + + image->SetDataNoCopy(data); + + return image; +} + +void GStreamerReader::CopyIntoImageBuffer(GstBuffer* aBuffer, + GstBuffer** aOutBuffer, + nsRefPtr &aImage) +{ + AllocateVideoBufferFull(nullptr, GST_BUFFER_OFFSET(aBuffer), + GST_BUFFER_SIZE(aBuffer), nullptr, aOutBuffer, aImage); + + gst_buffer_copy_metadata(*aOutBuffer, aBuffer, (GstBufferCopyFlags)GST_BUFFER_COPY_ALL); + memcpy(GST_BUFFER_DATA(*aOutBuffer), GST_BUFFER_DATA(aBuffer), GST_BUFFER_SIZE(*aOutBuffer)); + + aImage = GetImageFromBuffer(*aOutBuffer); +} + +GstCaps* GStreamerReader::BuildAudioSinkCaps() +{ + GstCaps* caps; +#ifdef IS_LITTLE_ENDIAN + int endianness = 1234; +#else + int endianness = 4321; +#endif + gint width; +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 + caps = gst_caps_from_string("audio/x-raw-float, channels={1,2}"); + width = 32; +#else /* !MOZ_SAMPLE_TYPE_FLOAT32 */ + caps = gst_caps_from_string("audio/x-raw-int, channels={1,2}"); + width = 16; +#endif + gst_caps_set_simple(caps, + "width", G_TYPE_INT, width, + "endianness", G_TYPE_INT, endianness, + NULL); + + return caps; +} + +void GStreamerReader::InstallPadCallbacks() +{ + GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); + gst_pad_add_event_probe(sinkpad, + G_CALLBACK(&GStreamerReader::EventProbeCb), this); + + gst_pad_set_bufferalloc_function(sinkpad, GStreamerReader::AllocateVideoBufferCb); + gst_pad_set_element_private(sinkpad, this); + gst_object_unref(sinkpad); + + sinkpad = gst_element_get_static_pad(GST_ELEMENT(mAudioAppSink), "sink"); + gst_pad_add_event_probe(sinkpad, + G_CALLBACK(&GStreamerReader::EventProbeCb), this); + gst_object_unref(sinkpad); +} diff --git content/media/gstreamer/GStreamerReader.cpp content/media/gstreamer/GStreamerReader.cpp index 2be45dc..54509e3 100644 --- mozilla/content/media/gstreamer/GStreamerReader.cpp +++ mozilla/content/media/gstreamer/GStreamerReader.cpp @@ -10,8 +10,10 @@ #include "AbstractMediaDecoder.h" #include "MediaResource.h" #include "GStreamerReader.h" +#if GST_VERSION_MAJOR >= 1 +#include "GStreamerAllocator.h" +#endif #include "GStreamerFormatHelper.h" -#include "GStreamerMozVideoBuffer.h" #include "VideoUtils.h" #include "mozilla/dom/TimeRanges.h" #include "mozilla/Preferences.h" @@ -31,14 +33,16 @@ extern PRLogModuleInfo* gMediaDecoderLog; #define LOG(type, msg, ...) #endif -extern bool -IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane, - const VideoData::YCbCrBuffer::Plane& aCbPlane, - const VideoData::YCbCrBuffer::Plane& aCrPlane); - +#if DEBUG static const unsigned int MAX_CHANNELS = 4; -// Let the demuxer work in pull mode for short files -static const int SHORT_FILE_SIZE = 1024 * 1024; +#endif +// Let the demuxer work in pull mode for short files. This used to be a micro +// optimization to have more accurate durations for ogg files in mochitests. +// Since as of today we aren't using gstreamer to demux ogg, and having demuxers +// work in pull mode over http makes them slower (since they really assume +// near-zero latency in pull mode) set the constant to 0 for now, which +// effectively disables it. +static const int SHORT_FILE_SIZE = 0; // The default resource->Read() size when working in push mode static const int DEFAULT_SOURCE_READ_SIZE = 50 * 1024; @@ -60,6 +62,10 @@ GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder) : MediaDecoderReader(aDecoder), mMP3FrameParser(aDecoder->GetResource()->GetLength()), mUseParserDuration(false), +#if GST_VERSION_MAJOR >= 1 + mAllocator(nullptr), + mBufferPool(nullptr), +#endif mPlayBin(nullptr), mBus(nullptr), mSource(nullptr), @@ -72,6 +78,9 @@ GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder) mAudioSinkBufferCount(0), mGstThreadsMonitor("media.gst.threads"), mReachedEos(false), +#if GST_VERSION_MAJOR >= 1 + mConfigureAlignment(true), +#endif fpsNum(0), fpsDen(0) { @@ -83,8 +92,12 @@ GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder) mSinkCallbacks.eos = GStreamerReader::EosCb; mSinkCallbacks.new_preroll = GStreamerReader::NewPrerollCb; +#if GST_VERSION_MAJOR >= 1 + mSinkCallbacks.new_sample = GStreamerReader::NewBufferCb; +#else mSinkCallbacks.new_buffer = GStreamerReader::NewBufferCb; mSinkCallbacks.new_buffer_list = nullptr; +#endif gst_segment_init(&mVideoSegment, GST_FORMAT_UNDEFINED); gst_segment_init(&mAudioSegment, GST_FORMAT_UNDEFINED); @@ -108,65 +121,59 @@ GStreamerReader::~GStreamerReader() mAudioAppSink = nullptr; gst_object_unref(mBus); mBus = nullptr; +#if GST_VERSION_MAJOR >= 1 + g_object_unref(mAllocator); + g_object_unref(mBufferPool); +#endif } } nsresult GStreamerReader::Init(MediaDecoderReader* aCloneDonor) { - GError* error = nullptr; - if (!gst_init_check(0, 0, &error)) { - LOG(PR_LOG_ERROR, "gst initialization failed: %s", error->message); - g_error_free(error); - return NS_ERROR_FAILURE; - } + GStreamerFormatHelper::Instance(); + +#if GST_VERSION_MAJOR >= 1 + mAllocator = static_cast(g_object_new(GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR, nullptr)); + moz_gfx_memory_allocator_set_reader(mAllocator, this); + + mBufferPool = static_cast(g_object_new(GST_TYPE_MOZ_GFX_BUFFER_POOL, nullptr)); +#endif +#if GST_VERSION_MAJOR >= 1 + mPlayBin = gst_element_factory_make("playbin", nullptr); +#else mPlayBin = gst_element_factory_make("playbin2", nullptr); +#endif if (!mPlayBin) { - LOG(PR_LOG_ERROR, "couldn't create playbin2"); + LOG(PR_LOG_ERROR, "couldn't create playbin"); return NS_ERROR_FAILURE; } g_object_set(mPlayBin, "buffer-size", 0, nullptr); mBus = gst_pipeline_get_bus(GST_PIPELINE(mPlayBin)); mVideoSink = gst_parse_bin_from_description("capsfilter name=filter ! " - "appsink name=videosink sync=true max-buffers=1 " + "appsink name=videosink sync=false max-buffers=1 " +#if GST_VERSION_MAJOR >= 1 + "caps=video/x-raw,format=I420" +#else "caps=video/x-raw-yuv,format=(fourcc)I420" +#endif , TRUE, nullptr); mVideoAppSink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(mVideoSink), "videosink")); - gst_app_sink_set_callbacks(mVideoAppSink, &mSinkCallbacks, - (gpointer) this, nullptr); - GstPad* sinkpad = gst_element_get_pad(GST_ELEMENT(mVideoAppSink), "sink"); - gst_pad_add_event_probe(sinkpad, - G_CALLBACK(&GStreamerReader::EventProbeCb), this); - gst_object_unref(sinkpad); - gst_pad_set_bufferalloc_function(sinkpad, GStreamerReader::AllocateVideoBufferCb); - gst_pad_set_element_private(sinkpad, this); - mAudioSink = gst_parse_bin_from_description("capsfilter name=filter ! " -#ifdef MOZ_SAMPLE_TYPE_FLOAT32 - "appsink name=audiosink max-buffers=2 sync=false caps=audio/x-raw-float," -#ifdef IS_LITTLE_ENDIAN - "channels={1,2},width=32,endianness=1234", TRUE, nullptr); -#else - "channels={1,2},width=32,endianness=4321", TRUE, nullptr); -#endif -#else - "appsink name=audiosink max-buffers=2 sync=false caps=audio/x-raw-int," -#ifdef IS_LITTLE_ENDIAN - "channels={1,2},width=16,endianness=1234", TRUE, nullptr); -#else - "channels={1,2},width=16,endianness=4321", TRUE, nullptr); -#endif -#endif + "appsink name=audiosink sync=false max-buffers=1", TRUE, nullptr); mAudioAppSink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(mAudioSink), "audiosink")); + GstCaps* caps = BuildAudioSinkCaps(); + g_object_set(mAudioAppSink, "caps", caps, nullptr); + gst_caps_unref(caps); + + gst_app_sink_set_callbacks(mVideoAppSink, &mSinkCallbacks, + (gpointer) this, nullptr); gst_app_sink_set_callbacks(mAudioAppSink, &mSinkCallbacks, (gpointer) this, nullptr); - sinkpad = gst_element_get_pad(GST_ELEMENT(mAudioAppSink), "sink"); - gst_pad_add_event_probe(sinkpad, - G_CALLBACK(&GStreamerReader::EventProbeCb), this); - gst_object_unref(sinkpad); + InstallPadCallbacks(); g_object_set(mPlayBin, "uri", "appsrc://", "video-sink", mVideoSink, @@ -331,13 +340,12 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, /* Little trick: set the target caps to "skip" so that playbin2 fails to * find a decoder for the stream we want to skip. */ - GstCaps* filterCaps = gst_caps_new_simple ("skip", nullptr); + GstCaps* filterCaps = gst_caps_new_simple ("skip", nullptr, nullptr); g_object_set(filter, "caps", filterCaps, nullptr); gst_caps_unref(filterCaps); gst_object_unref(filter); } - /* start the pipeline */ LOG(PR_LOG_DEBUG, "starting metadata pipeline"); gst_element_set_state(mPlayBin, GST_STATE_PAUSED); @@ -358,6 +366,7 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, gst_message_unref(message); ret = NS_ERROR_FAILURE; } else { + LOG(PR_LOG_DEBUG, "read metadata pipeline prerolled"); gst_message_unref(message); ret = NS_OK; break; @@ -373,21 +383,24 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, /* FIXME: workaround for a bug in matroskademux. This seek makes matroskademux * parse the index */ + LOG(PR_LOG_DEBUG, "doing matroskademux seek hack"); if (gst_element_seek_simple(mPlayBin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, 0)) { /* after a seek we need to wait again for ASYNC_DONE */ - message = gst_bus_timed_pop_filtered(mBus, GST_CLOCK_TIME_NONE, + message = gst_bus_timed_pop_filtered(mBus, 5 * GST_SECOND, (GstMessageType)(GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR)); - if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { + LOG(PR_LOG_DEBUG, "matroskademux seek hack done"); + if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ASYNC_DONE) { gst_element_set_state(mPlayBin, GST_STATE_NULL); gst_message_unref(message); return NS_ERROR_FAILURE; } + } else { + LOG(PR_LOG_DEBUG, "matroskademux seek hack failed (non fatal)"); } /* report the duration */ gint64 duration; - GstFormat format = GST_FORMAT_TIME; if (isMP3 && mMP3FrameParser.IsMP3()) { // The MP3FrameParser has reported a duration; use that over the gstreamer @@ -396,17 +409,25 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, mUseParserDuration = true; mLastParserDuration = mMP3FrameParser.GetDuration(); mDecoder->SetMediaDuration(mLastParserDuration); - - } else if (gst_element_query_duration(GST_ELEMENT(mPlayBin), - &format, &duration) && format == GST_FORMAT_TIME) { - // Otherwise use the gstreamer duration. - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - LOG(PR_LOG_DEBUG, "returning duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration)); - duration = GST_TIME_AS_USECONDS (duration); - mDecoder->SetMediaDuration(duration); - } else { - mDecoder->SetMediaSeekable(false); + LOG(PR_LOG_DEBUG, "querying duration"); + // Otherwise use the gstreamer duration. +#if GST_VERSION_MAJOR >= 1 + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + GST_FORMAT_TIME, &duration)) { +#else + GstFormat format = GST_FORMAT_TIME; + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + &format, &duration) && format == GST_FORMAT_TIME) { +#endif + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + LOG(PR_LOG_DEBUG, "have duration %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration)); + duration = GST_TIME_AS_USECONDS (duration); + mDecoder->SetMediaDuration(duration); + } else { + mDecoder->SetMediaSeekable(false); + } } int n_video = 0, n_audio = 0; @@ -410,7 +428,11 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, *aTags = nullptr; // Watch the pipeline for fatal errors +#if GST_VERSION_MAJOR >= 1 + gst_bus_set_sync_handler(mBus, GStreamerReader::ErrorCb, this, nullptr); +#else gst_bus_set_sync_handler(mBus, GStreamerReader::ErrorCb, this); +#endif /* set the pipeline to PLAYING so that it starts decoding and queueing data in * the appsinks */ @@ -424,19 +446,35 @@ nsresult GStreamerReader::CheckSupportedFormats() bool done = false; bool unsupported = false; - GstIterator *it = gst_bin_iterate_recurse(GST_BIN(mPlayBin)); + GstIterator* it = gst_bin_iterate_recurse(GST_BIN(mPlayBin)); while (!done) { + GstIteratorResult res; GstElement* element; - GstIteratorResult res = gst_iterator_next(it, (void **)&element); + +#if GST_VERSION_MAJOR >= 1 + GValue value = {0,}; + res = gst_iterator_next(it, &value); +#else + res = gst_iterator_next(it, (void **) &element); +#endif switch(res) { case GST_ITERATOR_OK: { +#if GST_VERSION_MAJOR >= 1 + element = GST_ELEMENT (g_value_get_object (&value)); +#endif GstElementFactory* factory = gst_element_get_factory(element); if (factory) { const char* klass = gst_element_factory_get_klass(factory); - GstPad* pad = gst_element_get_pad(element, "sink"); + GstPad* pad = gst_element_get_static_pad(element, "sink"); if (pad) { - GstCaps* caps = gst_pad_get_negotiated_caps(pad); + GstCaps* caps; + +#if GST_VERSION_MAJOR >= 1 + caps = gst_pad_get_current_caps(pad); +#else + caps = gst_pad_get_negotiated_caps(pad); +#endif if (caps) { /* check for demuxers but ignore elements like id3demux */ @@ -451,7 +489,11 @@ nsresult GStreamerReader::CheckSupportedFormats() } } +#if GST_VERSION_MAJOR >= 1 + g_value_unset (&value); +#else gst_object_unref(element); +#endif done = unsupported; break; } @@ -475,6 +517,8 @@ nsresult GStreamerReader::ResetDecode() { nsresult res = NS_OK; + LOG(PR_LOG_DEBUG, "reset decode"); + if (NS_FAILED(MediaDecoderReader::ResetDecode())) { res = NS_ERROR_FAILURE; } @@ -485,6 +529,11 @@ nsresult GStreamerReader::ResetDecode() mVideoSinkBufferCount = 0; mAudioSinkBufferCount = 0; mReachedEos = false; +#if GST_VERSION_MAJOR >= 1 + mConfigureAlignment = true; +#endif + + LOG(PR_LOG_DEBUG, "reset decode done"); return res; } @@ -508,11 +557,11 @@ bool GStreamerReader::DecodeAudioData() /* We have nothing decoded so it makes no sense to return to the state machine * as it will call us back immediately, we'll return again and so on, wasting * CPU cycles for no job done. So, block here until there is either video or - * audio data available + * audio data available */ mon.Wait(); if (!mAudioSinkBufferCount) { - /* There is still no audio data available, so either there is video data or + /* There is still no audio data available, so either there is video data or * something else has happened (Eos, etc...). Return to the state machine * to process it. */ @@ -533,24 +584,44 @@ bool GStreamerReader::DecodeAudioData() } } +#if GST_VERSION_MAJOR >= 1 + GstSample *sample = gst_app_sink_pull_sample(mAudioAppSink); + buffer = gst_buffer_ref(gst_sample_get_buffer(sample)); + gst_sample_unref(sample); +#else buffer = gst_app_sink_pull_buffer(mAudioAppSink); +#endif + mAudioSinkBufferCount--; } int64_t timestamp = GST_BUFFER_TIMESTAMP(buffer); timestamp = gst_segment_to_stream_time(&mAudioSegment, GST_FORMAT_TIME, timestamp); + timestamp = GST_TIME_AS_USECONDS(timestamp); + int64_t duration = 0; if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer))) duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer)); int64_t offset = GST_BUFFER_OFFSET(buffer); +#if GST_VERSION_MAJOR >= 1 + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + unsigned int size = info.size; +#else unsigned int size = GST_BUFFER_SIZE(buffer); +#endif int32_t frames = (size / sizeof(AudioDataValue)) / mInfo.mAudio.mChannels; ssize_t outSize = static_cast(size / sizeof(AudioDataValue)); nsAutoArrayPtr data(new AudioDataValue[outSize]); +#if GST_VERSION_MAJOR >= 1 + memcpy(data, info.data, info.size); + gst_buffer_unmap(buffer, &info); +#else memcpy(data, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer)); +#endif AudioData* audio = new AudioData(offset, timestamp, duration, frames, data.forget(), mInfo.mAudio.mChannels); @@ -552,7 +620,7 @@ bool GStreamerReader::DecodeAudioData() } bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, - int64_t aTimeThreshold) + int64_t aTimeThreshold) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); @@ -571,11 +639,11 @@ bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, /* We have nothing decoded so it makes no sense to return to the state machine * as it will call us back immediately, we'll return again and so on, wasting * CPU cycles for no job done. So, block here until there is either video or - * audio data available + * audio data available */ mon.Wait(); if (!mVideoSinkBufferCount) { - /* There is still no video data available, so either there is audio data or + /* There is still no video data available, so either there is audio data or * something else has happened (Eos, etc...). Return to the state machine * to process it */ @@ -589,11 +657,17 @@ bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, mDecoder->NotifyDecodedFrames(0, 1); +#if GST_VERSION_MAJOR >= 1 + GstSample *sample = gst_app_sink_pull_sample(mVideoAppSink); + buffer = gst_buffer_ref(gst_sample_get_buffer(sample)); + gst_sample_unref(sample); +#else buffer = gst_app_sink_pull_buffer(mVideoAppSink); +#endif mVideoSinkBufferCount--; } - bool isKeyframe = !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DISCONT); + bool isKeyframe = !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); if ((aKeyFrameSkip && !isKeyframe)) { gst_buffer_unref(buffer); return true; @@ -611,10 +687,18 @@ bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, "frame has invalid timestamp"); timestamp = GST_TIME_AS_USECONDS(timestamp); + int64_t duration; + if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer))) + duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer)); + else if (fpsNum && fpsDen) + /* add 1-frame duration */ + duration = gst_util_uint64_scale(GST_USECOND, fpsDen, fpsNum); + if (timestamp < aTimeThreshold) { LOG(PR_LOG_DEBUG, "skipping frame %" GST_TIME_FORMAT " threshold %" GST_TIME_FORMAT, - GST_TIME_ARGS(timestamp), GST_TIME_ARGS(aTimeThreshold)); + GST_TIME_ARGS(timestamp * 1000), + GST_TIME_ARGS(aTimeThreshold * 1000)); gst_buffer_unref(buffer); return true; } @@ -623,61 +707,36 @@ bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, /* no more frames */ return false; - int64_t duration = 0; - if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer))) - duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer)); - else if (fpsNum && fpsDen) - /* 1-frame duration */ - duration = gst_util_uint64_scale(GST_USECOND, fpsNum, fpsDen); - - nsRefPtr image; - GstMozVideoBufferData* bufferdata = reinterpret_cast - GST_IS_MOZ_VIDEO_BUFFER(buffer)?gst_moz_video_buffer_get_data(GST_MOZ_VIDEO_BUFFER(buffer)):nullptr; - - if(bufferdata) - image = bufferdata->mImage; +#if GST_VERSION_MAJOR >= 1 + if (mConfigureAlignment && buffer->pool) { + GstStructure *config = gst_buffer_pool_get_config(buffer->pool); + GstVideoAlignment align; + if (gst_buffer_pool_config_get_video_alignment(config, &align)) + gst_video_info_align(&mVideoInfo, &align); + gst_structure_free(config); + mConfigureAlignment = false; + } +#endif + nsRefPtr image = GetImageFromBuffer(buffer); if (!image) { /* Ugh, upstream is not calling gst_pad_alloc_buffer(). Fallback to * allocating a PlanarYCbCrImage backed GstBuffer here and memcpy. */ GstBuffer* tmp = nullptr; - AllocateVideoBufferFull(nullptr, GST_BUFFER_OFFSET(buffer), - GST_BUFFER_SIZE(buffer), nullptr, &tmp, image); - - /* copy */ - gst_buffer_copy_metadata(tmp, buffer, (GstBufferCopyFlags)GST_BUFFER_COPY_ALL); - memcpy(GST_BUFFER_DATA(tmp), GST_BUFFER_DATA(buffer), - GST_BUFFER_SIZE(tmp)); + CopyIntoImageBuffer(buffer, &tmp, image); gst_buffer_unref(buffer); buffer = tmp; } - guint8* data = GST_BUFFER_DATA(buffer); - - int width = mPicture.width; - int height = mPicture.height; - GstVideoFormat format = mFormat; - - VideoData::YCbCrBuffer b; - for(int i = 0; i < 3; i++) { - b.mPlanes[i].mData = data + gst_video_format_get_component_offset(format, i, - width, height); - b.mPlanes[i].mStride = gst_video_format_get_row_stride(format, i, width); - b.mPlanes[i].mHeight = gst_video_format_get_component_height(format, - i, height); - b.mPlanes[i].mWidth = gst_video_format_get_component_width(format, - i, width); - b.mPlanes[i].mOffset = 0; - b.mPlanes[i].mSkip = 0; - } - - isKeyframe = !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); int64_t offset = mDecoder->GetResource()->Tell(); // Estimate location in media. - VideoData* video = VideoData::Create(mInfo.mVideo, image, offset, - timestamp, duration, b, - isKeyframe, -1, mPicture); + VideoData* video = VideoData::CreateFromImage(mInfo.mVideo, + mDecoder->GetImageContainer(), + offset, timestamp, duration, + static_cast(image.get()), + isKeyframe, -1, mPicture); mVideoQueue.Push(video); + gst_buffer_unref(buffer); return true; @@ -698,6 +755,10 @@ nsresult GStreamerReader::Seek(int64_t aTarget, return NS_ERROR_FAILURE; } LOG(PR_LOG_DEBUG, "seek succeeded"); + GstMessage* message = gst_bus_timed_pop_filtered(mBus, GST_CLOCK_TIME_NONE, + (GstMessageType)(GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR)); + gst_message_unref(message); + LOG(PR_LOG_DEBUG, "seek completed"); return DecodeToTarget(aTarget); } @@ -709,7 +770,9 @@ nsresult GStreamerReader::GetBuffered(dom::TimeRanges* aBuffered, return NS_OK; } +#if GST_VERSION_MAJOR == 0 GstFormat format = GST_FORMAT_TIME; +#endif MediaResource* resource = mDecoder->GetResource(); nsTArray ranges; resource->GetCachedRanges(ranges); @@ -731,12 +794,21 @@ nsresult GStreamerReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t endOffset = ranges[index].mEnd; gint64 startTime, endTime; +#if GST_VERSION_MAJOR >= 1 + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + startOffset, GST_FORMAT_TIME, &startTime)) + continue; + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + endOffset, GST_FORMAT_TIME, &endTime)) + continue; +#else if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, startOffset, &format, &startTime) || format != GST_FORMAT_TIME) continue; if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, endOffset, &format, &endTime) || format != GST_FORMAT_TIME) continue; +#endif double start = (double) GST_TIME_AS_USECONDS (startTime) / GST_MSECOND; double end = (double) GST_TIME_AS_USECONDS (endTime) / GST_MSECOND; @@ -755,7 +827,13 @@ void GStreamerReader::ReadAndPushData(guint aLength) nsresult rv = NS_OK; GstBuffer* buffer = gst_buffer_new_and_alloc(aLength); +#if GST_VERSION_MAJOR >= 1 + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_WRITE); + guint8 *data = info.data; +#else guint8* data = GST_BUFFER_DATA(buffer); +#endif uint32_t size = 0, bytesRead = 0; while(bytesRead < aLength) { rv = resource->Read(reinterpret_cast(data + bytesRead), @@ -780,7 +860,12 @@ void GStreamerReader::ReadAndPushData(guint aLength) int64_t offset2 = resource->Tell(); unused << offset2; +#if GST_VERSION_MAJOR >= 1 + gst_buffer_unmap(buffer, &info); + gst_buffer_set_size(buffer, bytesRead); +#else GST_BUFFER_SIZE(buffer) = bytesRead; +#endif GstFlowReturn ret = gst_app_src_push_buffer(mSource, gst_buffer_ref(buffer)); if (ret != GST_FLOW_OK) { @@ -786,8 +869,13 @@ int64_t GStreamerReader::QueryDuration() gint64 duration = 0; GstFormat format = GST_FORMAT_TIME; +#if GST_VERSION_MAJOR >= 1 + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + format, &duration)) { +#else if (gst_element_query_duration(GST_ELEMENT(mPlayBin), &format, &duration)) { +#endif if (format == GST_FORMAT_TIME) { LOG(PR_LOG_DEBUG, "pipeline duration %" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); @@ -893,109 +984,6 @@ gboolean GStreamerReader::SeekData(GstAppSrc* aSrc, guint64 aOffset) return NS_SUCCEEDED(rv); } -gboolean GStreamerReader::EventProbeCb(GstPad* aPad, - GstEvent* aEvent, - gpointer aUserData) -{ - GStreamerReader* reader = reinterpret_cast(aUserData); - return reader->EventProbe(aPad, aEvent); -} - -gboolean GStreamerReader::EventProbe(GstPad* aPad, GstEvent* aEvent) -{ - GstElement* parent = GST_ELEMENT(gst_pad_get_parent(aPad)); - switch(GST_EVENT_TYPE(aEvent)) { - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate; - GstFormat format; - gint64 start, stop, position; - GstSegment* segment; - - /* Store the segments so we can convert timestamps to stream time, which - * is what the upper layers sync on. - */ - ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); - gst_event_parse_new_segment(aEvent, &update, &rate, &format, - &start, &stop, &position); - if (parent == GST_ELEMENT(mVideoAppSink)) - segment = &mVideoSegment; - else - segment = &mAudioSegment; - gst_segment_set_newsegment(segment, update, rate, format, - start, stop, position); - break; - } - case GST_EVENT_FLUSH_STOP: - /* Reset on seeks */ - ResetDecode(); - break; - default: - break; - } - gst_object_unref(parent); - - return TRUE; -} - -GstFlowReturn GStreamerReader::AllocateVideoBufferFull(GstPad* aPad, - guint64 aOffset, - guint aSize, - GstCaps* aCaps, - GstBuffer** aBuf, - nsRefPtr& aImage) -{ - /* allocate an image using the container */ - ImageContainer* container = mDecoder->GetImageContainer(); - if (!container) { - // We don't have an ImageContainer. We probably belong to an