From ca5193d055259a2ef6d84dc26c204df879650b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 23 Dec 2007 06:22:32 +0000 Subject: [PATCH 01/29] Add new plugin rawparse that contains a base class for raw data parsers and the two elements audioparse and videopars... Original commit message from CVS: * configure.ac: * gst/rawparse/Makefile.am: * gst/rawparse/README: * gst/rawparse/gstaudioparse.c: (gst_audio_parse_format_get_type), (gst_audio_parse_endianness_get_type), (gst_audio_parse_base_init), (gst_audio_parse_class_init), (gst_audio_parse_init), (gst_audio_parse_set_property), (gst_audio_parse_get_property), (gst_audio_parse_update_frame_size), (gst_audio_parse_get_caps): * gst/rawparse/gstaudioparse.h: * gst/rawparse/gstrawparse.c: (gst_raw_parse_base_init), (gst_raw_parse_class_init), (gst_raw_parse_init), (gst_raw_parse_dispose), (gst_raw_parse_class_set_src_pad_template), (gst_raw_parse_class_set_multiple_frames_per_buffer), (gst_raw_parse_reset), (gst_raw_parse_chain), (gst_raw_parse_convert), (gst_raw_parse_sink_event), (gst_raw_parse_src_event), (gst_raw_parse_src_query_type), (gst_raw_parse_src_query), (gst_raw_parse_set_framesize), (gst_raw_parse_set_fps), (gst_raw_parse_get_fps), (gst_raw_parse_is_negotiated): * gst/rawparse/gstrawparse.h: * gst/rawparse/gstvideoparse.c: (gst_video_parse_format_get_type), (gst_video_parse_endianness_get_type), (gst_video_parse_base_init), (gst_video_parse_class_init), (gst_video_parse_init), (gst_video_parse_set_property), (gst_video_parse_get_property), (gst_video_parse_format_to_fourcc), (gst_video_parse_update_frame_size), (gst_video_parse_get_caps): * gst/rawparse/gstvideoparse.h: * gst/rawparse/plugin.c: (plugin_init): Add new plugin rawparse that contains a base class for raw data parsers and the two elements audioparse and videoparse that can be used to parse raw audio and video. These are inspired by the old videoparse element which the new rawparse plugin deprecates. --- gst/rawparse/Makefile.am | 8 ++++++++ gst/rawparse/plugin.c | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 gst/rawparse/Makefile.am create mode 100644 gst/rawparse/plugin.c diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am new file mode 100644 index 0000000000..ba235ea068 --- /dev/null +++ b/gst/rawparse/Makefile.am @@ -0,0 +1,8 @@ + +plugin_LTLIBRARIES = libgstrawparse.la + +libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin.c +libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) +libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + diff --git a/gst/rawparse/plugin.c b/gst/rawparse/plugin.c new file mode 100644 index 0000000000..250be6de7f --- /dev/null +++ b/gst/rawparse/plugin.c @@ -0,0 +1,26 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "gstaudioparse.h" +#include "gstvideoparse.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean ret; + + ret = gst_element_register (plugin, "videoparse", GST_RANK_NONE, + gst_video_parse_get_type ()); + ret &= gst_element_register (plugin, "audioparse", GST_RANK_NONE, + gst_audio_parse_get_type ()); + + return ret; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "rawparse", + "Parses byte streams into raw frames", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); From a9b19a8a004c22eff585b08842386238015725fc Mon Sep 17 00:00:00 2001 From: Christian Schaller Date: Wed, 23 Jan 2008 13:18:24 +0000 Subject: [PATCH 02/29] Add missing no_inst header files to Makefile.am so disting still works Original commit message from CVS: Add missing no_inst header files to Makefile.am so disting still works Update spec file with latest changes --- gst/rawparse/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index ba235ea068..c4d7f34546 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -6,3 +6,7 @@ libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +noinst_HEADERS = \ + gstaudioparse.h \ + gstrawparse.h \ + gstvideoparse.h From 4e30d665682bdfe53bdd9b991db0d6e8e770212f Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Tue, 4 Nov 2008 12:42:30 +0000 Subject: [PATCH 03/29] Don't install static libs for plugins. Fixes #550851 for -bad. Original commit message from CVS: * ext/alsaspdif/Makefile.am: * ext/amrwb/Makefile.am: * ext/apexsink/Makefile.am: * ext/arts/Makefile.am: * ext/artsd/Makefile.am: * ext/audiofile/Makefile.am: * ext/audioresample/Makefile.am: * ext/bz2/Makefile.am: * ext/cdaudio/Makefile.am: * ext/celt/Makefile.am: * ext/dc1394/Makefile.am: * ext/dirac/Makefile.am: * ext/directfb/Makefile.am: * ext/divx/Makefile.am: * ext/dts/Makefile.am: * ext/faac/Makefile.am: * ext/faad/Makefile.am: * ext/gsm/Makefile.am: * ext/hermes/Makefile.am: * ext/ivorbis/Makefile.am: * ext/jack/Makefile.am: * ext/jp2k/Makefile.am: * ext/ladspa/Makefile.am: * ext/lcs/Makefile.am: * ext/libfame/Makefile.am: * ext/libmms/Makefile.am: * ext/metadata/Makefile.am: * ext/mpeg2enc/Makefile.am: * ext/mplex/Makefile.am: * ext/musepack/Makefile.am: * ext/musicbrainz/Makefile.am: * ext/mythtv/Makefile.am: * ext/nas/Makefile.am: * ext/neon/Makefile.am: * ext/ofa/Makefile.am: * ext/polyp/Makefile.am: * ext/resindvd/Makefile.am: * ext/sdl/Makefile.am: * ext/shout/Makefile.am: * ext/snapshot/Makefile.am: * ext/sndfile/Makefile.am: * ext/soundtouch/Makefile.am: * ext/spc/Makefile.am: * ext/swfdec/Makefile.am: * ext/tarkin/Makefile.am: * ext/theora/Makefile.am: * ext/timidity/Makefile.am: * ext/twolame/Makefile.am: * ext/x264/Makefile.am: * ext/xine/Makefile.am: * ext/xvid/Makefile.am: * gst-libs/gst/app/Makefile.am: * gst-libs/gst/dshow/Makefile.am: * gst/aiffparse/Makefile.am: * gst/app/Makefile.am: * gst/audiobuffer/Makefile.am: * gst/bayer/Makefile.am: * gst/cdxaparse/Makefile.am: * gst/chart/Makefile.am: * gst/colorspace/Makefile.am: * gst/dccp/Makefile.am: * gst/deinterlace/Makefile.am: * gst/deinterlace2/Makefile.am: * gst/dvdspu/Makefile.am: * gst/festival/Makefile.am: * gst/filter/Makefile.am: * gst/flacparse/Makefile.am: * gst/flv/Makefile.am: * gst/games/Makefile.am: * gst/h264parse/Makefile.am: * gst/librfb/Makefile.am: * gst/mixmatrix/Makefile.am: * gst/modplug/Makefile.am: * gst/mpeg1sys/Makefile.am: * gst/mpeg4videoparse/Makefile.am: * gst/mpegdemux/Makefile.am: * gst/mpegtsmux/Makefile.am: * gst/mpegvideoparse/Makefile.am: * gst/mve/Makefile.am: * gst/nsf/Makefile.am: * gst/nuvdemux/Makefile.am: * gst/overlay/Makefile.am: * gst/passthrough/Makefile.am: * gst/pcapparse/Makefile.am: * gst/playondemand/Makefile.am: * gst/rawparse/Makefile.am: * gst/real/Makefile.am: * gst/rtjpeg/Makefile.am: * gst/rtpmanager/Makefile.am: * gst/scaletempo/Makefile.am: * gst/sdp/Makefile.am: * gst/selector/Makefile.am: * gst/smooth/Makefile.am: * gst/smoothwave/Makefile.am: * gst/speed/Makefile.am: * gst/speexresample/Makefile.am: * gst/stereo/Makefile.am: * gst/subenc/Makefile.am: * gst/tta/Makefile.am: * gst/vbidec/Makefile.am: * gst/videodrop/Makefile.am: * gst/videosignal/Makefile.am: * gst/virtualdub/Makefile.am: * gst/vmnc/Makefile.am: * gst/y4m/Makefile.am: * sys/acmenc/Makefile.am: * sys/cdrom/Makefile.am: * sys/dshowdecwrapper/Makefile.am: * sys/dshowsrcwrapper/Makefile.am: * sys/dvb/Makefile.am: * sys/dxr3/Makefile.am: * sys/fbdev/Makefile.am: * sys/oss4/Makefile.am: * sys/qcam/Makefile.am: * sys/qtwrapper/Makefile.am: * sys/vcd/Makefile.am: * sys/wininet/Makefile.am: * win32/common/config.h: Don't install static libs for plugins. Fixes #550851 for -bad. --- gst/rawparse/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index c4d7f34546..88e4e8c03f 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -5,6 +5,7 @@ libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = \ gstaudioparse.h \ From 4b122ec93d8e21110ad83a8b91ee494ccd104f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 07:59:16 +0200 Subject: [PATCH 04/29] videoparse: Use libgstvideo for everything instead of our own calculations Also make RGB usage easier by providing xRGB, RGBx, etc. formats instead of requiring to set red_mask and friends. --- gst/rawparse/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 88e4e8c03f..b56db45b2b 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -3,7 +3,7 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin.c libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static From 3d93c654151ac762a5bfc565588b1c0f4e767400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Tue, 25 Aug 2009 23:44:50 +0400 Subject: [PATCH 05/29] resindvd, rawparse: fix LDFLAGS for gst-plugins-base libs Fixes #593063. --- gst/rawparse/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index b56db45b2b..57543533a0 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -3,7 +3,7 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin.c libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ +libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static From 1fe4e2ad2aad70bea005261ca19c276596ae027a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 7 Oct 2010 10:34:48 +0200 Subject: [PATCH 06/29] audioparse: Add support for setting the channel-positions --- gst/rawparse/Makefile.am | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 57543533a0..491fdd6351 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -1,9 +1,20 @@ plugin_LTLIBRARIES = libgstrawparse.la -libgstrawparse_la_SOURCES = gstrawparse.c gstaudioparse.c gstvideoparse.c plugin.c -libgstrawparse_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstrawparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ +libgstrawparse_la_SOURCES = \ + gstrawparse.c \ + gstaudioparse.c \ + gstvideoparse.c \ + plugin.c +libgstrawparse_la_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) +libgstrawparse_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-@GST_MAJORMINOR@ -lgstaudio-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) \ + $(GST_LIBS) libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static From 81b71b65ee028ffcfd98ccfec52602e463c91c0a Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Mon, 11 Apr 2011 00:36:35 -0400 Subject: [PATCH 07/29] android: make it ready for androgenizer Remove the android/ top dir Fixe the Makefile.am to be androgenized To build gstreamer for android we are now using androgenizer which generates the needed Android.mk files. Androgenizer can be found here: http://git.collabora.co.uk/?p=user/derek/androgenizer.git --- gst/rawparse/Makefile.am | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 491fdd6351..ac0aadc7d4 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -22,3 +22,17 @@ noinst_HEADERS = \ gstaudioparse.h \ gstrawparse.h \ gstvideoparse.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT libgstrawparse -:SHARED libgstrawparse \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstrawparse_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstrawparse_la_CFLAGS) \ + -:LDFLAGS $(libgstrawparse_la_LDFLAGS) \ + $(libgstrawparse_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ From 94f7dcc9c9851598cb3aaa9482543f64ca41d6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 4 Apr 2012 14:41:22 +0200 Subject: [PATCH 08/29] gst: Update versioning --- gst/rawparse/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index ac0aadc7d4..254f3cf521 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -12,7 +12,7 @@ libgstrawparse_la_CFLAGS = \ $(GST_CFLAGS) libgstrawparse_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ - -lgstvideo-@GST_MAJORMINOR@ -lgstaudio-@GST_MAJORMINOR@ \ + -lgstvideo-@GST_API_VERSION@ -lgstaudio-@GST_API_VERSION@ \ $(GST_BASE_LIBS) \ $(GST_LIBS) libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) From 28e1b08db5c8e240920037f449b73053f6adf3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 5 Apr 2012 18:02:56 +0200 Subject: [PATCH 09/29] gst: Update for GST_PLUGIN_DEFINE() API changes --- gst/rawparse/plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/plugin.c b/gst/rawparse/plugin.c index 250be6de7f..dc860e18f8 100644 --- a/gst/rawparse/plugin.c +++ b/gst/rawparse/plugin.c @@ -21,6 +21,6 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "rawparse", + rawparse, "Parses byte streams into raw frames", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); From 228e65b33cc0874876123911d7767fef3ddc7ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Oct 2012 12:16:39 +0200 Subject: [PATCH 10/29] gst: Add better support for static plugins --- gst/rawparse/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 254f3cf521..15d28040ff 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -16,7 +16,7 @@ libgstrawparse_la_LIBADD = \ $(GST_BASE_LIBS) \ $(GST_LIBS) libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstrawparse_la_LIBTOOLFLAGS = --tag=disable-static +libgstrawparse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = \ gstaudioparse.h \ From 4984828fcfeaf88da91b7fa767b0a8a7aa13a3ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 26 Apr 2015 18:04:16 +0100 Subject: [PATCH 11/29] Remove obsolete Android build cruft This is not needed any longer. --- gst/rawparse/Makefile.am | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 15d28040ff..03eeb48a59 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -22,17 +22,3 @@ noinst_HEADERS = \ gstaudioparse.h \ gstrawparse.h \ gstvideoparse.h - -Android.mk: Makefile.am $(BUILT_SOURCES) - androgenizer \ - -:PROJECT libgstrawparse -:SHARED libgstrawparse \ - -:TAGS eng debug \ - -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ - -:SOURCES $(libgstrawparse_la_SOURCES) \ - -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstrawparse_la_CFLAGS) \ - -:LDFLAGS $(libgstrawparse_la_LDFLAGS) \ - $(libgstrawparse_la_LIBADD) \ - -ldl \ - -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ - LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ - > $@ From f6e6264711af0a5fe7e6565c8874f800e9a2441a Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Fri, 4 Mar 2016 22:10:47 +0100 Subject: [PATCH 12/29] rawparse: Add unaligned raw audio parsing to audioparse and add new element This helps in cases where raw audio data is being delivered, but the buffers do not come in sample aligned sizes. The new unalignedaudioparse bin can be autoplugged and configures an internal audioparse element to align the data. audioparse itself gets support for audio/x-unaligned-raw input caps; the output caps then contain the same information, except that the name is changed to audio/x-raw (since audioparse aligns the data). This ensures that souphttpsrc ! audioparse still works. https://bugzilla.gnome.org/show_bug.cgi?id=689460 --- gst/rawparse/Makefile.am | 3 + gst/rawparse/gstunalignedaudioparse.c | 125 ++++++++++++++++++++++++++ gst/rawparse/gstunalignedaudioparse.h | 49 ++++++++++ gst/rawparse/plugin.c | 3 + gst/rawparse/unalignedaudio.h | 35 ++++++++ 5 files changed, 215 insertions(+) create mode 100644 gst/rawparse/gstunalignedaudioparse.c create mode 100644 gst/rawparse/gstunalignedaudioparse.h create mode 100644 gst/rawparse/unalignedaudio.h diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 03eeb48a59..3bd8438675 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -3,6 +3,7 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = \ gstrawparse.c \ + gstunalignedaudioparse.c \ gstaudioparse.c \ gstvideoparse.c \ plugin.c @@ -19,6 +20,8 @@ libgstrawparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstrawparse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = \ + unalignedaudio.h \ + gstunalignedaudioparse.h \ gstaudioparse.h \ gstrawparse.h \ gstvideoparse.h diff --git a/gst/rawparse/gstunalignedaudioparse.c b/gst/rawparse/gstunalignedaudioparse.c new file mode 100644 index 0000000000..62a83f3638 --- /dev/null +++ b/gst/rawparse/gstunalignedaudioparse.c @@ -0,0 +1,125 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * gstunalignedaudioparse.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include "gstunalignedaudioparse.h" +#include "unalignedaudio.h" + + +GST_DEBUG_CATEGORY (unaligned_audio_parse_debug); +#define GST_CAT_DEFAULT unaligned_audio_parse_debug + + +struct _GstUnalignedAudioParse +{ + GstBin parent; + GstElement *inner_parser; +}; + + +struct _GstUnalignedAudioParseClass +{ + GstBinClass parent_class; +}; + + +static GstStaticPadTemplate static_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UNALIGNED_RAW_AUDIO_CAPS) + ); + + +static GstStaticPadTemplate static_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) + ", layout = (string) { interleaved, non-interleaved }") + ); + + + + +G_DEFINE_TYPE (GstUnalignedAudioParse, gst_unaligned_audio_parse, GST_TYPE_BIN); + + +static void +gst_unaligned_audio_parse_class_init (GstUnalignedAudioParseClass * klass) +{ + GstElementClass *element_class; + + GST_DEBUG_CATEGORY_INIT (unaligned_audio_parse_debug, "unalignedaudioparse", + 0, "Unaligned raw audio parser"); + + element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_src_template)); + + gst_element_class_set_static_metadata (element_class, + "unalignedaudioparse", + "Codec/Parser/Bin/Audio", + "Parse unaligned raw audio data", + "Carlos Rafael Giani "); +} + + +static void +gst_unaligned_audio_parse_init (GstUnalignedAudioParse * unaligned_audio_parse) +{ + GstPad *inner_pad; + GstPad *ghostpad; + + unaligned_audio_parse->inner_parser = + gst_element_factory_make ("audioparse", "inner_parser"); + g_assert (unaligned_audio_parse->inner_parser != NULL); + + g_object_set (G_OBJECT (unaligned_audio_parse->inner_parser), + "use-sink-caps", TRUE, NULL); + + gst_bin_add (GST_BIN (unaligned_audio_parse), + unaligned_audio_parse->inner_parser); + + inner_pad = + gst_element_get_static_pad (unaligned_audio_parse->inner_parser, "sink"); + ghostpad = + gst_ghost_pad_new_from_template ("sink", inner_pad, + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS + (unaligned_audio_parse), "sink")); + gst_element_add_pad (GST_ELEMENT (unaligned_audio_parse), ghostpad); + gst_object_unref (GST_OBJECT (inner_pad)); + + inner_pad = gst_element_get_static_pad (unaligned_audio_parse->inner_parser, + "src"); + ghostpad = + gst_ghost_pad_new_from_template ("src", inner_pad, + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS + (unaligned_audio_parse), "src")); + gst_element_add_pad (GST_ELEMENT (unaligned_audio_parse), ghostpad); + gst_object_unref (GST_OBJECT (inner_pad)); +} diff --git a/gst/rawparse/gstunalignedaudioparse.h b/gst/rawparse/gstunalignedaudioparse.h new file mode 100644 index 0000000000..a07bee7ebe --- /dev/null +++ b/gst/rawparse/gstunalignedaudioparse.h @@ -0,0 +1,49 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * gstunalignedaudioparse.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_UNALIGNED_AUDIO_PARSE_H___ +#define __GST_UNALIGNED_AUDIO_PARSE_H___ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_UNALIGNED_AUDIO_PARSE \ + (gst_unaligned_audio_parse_get_type()) +#define GST_UNALIGNED_AUDIO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_UNALIGNED_AUDIO_PARSE, GstUnalignedAudioParse)) +#define GST_UNALIGNED_AUDIO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_UNALIGNED_AUDIO_PARSE, GstUnalignedAudioParseClass)) +#define GST_UNALIGNED_AUDIO_PARSE_CAST(obj) \ + ((GstUnalignedAudioParse *)(obj)) +#define GST_IS_UNALIGNED_AUDIO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_UNALIGNED_AUDIO_PARSE)) +#define GST_IS_UNALIGNED_AUDIO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_UNALIGNED_AUDIO_PARSE)) + +typedef struct _GstUnalignedAudioParse GstUnalignedAudioParse; +typedef struct _GstUnalignedAudioParseClass GstUnalignedAudioParseClass; + +GType gst_unaligned_audio_parse_get_type (void); + +G_END_DECLS + +#endif /* __GST_UNALIGNED_AUDIO_PARSE_H___ */ diff --git a/gst/rawparse/plugin.c b/gst/rawparse/plugin.c index dc860e18f8..bb21f77b15 100644 --- a/gst/rawparse/plugin.c +++ b/gst/rawparse/plugin.c @@ -3,6 +3,7 @@ #endif #include +#include "gstunalignedaudioparse.h" #include "gstaudioparse.h" #include "gstvideoparse.h" @@ -15,6 +16,8 @@ plugin_init (GstPlugin * plugin) gst_video_parse_get_type ()); ret &= gst_element_register (plugin, "audioparse", GST_RANK_NONE, gst_audio_parse_get_type ()); + ret &= gst_element_register (plugin, "unalignedaudioparse", GST_RANK_MARGINAL, + gst_unaligned_audio_parse_get_type ()); return ret; } diff --git a/gst/rawparse/unalignedaudio.h b/gst/rawparse/unalignedaudio.h new file mode 100644 index 0000000000..15d499525f --- /dev/null +++ b/gst/rawparse/unalignedaudio.h @@ -0,0 +1,35 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * unalignedaudio.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_UNALIGNED_AUDIO_H__ +#define __GST_UNALIGNED_AUDIO_H__ + +#include +#include + +#define GST_UNALIGNED_RAW_AUDIO_CAPS \ + "audio/x-unaligned-raw" \ + ", format = (string) " GST_AUDIO_FORMATS_ALL \ + ", rate = (int) [ 1, MAX ]" \ + ", channels = (int) [ 1, MAX ]" \ + ", layout = (string) { interleaved, non-interleaved }" + +#endif /* __GST_UNALIGNED_AUDIO_H__ */ From d5085251193cc25ed9528faaf5e1085708f53858 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Mon, 25 Jul 2016 13:45:40 +0200 Subject: [PATCH 13/29] rawparse: Add new raw audio and video parser elements The new rawaudioparse and rawvideoparse elements are based on GstBaseParse and completely replace audioparse and videoparse https://bugzilla.gnome.org/show_bug.cgi?id=767011 --- gst/rawparse/Makefile.am | 11 +- gst/rawparse/gstrawaudioparse.c | 1088 ++++++++++++++++++++++++ gst/rawparse/gstrawaudioparse.h | 131 +++ gst/rawparse/gstrawbaseparse.c | 748 ++++++++++++++++ gst/rawparse/gstrawbaseparse.h | 207 +++++ gst/rawparse/gstrawvideoparse.c | 1131 +++++++++++++++++++++++++ gst/rawparse/gstrawvideoparse.h | 116 +++ gst/rawparse/gstunalignedaudioparse.c | 2 +- gst/rawparse/gstunalignedvideoparse.c | 124 +++ gst/rawparse/gstunalignedvideoparse.h | 49 ++ gst/rawparse/plugin.c | 9 + gst/rawparse/unalignedvideo.h | 35 + tests/check/elements/rawaudioparse.c | 339 ++++++++ tests/check/elements/rawvideoparse.c | 459 ++++++++++ 14 files changed, 4447 insertions(+), 2 deletions(-) create mode 100644 gst/rawparse/gstrawaudioparse.c create mode 100644 gst/rawparse/gstrawaudioparse.h create mode 100644 gst/rawparse/gstrawbaseparse.c create mode 100644 gst/rawparse/gstrawbaseparse.h create mode 100644 gst/rawparse/gstrawvideoparse.c create mode 100644 gst/rawparse/gstrawvideoparse.h create mode 100644 gst/rawparse/gstunalignedvideoparse.c create mode 100644 gst/rawparse/gstunalignedvideoparse.h create mode 100644 gst/rawparse/unalignedvideo.h create mode 100644 tests/check/elements/rawaudioparse.c create mode 100644 tests/check/elements/rawvideoparse.c diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 3bd8438675..f3ad3c4142 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -4,8 +4,12 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = \ gstrawparse.c \ gstunalignedaudioparse.c \ + gstunalignedvideoparse.c \ gstaudioparse.c \ gstvideoparse.c \ + gstrawbaseparse.c \ + gstrawaudioparse.c \ + gstrawvideoparse.c \ plugin.c libgstrawparse_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ @@ -21,7 +25,12 @@ libgstrawparse_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) noinst_HEADERS = \ unalignedaudio.h \ + unalignedvideo.h \ gstunalignedaudioparse.h \ + gstunalignedvideoparse.h \ gstaudioparse.h \ gstrawparse.h \ - gstvideoparse.h + gstvideoparse.h \ + gstrawbaseparse.h \ + gstrawaudioparse.h \ + gstrawvideoparse.h diff --git a/gst/rawparse/gstrawaudioparse.c b/gst/rawparse/gstrawaudioparse.c new file mode 100644 index 0000000000..d955f0e59c --- /dev/null +++ b/gst/rawparse/gstrawaudioparse.c @@ -0,0 +1,1088 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rawaudioparse + * + * This element parses incoming data as raw audio samples and timestamps it. + * It also handles seek queries in said raw audio data, and ensures that output + * buffers contain an integer number of samples, even if the input buffers don't. + * For example, with sample format S16LE and 2 channels, an input buffer of 411 + * bytes contains 102.75 samples. rawaudioparse will then output 102 samples + * (= 408 bytes) and keep the remaining 3 bytes. These will then be prepended to + * the next input data. + * + * The element implements the properties and sink caps configuration as specified + * in the #GstRawBaseParse documentation. The properties configuration can be + * modified by using the sample-rate, num-channels, channel-positions, format, + * and pcm-format properties. + * + * Currently, this parser supports raw data in a-law, mu-law, or linear PCM format. + * + * To facilitate operation with the unalignedaudioparse element, rawaudioparse + * supports the "audio/x-unaligned-raw" media type. This is treated identically to + * "audio/x-raw", except that it is used by source elements which do not guarantee + * that the buffers they push out are timestamped and contain an integer amount of + * samples (see the 411 bytes example above). By using a different media type, it + * is guaranteed that unalignedaudioparse is autoplugged, making sure that the + * autoplugged chain does not push unparsed content downstream. The source caps' + * media type with linear PCM data is always "audio/x-raw", even if the sink caps + * use "audio/x-unaligned-raw". + * + * The channel-positions property can be used to set explicit position information + * for each channel. If the array that is passed to this property does not match + * the number of channels indicated by num-channels, then said number of channels + * is updated to the array length. If channel-positions is NULL, then the default + * GStreamer positioning is used. This property is also useful for swapping left + * and right in a stereo signal for example. + * + * + * Example pipelines + * |[ + * gst-launch-1.0 souphttpsrc http://my-dlna-server/track.l16 \ + * rawaudioparse ! audioconvert ! audioresample ! autoaudiosink + * ]| Receive L16 data from a DLNA server, parse and timestamp it with + * rawaudioparse, and play it. use-sink-caps is set to true since souphttpsrc + * will set its source pad's caps to audio/x-unaligned-raw for the L16 stream. + * |[ + * gst-launch-1.0 filesrc location=audio.raw ! rawaudioparse use-sink-caps=false \ + * format=pcm pcm-format=s16le sample-rate=48000 num-channels=2 \ + * audioconvert ! audioresample ! autoaudiosink + * ]| Read raw data from a local file and parse it as PCM data with 48000 Hz sample + * rate, signed 16 bit integer samples, and 2 channels. use-sink-caps is set to + * false to ensure the property information is used and the parser does not expect + * audio/x-raw or audio/x-unaligned-raw caps. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative + * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include "gstrawaudioparse.h" +#include "unalignedaudio.h" + + +GST_DEBUG_CATEGORY_STATIC (raw_audio_parse_debug); +#define GST_CAT_DEFAULT raw_audio_parse_debug + + +enum +{ + PROP_0, + PROP_FORMAT, + PROP_PCM_FORMAT, + PROP_SAMPLE_RATE, + PROP_NUM_CHANNELS, + PROP_INTERLEAVED, + PROP_CHANNEL_POSITIONS +}; + + +#define DEFAULT_FORMAT GST_RAW_AUDIO_PARSE_FORMAT_PCM +#define DEFAULT_PCM_FORMAT GST_AUDIO_FORMAT_S16 +#define DEFAULT_SAMPLE_RATE 44100 +#define DEFAULT_NUM_CHANNELS 2 +#define DEFAULT_INTERLEAVED TRUE + + +#define GST_RAW_AUDIO_PARSE_CAPS \ + GST_AUDIO_CAPS_MAKE(GST_AUDIO_FORMATS_ALL) \ + ", layout = (string) { interleaved, non-interleaved }; " \ + "audio/x-alaw, rate = (int) [ 1, MAX ], channels = (int) [ 1, MAX ]; " \ + "audio/x-mulaw, rate = (int) [ 1, MAX ], channels = (int) [ 1, MAX ]; " + + +static GstStaticPadTemplate static_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UNALIGNED_RAW_AUDIO_CAPS "; " GST_RAW_AUDIO_PARSE_CAPS) + ); + + +static GstStaticPadTemplate static_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_RAW_AUDIO_PARSE_CAPS) + ); + + +#define gst_raw_audio_parse_parent_class parent_class +G_DEFINE_TYPE (GstRawAudioParse, gst_raw_audio_parse, GST_TYPE_RAW_BASE_PARSE); + + +static void gst_raw_audio_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec); +static void gst_raw_audio_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_raw_audio_parse_stop (GstBaseParse * parse); + +static gboolean gst_raw_audio_parse_set_current_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static GstRawBaseParseConfig +gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse); +static gboolean gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps * caps); +static gboolean gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps ** caps); +static gsize gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_audio_parse_is_config_ready (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, + gsize num_valid_in_bytes, GstBuffer ** processed_data); +static gboolean gst_raw_audio_parse_is_unit_format_supported (GstRawBaseParse * + raw_base_parse, GstFormat format); +static void gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * + raw_base_parse, GstFormat format, GstRawBaseParseConfig config, + gsize * units_per_sec_n, gsize * units_per_sec_d); + +static gboolean gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * + raw_audio_parse); +static GstRawAudioParseConfig + * gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, + GstRawBaseParseConfig config); + +static void gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config); +static gboolean gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig + * config, guint num_channels, guint64 channel_mask, gboolean set_positions); +static gboolean +gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * + config); +static void gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * + config); +static gboolean gst_raw_audio_parse_caps_to_config (GstRawAudioParse * + raw_audio_parse, GstCaps * caps, GstRawAudioParseConfig * config); +static gboolean gst_raw_audio_parse_config_to_caps (GstRawAudioParse * + raw_audio_parse, GstCaps ** caps, GstRawAudioParseConfig * config); + + + +static void +gst_raw_audio_parse_class_init (GstRawAudioParseClass * klass) +{ + GObjectClass *object_class; + GstElementClass *element_class; + GstBaseParseClass *baseparse_class; + GstRawBaseParseClass *rawbaseparse_class; + + GST_DEBUG_CATEGORY_INIT (raw_audio_parse_debug, "rawaudioparse", 0, + "rawaudioparse element"); + + object_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + baseparse_class = GST_BASE_PARSE_CLASS (klass); + rawbaseparse_class = GST_RAW_BASE_PARSE_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_src_template)); + + object_class->set_property = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_property); + object_class->get_property = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_property); + + baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_raw_audio_parse_stop); + + rawbaseparse_class->set_current_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_current_config); + rawbaseparse_class->get_current_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_current_config); + rawbaseparse_class->set_config_from_caps = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_set_config_from_caps); + rawbaseparse_class->get_caps_from_config = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_caps_from_config); + rawbaseparse_class->get_config_frame_size = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_config_frame_size); + rawbaseparse_class->is_config_ready = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_is_config_ready); + rawbaseparse_class->process = GST_DEBUG_FUNCPTR (gst_raw_audio_parse_process); + rawbaseparse_class->is_unit_format_supported = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_is_unit_format_supported); + rawbaseparse_class->get_units_per_second = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_units_per_second); + + g_object_class_install_property (object_class, + PROP_FORMAT, + g_param_spec_enum ("format", + "Format", + "Format of the raw audio stream", + gst_raw_audio_parse_format_get_type (), + GST_RAW_AUDIO_PARSE_FORMAT_PCM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_PCM_FORMAT, + g_param_spec_enum ("pcm-format", + "PCM format", + "Format of audio samples in PCM stream (ignored if format property is not set to pcm)", + GST_TYPE_AUDIO_FORMAT, + GST_RAW_AUDIO_PARSE_FORMAT_PCM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_SAMPLE_RATE, + g_param_spec_int ("sample-rate", + "Sample rate", + "Rate of audio samples in raw stream", + 1, INT_MAX, + DEFAULT_SAMPLE_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_NUM_CHANNELS, + g_param_spec_int ("num-channels", + "Number of channels", + "Number of channels in raw stream", + 1, INT_MAX, + DEFAULT_NUM_CHANNELS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_INTERLEAVED, + g_param_spec_boolean ("interleaved", + "Interleaved layout", + "True if audio has interleaved layout", + DEFAULT_INTERLEAVED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_CHANNEL_POSITIONS, + g_param_spec_value_array ("channel-positions", + "Channel positions", + "Channel positions used on the output", + g_param_spec_enum ("channel-position", + "Channel position", + "Channel position of the n-th input", + GST_TYPE_AUDIO_CHANNEL_POSITION, + GST_AUDIO_CHANNEL_POSITION_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gst_element_class_set_static_metadata (element_class, + "rawaudioparse", + "Codec/Parser/Audio", + "Converts unformatted data streams into timestamped raw audio frames", + "Carlos Rafael Giani "); +} + + +static void +gst_raw_audio_parse_init (GstRawAudioParse * raw_audio_parse) +{ + /* Setup configs and select which one shall be the current one from the start. */ + gst_raw_audio_parse_init_config (&(raw_audio_parse->properties_config)); + gst_raw_audio_parse_init_config (&(raw_audio_parse->sink_caps_config)); + /* As required by GstRawBaseParse, ensure that the current configuration + * is initially set to be the properties config */ + raw_audio_parse->current_config = &(raw_audio_parse->properties_config); + + /* Properties config must be valid from the start, so set its ready value + * to TRUE, and make sure its bpf value is valid. */ + raw_audio_parse->properties_config.ready = TRUE; + gst_raw_audio_parse_update_config_bpf (&(raw_audio_parse->properties_config)); +} + + +static void +gst_raw_audio_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec) +{ + GstBaseParse *base_parse = GST_BASE_PARSE (object); + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (object); + + /* All properties are handled similarly: + * - if the new value is the same as the current value, nothing is done + * - the parser lock is held while the new value is set + * - if the properties config is the current config, the source caps are + * invalidated to ensure that the code in handle_frame pushes a new CAPS + * event out + * - properties that affect the bpf value call the function to update + * the bpf and also call gst_base_parse_set_min_frame_size() to ensure + * that the minimum frame size can hold 1 frame (= one sample for each + * channel) + */ + + switch (prop_id) { + case PROP_FORMAT: + { + GstRawAudioParseFormat new_format = g_value_get_enum (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_format != raw_audio_parse->properties_config.format) { + raw_audio_parse->properties_config.format = new_format; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_PCM_FORMAT: + { + GstAudioFormat new_pcm_format = g_value_get_enum (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_pcm_format != raw_audio_parse->properties_config.pcm_format) { + raw_audio_parse->properties_config.pcm_format = new_pcm_format; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_SAMPLE_RATE: + { + guint new_sample_rate = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_sample_rate != raw_audio_parse->properties_config.sample_rate) { + raw_audio_parse->properties_config.sample_rate = new_sample_rate; + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_NUM_CHANNELS: + { + guint new_num_channels = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_num_channels != raw_audio_parse->properties_config.num_channels) { + gst_raw_audio_parse_set_config_channels (& + (raw_audio_parse->properties_config), new_num_channels, 0, TRUE); + + raw_audio_parse->properties_config.num_channels = new_num_channels; + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_INTERLEAVED: + { + gboolean new_interleaved = g_value_get_boolean (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_interleaved != raw_audio_parse->properties_config.interleaved) { + raw_audio_parse->properties_config.interleaved = new_interleaved; + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_CHANNEL_POSITIONS: + { + GValueArray *valarray = g_value_get_boxed (value); + GstRawAudioParseConfig *config = &(raw_audio_parse->properties_config); + + /* Sanity check - reject empty arrays */ + if ((valarray != NULL) && (valarray->n_values == 0)) { + GST_ELEMENT_ERROR (raw_audio_parse, LIBRARY, SETTINGS, + ("channel position property holds an empty array"), (NULL)); + break; + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if ((valarray == NULL) && (config->num_channels > 0)) { + /* NULL value given, and number of channels is nonzero. + * Use the default GStreamer positioning. Call + * set_config_channels with the set_positions parameter + * set to TRUE to ensure the position values are filled. */ + gst_raw_audio_parse_set_config_channels (& + (raw_audio_parse->properties_config), config->num_channels, 0, + TRUE); + } else { + /* Non-NULL value given. Make sure the channel_positions + * array in the properties config has enough room, and that + * the num_channels value equals the array length. Then copy + * the values from the valarray to channel_positions, and + * produce a copy of that array in case its channel positions + * are not in a valid GStreamer order (to be able to apply + * channel reordering later). + */ + + guint i; + + if (valarray->n_values != config->num_channels) { + /* Call with set_positions == FALSE to ensure that + * the array is properly allocated but not filled + * (it is filled below) */ + gst_raw_audio_parse_set_config_channels (config, valarray->n_values, + 0, FALSE); + } + + for (i = 0; i < config->num_channels; ++i) { + GValue *val = g_value_array_get_nth (valarray, i); + config->channel_positions[i] = g_value_get_enum (val); + } + + gst_raw_audio_parse_update_channel_reordering_flag (config); + } + + gst_raw_audio_parse_update_config_bpf (& + (raw_audio_parse->properties_config)); + + if (!gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + raw_audio_parse->properties_config.bpf); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_raw_audio_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (object); + + switch (prop_id) { + case PROP_FORMAT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_enum (value, raw_audio_parse->properties_config.format); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_PCM_FORMAT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_enum (value, raw_audio_parse->properties_config.pcm_format); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_SAMPLE_RATE: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, raw_audio_parse->properties_config.sample_rate); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_NUM_CHANNELS: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, raw_audio_parse->properties_config.num_channels); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_INTERLEAVED: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_boolean (value, + raw_audio_parse->properties_config.interleaved); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_CHANNEL_POSITIONS: + { + GstRawAudioParseConfig *config; + GValueArray *valarray; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + valarray = NULL; + config = &(raw_audio_parse->properties_config); + + /* Copy channel positions into the valuearray */ + if (config->num_channels > 0) { + guint i; + GValue val = G_VALUE_INIT; + g_assert (config->channel_positions); + + g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); + valarray = g_value_array_new (config->num_channels); + + for (i = 0; i < config->num_channels; ++i) { + g_value_set_enum (&val, config->channel_positions[i]); + g_value_array_insert (valarray, i, &val); + } + + g_value_unset (&val); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + /* Pass on ownership to the value array, + * since we don't need it anymore */ + g_value_take_boxed (value, valarray); + + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_raw_audio_parse_stop (GstBaseParse * parse) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (parse); + + /* Sink caps config is not ready until caps come in. + * We are stopping processing, the element is being reset, + * so the config has to be un-readied. + * (Since the properties config is not depending on caps, + * its ready status is always TRUE.) */ + raw_audio_parse->sink_caps_config.ready = FALSE; + + return GST_BASE_PARSE_CLASS (parent_class)->stop (parse); +} + + +static gboolean +gst_raw_audio_parse_set_current_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + raw_audio_parse->current_config = &(raw_audio_parse->properties_config); + break; + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + raw_audio_parse->current_config = &(raw_audio_parse->sink_caps_config); + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + + +static GstRawBaseParseConfig +gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_is_using_sink_caps (raw_audio_parse) ? + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; +} + + +static gboolean +gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps * caps) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_caps_to_config (raw_audio_parse, caps, + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); +} + + +static gboolean +gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps ** caps) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_config_to_caps (raw_audio_parse, caps, + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); +} + + +static gsize +gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->bpf; +} + + +static gboolean +gst_raw_audio_parse_is_config_ready (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->ready; +} + + +static gboolean +gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, + gsize num_valid_in_bytes, GstBuffer ** processed_data) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + GstRawAudioParseConfig *config_ptr = + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); + + if ((config_ptr->format == GST_RAW_AUDIO_PARSE_FORMAT_PCM) + && config_ptr->needs_channel_reordering) { + /* Need to reorder samples, since they are in an invalid + * channel order. */ + + GstBuffer *outbuf; + + GST_LOG_OBJECT (raw_audio_parse, + "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT + " bytes from the input buffer with reordering", num_valid_in_bytes, + total_num_in_bytes); + + outbuf = + gst_buffer_copy_region (in_data, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY, 0, num_valid_in_bytes); + + gst_audio_buffer_reorder_channels (outbuf, + config_ptr->pcm_format, + config_ptr->num_channels, + config_ptr->channel_positions, config_ptr->reordered_channel_positions); + + *processed_data = outbuf; + } else { + /* Nothing needs to be done with the sample data. + * Instruct the baseparse class to just take out_size bytes + * from the input buffer */ + + GST_LOG_OBJECT (raw_audio_parse, + "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT + " bytes from the input buffer without reordering", num_valid_in_bytes, + total_num_in_bytes); + + *processed_data = NULL; + } + + return TRUE; +} + + +static gboolean +gst_raw_audio_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * + raw_base_parse, GstFormat format) +{ + switch (format) { + case GST_FORMAT_BYTES: + case GST_FORMAT_DEFAULT: + return TRUE; + default: + return FALSE; + } +} + + +static void +gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, + GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, + gsize * units_per_sec_d) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + GstRawAudioParseConfig *config_ptr = + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); + + switch (format) { + case GST_FORMAT_BYTES: + *units_per_sec_n = config_ptr->sample_rate * config_ptr->bpf; + *units_per_sec_d = 1; + break; + + case GST_FORMAT_DEFAULT: + *units_per_sec_n = config_ptr->sample_rate; + *units_per_sec_d = 1; + break; + + default: + g_assert_not_reached (); + } +} + + +static gboolean +gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * raw_audio_parse) +{ + return raw_audio_parse->current_config == + &(raw_audio_parse->sink_caps_config); +} + + +static GstRawAudioParseConfig * +gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, + GstRawBaseParseConfig config) +{ + g_assert (raw_audio_parse->current_config != NULL); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + return &(raw_audio_parse->properties_config); + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + return &(raw_audio_parse->sink_caps_config); + + default: + g_assert (raw_audio_parse->current_config != NULL); + return raw_audio_parse->current_config; + } +} + + +static void +gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config) +{ + config->ready = FALSE; + config->format = DEFAULT_FORMAT; + config->pcm_format = DEFAULT_PCM_FORMAT; + config->bpf = 0; + config->sample_rate = DEFAULT_SAMPLE_RATE; + config->num_channels = DEFAULT_NUM_CHANNELS; + config->interleaved = DEFAULT_INTERLEAVED; + config->needs_channel_reordering = FALSE; + + gst_raw_audio_parse_set_config_channels (config, config->num_channels, 0, + TRUE); +} + + +static gboolean +gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig * config, + guint num_channels, guint64 channel_mask, gboolean set_positions) +{ + g_assert (num_channels > 0); + + config->num_channels = num_channels; + /* Setting this to FALSE, since initially, after setting the channels, + * the default GStreamer channel ordering is used. */ + config->needs_channel_reordering = FALSE; + + /* Set the channel positions based on the given channel mask if set_positions + * is set to TRUE. A channel mask of 0 signifies that a fallback mask should be + * used for the given number of channels. */ + if (set_positions) { + if (channel_mask == 0) + channel_mask = gst_audio_channel_get_fallback_mask (config->num_channels); + + return gst_audio_channel_positions_from_mask (config->num_channels, + channel_mask, config->channel_positions); + } else { + return TRUE; + } +} + + +static gboolean +gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * + config) +{ + g_assert (config->num_channels > 0); + + /* If the channel_positions array contains channel positions which are in an + * order that conforms to the valid GStreamer order, ensure that channel + * reordering is disabled. + * Otherwise, if the order of the positions in the channel_positions array + * does not conform to the GStreamer order, ensure it is enabled. + */ + + if (gst_audio_check_valid_channel_positions (config->channel_positions, + config->num_channels, TRUE)) { + + config->needs_channel_reordering = FALSE; + + return TRUE; + } else { + config->needs_channel_reordering = TRUE; + memcpy (config->reordered_channel_positions, config->channel_positions, + sizeof (GstAudioChannelPosition) * config->num_channels); + return + gst_audio_channel_positions_to_valid_order + (config->reordered_channel_positions, config->num_channels); + } +} + + +static void +gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * config) +{ + switch (config->format) { + case GST_RAW_AUDIO_PARSE_FORMAT_PCM: + { + GstAudioFormatInfo const *fmt_info = + gst_audio_format_get_info (config->pcm_format); + g_assert (fmt_info != NULL); + + config->bpf = + GST_AUDIO_FORMAT_INFO_WIDTH (fmt_info) * config->num_channels / 8; + + break; + } + + case GST_RAW_AUDIO_PARSE_FORMAT_ALAW: + case GST_RAW_AUDIO_PARSE_FORMAT_MULAW: + /* A-law and mu-law both use 1 byte per sample */ + config->bpf = 1 * config->num_channels; + break; + + default: + g_assert_not_reached (); + } +} + + +static gboolean +gst_raw_audio_parse_caps_to_config (GstRawAudioParse * raw_audio_parse, + GstCaps * caps, GstRawAudioParseConfig * config) +{ + gboolean ret = FALSE; + GstStructure *structure; + + /* Caps might get copied, and the copy needs to be unref'd. + * Also, the caller retains ownership over the original caps. + * So, to make this mechanism also work with cases where the + * caps are *not* copied, ref the original caps here first. */ + gst_caps_ref (caps); + + structure = gst_caps_get_structure (caps, 0); + + /* For unaligned raw data, the output caps stay the same, + * except that audio/x-unaligned-raw becomes audio/x-raw, + * since the parser aligns the sample data */ + if (gst_structure_has_name (structure, "audio/x-unaligned-raw")) { + /* Copy the caps to be able to modify them */ + GstCaps *new_caps = gst_caps_copy (caps); + gst_caps_unref (caps); + caps = new_caps; + + /* Change the media type to audio/x-raw , otherwise + * gst_audio_info_from_caps() won't work */ + structure = gst_caps_get_structure (caps, 0); + gst_structure_set_name (structure, "audio/x-raw"); + } + + if (gst_structure_has_name (structure, "audio/x-raw")) { + guint num_channels; + GstAudioInfo info; + if (!gst_audio_info_from_caps (&info, caps)) { + GST_ERROR_OBJECT (raw_audio_parse, + "failed to parse caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + + num_channels = GST_AUDIO_INFO_CHANNELS (&info); + + config->format = GST_RAW_AUDIO_PARSE_FORMAT_PCM; + config->pcm_format = GST_AUDIO_INFO_FORMAT (&info); + config->bpf = GST_AUDIO_INFO_BPF (&info); + config->sample_rate = GST_AUDIO_INFO_RATE (&info); + config->interleaved = + (GST_AUDIO_INFO_LAYOUT (&info) == GST_AUDIO_LAYOUT_INTERLEAVED); + + gst_raw_audio_parse_set_config_channels (config, num_channels, 0, FALSE); + memcpy (config->channel_positions, &(GST_AUDIO_INFO_POSITION (&info, 0)), + sizeof (GstAudioChannelPosition) * num_channels); + } else if (gst_structure_has_name (structure, "audio/x-alaw") + || gst_structure_has_name (structure, "audio/x-mulaw")) { + gint i; + guint64 channel_mask; + guint num_channels; + + config->format = + gst_structure_has_name (structure, + "audio/x-alaw") ? GST_RAW_AUDIO_PARSE_FORMAT_ALAW : + GST_RAW_AUDIO_PARSE_FORMAT_MULAW; + + if (!gst_structure_get_int (structure, "rate", &i)) { + GST_ERROR_OBJECT (raw_audio_parse, + "missing rate value in caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + config->sample_rate = i; + + if (!gst_structure_get_int (structure, "channels", &i)) { + GST_ERROR_OBJECT (raw_audio_parse, + "missing channels value in caps %" GST_PTR_FORMAT, (gpointer) caps); + goto done; + } + num_channels = i; + + if (!gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK, + &channel_mask, NULL)) { + channel_mask = gst_audio_channel_get_fallback_mask (num_channels); + GST_DEBUG_OBJECT (raw_audio_parse, + "input caps have no channel mask - using fallback mask %#lx for %u channels", + channel_mask, num_channels); + } + + if (!gst_raw_audio_parse_set_config_channels (config, num_channels, + channel_mask, TRUE)) { + GST_ERROR_OBJECT (raw_audio_parse, + "could not use channel mask %#lx for channel positions", + channel_mask); + goto done; + } + + /* A-law and mu-law both use 1 byte per sample */ + config->bpf = 1 * num_channels; + } else { + GST_ERROR_OBJECT (raw_audio_parse, + "caps %" GST_PTR_FORMAT " have an unsupported media type", + (gpointer) caps); + goto done; + } + + ret = TRUE; + +done: + gst_caps_unref (caps); + if (ret) + config->ready = TRUE; + return ret; +} + + +static gboolean +gst_raw_audio_parse_config_to_caps (GstRawAudioParse * raw_audio_parse, + GstCaps ** caps, GstRawAudioParseConfig * config) +{ + gboolean ret = TRUE; + GstAudioChannelPosition *channel_positions; + + g_assert (caps != NULL); + + if (config->bpf == 0) { + GST_ERROR_OBJECT (raw_audio_parse, + "cannot convert config to caps - config not filled with valid values"); + *caps = NULL; + return FALSE; + } + + channel_positions = + config->needs_channel_reordering ? &(config-> + reordered_channel_positions[0]) : &(config->channel_positions[0]); + + switch (config->format) { + case GST_RAW_AUDIO_PARSE_FORMAT_PCM: + { + GstAudioInfo info; + gst_audio_info_init (&info); + gst_audio_info_set_format (&info, + config->pcm_format, + config->sample_rate, config->num_channels, channel_positions); + + *caps = gst_audio_info_to_caps (&info); + + break; + } + + case GST_RAW_AUDIO_PARSE_FORMAT_ALAW: + case GST_RAW_AUDIO_PARSE_FORMAT_MULAW: + { + guint64 channel_mask; + + if (!gst_audio_channel_positions_to_mask (channel_positions, + config->num_channels, TRUE, &channel_mask)) { + GST_ERROR_OBJECT (raw_audio_parse, "invalid channel positions"); + ret = FALSE; + break; + } + + *caps = gst_caps_new_simple ( + (config->format == + GST_RAW_AUDIO_PARSE_FORMAT_ALAW) ? "audio/x-alaw" : + "audio/x-mulaw", "rate", G_TYPE_INT, config->sample_rate, "channels", + G_TYPE_INT, config->num_channels, "channel-mask", GST_TYPE_BITMASK, + channel_mask, NULL); + + break; + } + + default: + g_assert_not_reached (); + ret = FALSE; + } + + if (!ret) + *caps = NULL; + + return ret; +} + + + + +GType +gst_raw_audio_parse_format_get_type (void) +{ + static GType audio_parse_format_gtype = 0; + static const GEnumValue types[] = { + {GST_RAW_AUDIO_PARSE_FORMAT_PCM, "PCM", "pcm"}, + {GST_RAW_AUDIO_PARSE_FORMAT_ALAW, "A-Law", "alaw"}, + {GST_RAW_AUDIO_PARSE_FORMAT_MULAW, "\302\265-Law", "mulaw"}, + {0, NULL, NULL} + }; + + if (!audio_parse_format_gtype) + audio_parse_format_gtype = + g_enum_register_static ("GstRawAudioParseFormat", types); + + return audio_parse_format_gtype; +} diff --git a/gst/rawparse/gstrawaudioparse.h b/gst/rawparse/gstrawaudioparse.h new file mode 100644 index 0000000000..f1e0766e5e --- /dev/null +++ b/gst/rawparse/gstrawaudioparse.h @@ -0,0 +1,131 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RAW_AUDIO_PARSE_H__ +#define __GST_RAW_AUDIO_PARSE_H__ + +#include +#include +#include "gstrawbaseparse.h" + +G_BEGIN_DECLS + +#define GST_TYPE_RAW_AUDIO_PARSE \ + (gst_raw_audio_parse_get_type()) +#define GST_RAW_AUDIO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_AUDIO_PARSE, GstRawAudioParse)) +#define GST_RAW_AUDIO_PARSE_CAST(obj) \ + ((GstRawAudioParse *)(obj)) +#define GST_RAW_AUDIO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_AUDIO_PARSE, GstRawAudioParseClass)) +#define GST_IS_RAW_AUDIO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_AUDIO_PARSE)) +#define GST_IS_RAW_AUDIO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_AUDIO_PARSE)) + + +typedef enum _GstRawAudioParseFormat GstRawAudioParseFormat; + +typedef struct _GstRawAudioParseConfig GstRawAudioParseConfig; +typedef struct _GstRawAudioParse GstRawAudioParse; +typedef struct _GstRawAudioParseClass GstRawAudioParseClass; + + +enum _GstRawAudioParseFormat +{ + GST_RAW_AUDIO_PARSE_FORMAT_PCM, + GST_RAW_AUDIO_PARSE_FORMAT_MULAW, + GST_RAW_AUDIO_PARSE_FORMAT_ALAW +}; + + +/* Contains information about the sample rate, format, and channel count to use. */ +struct _GstRawAudioParseConfig +{ + /* If TRUE, then this configuration is ready to use */ + gboolean ready; + /* Format of the configuration. Can be PCM, a-law, mu-law. */ + GstRawAudioParseFormat format; + /* If format is set to PCM, this specifies the exact PCM format in use. + * Meaningless if format is set to anything other than PCM. */ + GstAudioFormat pcm_format; + /* Bytes per frame. Calculated as: bpf = bytes_per_sample * num_channels + * Must be nonzero. This is the size of one frame, the value returned + * by the GstRawBaseParseClass get_config_frame_size() vfunc. */ + guint bpf; + /* Sample rate in Hz - must be nonzero */ + guint sample_rate; + /* Number of channels - must be nonzero */ + guint num_channels; + /* TRUE if the data is interleaved, FALSE otherwise */ + gboolean interleaved; + + /* Array of channel positions, one position per channel; its first + * num_channels values are valid. They are computed out of the number + * of channels if no positions are explicitely given. */ + GstAudioChannelPosition channel_positions[64]; + + /* If the channel_positions are in a valid GStreamer channel order, then + * this is not used, and needs_channel_reordering is FALSE. Otherwise, + * this contains the same positions as in channel_positions, but in the + * order GStreamer expects. needs_channel_reordering will be TRUE in that + * case. This is used for reordering samples in outgoing buffers if + * necessary. */ + GstAudioChannelPosition reordered_channel_positions[64]; + + /* TRUE if channel reordering is necessary, FALSE otherwise. See above + * for details. */ + gboolean needs_channel_reordering; +}; + + +struct _GstRawAudioParse +{ + GstRawBaseParse parent; + + /*< private > */ + + /* Configuration controlled by the object properties. Its ready value + * is set to TRUE from the start, so it can be used right away. + */ + GstRawAudioParseConfig properties_config; + /* Configuration controlled by the sink caps. Its ready value is + * initially set to FALSE until valid sink caps come in. It is set to + * FALSE again when the stream-start event is observed. + */ + GstRawAudioParseConfig sink_caps_config; + /* Currently active configuration. Points either to properties_config + * or to sink_caps_config. This is never NULL. */ + GstRawAudioParseConfig *current_config; +}; + + +struct _GstRawAudioParseClass +{ + GstRawBaseParseClass parent_class; +}; + + +GType gst_raw_audio_parse_get_type (void); +GType gst_raw_audio_parse_format_get_type (void); + + +G_END_DECLS + +#endif diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c new file mode 100644 index 0000000000..e90f400fab --- /dev/null +++ b/gst/rawparse/gstrawbaseparse.c @@ -0,0 +1,748 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:gstrawbaseparse + * @short_description: Base class for raw media data parsers + * + * This base class is for parsers which read raw media data and output + * timestamped buffers with an integer number of frames inside. + * + * The format of the raw media data is specified in one of two ways: either, + * the information from the sink pad's caps is taken, or the information from + * the properties is used (this is chosen by the use-sink-caps property). + * These two ways are internally referred to as "configurations". The configuration + * that receives its information from the sink pad's caps is called the + * "sink caps configuration", while the one that depends on the information from + * the properties is the "properties configuration". Configurations have a + * "readiness". A configuration is "ready" when it contains valid information. + * For example, with an audio parser, a configuration is not ready unless it + * contains a valid sample rate, sample format, and channel count. + * + * The properties configuration must always be ready, even right from the start. + * Subclasses must ensure this. The underlying reason is that properties have valid + * values right from the start, and with the properties configuration, there is + * nothing that readies it before actual data is sent (unlike with the sink caps + * configuration, where a sink caps event will ready it before data is pushed + * downstream). + * + * It is possible to switch between the configurations during a stream by + * setting the use-sink-caps property. Subclasses typically allow for updating the + * properties configuration during a stream by setting the various properties + * (like sample-rate for a raw audio parser). + * In these cases, the parser will produce a new CAPS event and push it downstream + * to announce the caps for the new configuration. This also happens if the sink + * caps change. + * + * A common mistake when trying to parse raw data with no input caps (for example, + * a file with raw PCM samples when using rawaudioparse) is to forget to set the + * use-sink-caps property to FALSE. In this case, the parser will report an error + * when it tries to access the current configuration (because then the sink caps + * configuration will be the current one and it will not contain valid values + * since no sink caps were seen at this point). + * + * Subclasses must ensure that the properties configuration is the default one. + * + * The sink caps configuration is mostly useful with push-based sources, because these + * will produce caps events and send them downstream. With pull-based sources, it is + * possible that this doesn't happen. Since the sink caps configuration requires a caps + * event to arrive at the sinkpad, this will cause the parser to fail then. + * + * The base class identifies the configurations by means of the GstRawAudioParseConfig + * enum. It instructs the subclass to switch between configurations this way, and + * also requests information about the current configuration, a configuration's + * frame size, its readiness, etc. Subclasses are not required to use any particular + * structure for the configuration implementations. + * + * Use the GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK and GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK + * macros to protect configuration modifications. + * + * + * + * Summary of the subclass requirements + * + * Sink caps and properties configurations must both be + * implemented and supported. It must also be ensured that there is a + * "current" configuration. + * + * Modifications to the configurations must be protected with the + * GstRawBaseParse lock. This is typically necessary when the + * properties configuration is modified by setting new property values. + * (Note that the lock is held during *all* vfunc calls.) + * + * If the properties configuration is updated (typically by + * setting new property values), gst_raw_base_parse_invalidate_src_caps() + * must be called if the properties config is the current one. This is + * necessary to ensure that GstBaseParse pushes a new caps event downstream + * which contains caps from the updated configuration. + * + * + * In case there are bytes in each frame that aren't part of the actual + * payload, the get_overhead_size() vfunc must be defined, and the + * @get_config_frame_size() vfunc must return a frame size that includes + * the number of non-payload bytes (= the overhead). Otherwise, the + * timestamps will incorrectly include the overhead bytes. + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "gstrawbaseparse.h" + + +GST_DEBUG_CATEGORY_STATIC (raw_base_parse_debug); +#define GST_CAT_DEFAULT raw_base_parse_debug + + +enum +{ + PROP_0, + PROP_USE_SINK_CAPS +}; + + +#define DEFAULT_USE_SINK_CAPS FALSE +#define INITIAL_PARSER_CONFIG \ + ((DEFAULT_USE_SINK_CAPS) ? GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : \ + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES) + + +#define gst_raw_base_parse_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE (GstRawBaseParse, gst_raw_base_parse, + GST_TYPE_BASE_PARSE); + + +static void gst_raw_base_parse_finalize (GObject * object); +static void gst_raw_base_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec); +static void gst_raw_base_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static gboolean gst_raw_base_parse_start (GstBaseParse * parse); +static gboolean gst_raw_base_parse_stop (GstBaseParse * parse); +static gboolean gst_raw_base_parse_set_sink_caps (GstBaseParse * parse, + GstCaps * caps); +static GstFlowReturn gst_raw_base_parse_handle_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, gint * skipsize); +static gboolean gst_raw_base_parse_convert (GstBaseParse * parse, + GstFormat src_format, gint64 src_value, GstFormat dest_format, + gint64 * dest_value); + +static gboolean gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * + raw_base_parse); +static gboolean gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * + raw_base_parse, GstFormat format); + + + +static void +gst_raw_base_parse_class_init (GstRawBaseParseClass * klass) +{ + GObjectClass *object_class; + GstBaseParseClass *baseparse_class; + + GST_DEBUG_CATEGORY_INIT (raw_base_parse_debug, "rawbaseparse", 0, + "raw base parse class"); + + object_class = G_OBJECT_CLASS (klass); + baseparse_class = GST_BASE_PARSE_CLASS (klass); + + object_class->finalize = GST_DEBUG_FUNCPTR (gst_raw_base_parse_finalize); + object_class->set_property = + GST_DEBUG_FUNCPTR (gst_raw_base_parse_set_property); + object_class->get_property = + GST_DEBUG_FUNCPTR (gst_raw_base_parse_get_property); + + baseparse_class->start = GST_DEBUG_FUNCPTR (gst_raw_base_parse_start); + baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_raw_base_parse_stop); + baseparse_class->set_sink_caps = + GST_DEBUG_FUNCPTR (gst_raw_base_parse_set_sink_caps); + baseparse_class->handle_frame = + GST_DEBUG_FUNCPTR (gst_raw_base_parse_handle_frame); + baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_raw_base_parse_convert); + + /** + * GstRawBaseParse::use-sink-caps: + * + * Use sink caps configuration. If set to false, the parser + * will use the properties configuration instead. It is possible + * to switch between these during playback. + */ + g_object_class_install_property (object_class, + PROP_USE_SINK_CAPS, + g_param_spec_boolean ("use-sink-caps", + "Use sink caps", + "Use the sink caps for defining the output format", + DEFAULT_USE_SINK_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); +} + + +static void +gst_raw_base_parse_init (GstRawBaseParse * raw_base_parse) +{ + raw_base_parse->src_caps_set = FALSE; + g_mutex_init (&(raw_base_parse->config_mutex)); +} + + +static void +gst_raw_base_parse_finalize (GObject * object) +{ + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + + g_mutex_clear (&(raw_base_parse->config_mutex)); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static void +gst_raw_base_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec) +{ + GstBaseParse *base_parse = GST_BASE_PARSE (object); + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (object); + + g_assert (klass->is_config_ready); + g_assert (klass->set_current_config); + + switch (prop_id) { + case PROP_USE_SINK_CAPS: + { + gboolean new_state, cur_state; + GstRawBaseParseConfig new_config; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + /* Check to ensure nothing is done if the value stays the same */ + new_state = g_value_get_boolean (value); + cur_state = gst_raw_base_parse_is_using_sink_caps (raw_base_parse); + if (new_state == cur_state) { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + GST_DEBUG_OBJECT (raw_base_parse, "switching to %s config", + new_state ? "sink caps" : "properties"); + new_config = + new_state ? GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; + + if (!klass->set_current_config (raw_base_parse, new_config)) { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + GST_ELEMENT_ERROR (raw_base_parse, STREAM, FAILED, + ("could not set new current config"), ("use-sink-caps property: %d", + new_state)); + break; + } + + /* Update the minimum frame size if the config is ready. This ensures that + * the next buffer that is passed to handle_frame contains complete frames. + * If the current config is the properties config, then it will always be + * ready, and its frame size will be valid. Ensure that the baseparse minimum + * frame size is set properly then. + * If the current config is the sink caps config, then it will initially not + * be ready until the sink caps are set, so the minimum frame size cannot be + * set right here. However, since the caps always come in *before* the actual + * data, the config will be readied in the set_sink_caps function, and be ready + * by the time handle_frame is called. There, the minimum frame size is set as + * well. */ + if (klass->is_config_ready (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT)) { + gsize frame_size = klass->get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT); + gst_base_parse_set_min_frame_size (base_parse, frame_size); + } + + /* Since the current config was switched, the source caps change. Ensure the + * new caps are pushed downstream by setting src_caps_set to FALSE: This way, + * the next handle_frame call will take care of that. */ + raw_base_parse->src_caps_set = FALSE; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_raw_base_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + + switch (prop_id) { + case PROP_USE_SINK_CAPS: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_boolean (value, + gst_raw_base_parse_is_using_sink_caps (raw_base_parse)); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_raw_base_parse_start (GstBaseParse * parse) +{ + GstBaseParse *base_parse = GST_BASE_PARSE (parse); + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); + + g_assert (klass->set_current_config); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); + + /* If the config is ready from the start, set the min frame size + * (this will happen with the properties config) */ + if (klass->is_config_ready (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT)) { + gsize frame_size = klass->get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT); + gst_base_parse_set_min_frame_size (base_parse, frame_size); + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + + return TRUE; +} + + +static gboolean +gst_raw_base_parse_stop (GstBaseParse * parse) +{ + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); + raw_base_parse->src_caps_set = FALSE; + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + + return TRUE; +} + + +static gboolean +gst_raw_base_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps) +{ + gboolean ret = FALSE; + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); + + g_assert (klass->set_config_from_caps); + g_assert (klass->get_caps_from_config); + g_assert (klass->get_config_frame_size); + + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); + + GST_DEBUG_OBJECT (parse, "getting config from new sink caps"); + + /* Convert the new sink caps to sink caps config. This also + * readies the config. */ + ret = + klass->set_config_from_caps (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS, caps); + if (!ret) { + GST_ERROR_OBJECT (raw_base_parse, "could not get config from sink caps"); + goto done; + } + + /* If the sink caps config is currently active, push caps downstream, + * set the minimum frame size (to guarantee that input buffers hold + * complete frames), and update the src_caps_set flag. If the sink + * caps config isn't the currently active config, just exit, since in + * that case, the caps will always be pushed downstream in handle_frame. */ + if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) { + GstCaps *new_src_caps; + gsize frame_size; + + GST_DEBUG_OBJECT (parse, + "sink caps config is the current one; trying to push new caps downstream"); + + /* Convert back to caps. The caps may have changed, for example + * audio/x-unaligned-raw may have been replaced with audio/x-raw. + * (Also, this keeps the behavior in sync with that of the block + * in handle_frame that pushes caps downstream if not done already.) */ + if (!klass->get_caps_from_config (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT, &new_src_caps)) { + GST_ERROR_OBJECT (raw_base_parse, + "could not get src caps from current config"); + goto done; + } + + GST_DEBUG_OBJECT (raw_base_parse, + "got new sink caps; updating src caps to %" GST_PTR_FORMAT, + (gpointer) new_src_caps); + + frame_size = + klass->get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT); + gst_base_parse_set_min_frame_size (parse, frame_size); + + raw_base_parse->src_caps_set = TRUE; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + + /* Push caps outside of the lock */ + gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (raw_base_parse), + gst_event_new_caps (new_src_caps) + ); + + gst_caps_unref (new_src_caps); + } else { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + } + + ret = TRUE; + +done: + return ret; +} + + +static GstFlowReturn +gst_raw_base_parse_handle_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, gint * skipsize) +{ + gsize in_size, out_size; + guint frame_size; + guint num_out_frames; + gsize units_n, units_d; + guint64 buffer_duration; + GstFlowReturn flow_ret = GST_FLOW_OK; + GstEvent *new_caps_event = NULL; + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); + + g_assert (klass->is_config_ready); + g_assert (klass->get_caps_from_config); + g_assert (klass->get_config_frame_size); + g_assert (klass->get_units_per_second); + + + /* We never skip any bytes this way. Instead, subclass takes care + * of skipping any overhead (necessary, since the way it needs to + * be skipped is completely subclass specific). */ + *skipsize = 0; + + + /* The operations below access the current config. Protect + * against race conditions by using the object lock. */ + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); + + + /* If the source pad caps haven't been set yet, or need to be + * set again, do so now, BEFORE any buffers are pushed out */ + if (G_UNLIKELY (!raw_base_parse->src_caps_set)) { + GstCaps *new_src_caps; + + if (G_UNLIKELY (!klass->is_config_ready (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT))) { + /* The current configuration is not ready. No caps can be + * generated out of it. + * The most likely reason for this is that the sink caps config + * is the current one and no valid sink caps have been pushed + * by upstream. Report the problem and exit. */ + + if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) { + goto config_not_ready; + } else { + /* This should not be reached if the property config is active */ + g_assert_not_reached (); + } + } + + GST_DEBUG_OBJECT (parse, + "setting src caps since this has not been done yet"); + + /* Convert the current config to a caps structure to + * inform downstream about the new format */ + if (!klass->get_caps_from_config (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT, &new_src_caps)) { + GST_ERROR_OBJECT (raw_base_parse, + "could not get src caps from current config"); + flow_ret = GST_FLOW_NOT_NEGOTIATED; + goto error_locked; + } + + new_caps_event = gst_event_new_caps (new_src_caps); + gst_caps_unref (new_src_caps); + + raw_base_parse->src_caps_set = TRUE; + } + + frame_size = + klass->get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT); + + + in_size = gst_buffer_get_size (frame->buffer); + + /* gst_base_parse_set_min_frame_size() is called when the current + * configuration changes and the change affects the frame size. This + * means that a buffer must contain at least as many bytes as indicated + * by the frame size. If there are fewer inside an error occurred; + * either something in the parser went wrong, or the min frame size + * wasn't updated properly. */ + g_assert (in_size >= frame_size); + + /* Determine how many complete frames would fit in the input buffer. + * Then check if this amount exceeds the maximum number of frames + * as indicated by the subclass. */ + num_out_frames = (in_size / frame_size); + if (klass->get_max_frames_per_buffer) { + guint max_num_out_frames = klass->get_max_frames_per_buffer (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT); + num_out_frames = MIN (num_out_frames, max_num_out_frames); + } + + /* Ensure that the size of the buffers that get pushed downstream + * is always an integer multiple of the frame size to prevent cases + * where downstream gets buffers with incomplete frames. */ + out_size = num_out_frames * frame_size; + + /* Set the overhead size to ensure that timestamping excludes these + * extra overhead bytes. */ + frame->overhead = + klass->get_overhead_size ? klass->get_overhead_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT) : 0; + + g_assert (out_size >= (guint) (frame->overhead)); + out_size -= frame->overhead; + + GST_LOG_OBJECT (raw_base_parse, + "%" G_GSIZE_FORMAT " bytes input %" G_GSIZE_FORMAT + " bytes output (%u frame(s)) %d bytes overhead", in_size, out_size, + num_out_frames, frame->overhead); + + /* Calculate buffer duration */ + klass->get_units_per_second (raw_base_parse, GST_FORMAT_BYTES, + GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d); + if (units_n == 0 || units_d == 0) + buffer_duration = GST_CLOCK_TIME_NONE; + else + buffer_duration = + gst_util_uint64_scale (out_size, GST_SECOND * units_d, units_n); + + if (klass->process) { + GstBuffer *processed_data = NULL; + + if (!klass->process (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT, + frame->buffer, in_size, out_size, &processed_data)) + goto process_error; + + frame->out_buffer = processed_data; + } else { + frame->out_buffer = NULL; + } + + /* Set the duration of the output buffer, or if none exists, of + * the input buffer. Do this after the process() call, since in + * case out_buffer is set, the subclass has created a new buffer. + * Instead of requiring subclasses to set the duration (which + * anyway must always be buffer_duration), let's do it here. */ + if (frame->out_buffer != NULL) + GST_BUFFER_DURATION (frame->out_buffer) = buffer_duration; + else + GST_BUFFER_DURATION (frame->buffer) = buffer_duration; + + /* Access to the current config is not needed in subsequent + * operations, so the lock can be released */ + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + + + /* If any new caps have to be pushed downstrean, do so + * *before* the frame is finished */ + if (G_UNLIKELY (new_caps_event != NULL)) { + gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (raw_base_parse), + new_caps_event); + new_caps_event = NULL; + } + + gst_base_parse_finish_frame (parse, frame, out_size + frame->overhead); + + + return flow_ret; + + +config_not_ready: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + GST_ELEMENT_ERROR (parse, STREAM, FORMAT, + ("sink caps config is the current config, and it is not ready -" + "upstream may not have pushed a caps event yet"), (NULL)); + flow_ret = GST_FLOW_ERROR; + goto error_end; + +process_error: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + GST_ELEMENT_ERROR (parse, STREAM, DECODE, ("could not process data"), (NULL)); + flow_ret = GST_FLOW_ERROR; + goto error_end; + +error_locked: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + goto error_end; + +error_end: + frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; + if (new_caps_event != NULL) + gst_event_unref (new_caps_event); + return flow_ret; +} + + +static gboolean +gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format, + gint64 src_value, GstFormat dest_format, gint64 * dest_value) +{ + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); + gboolean ret = TRUE; + gsize units_n, units_d; + + g_assert (klass->is_config_ready); + g_assert (klass->get_units_per_second); + + + /* The operations below access the current config. Protect + * against race conditions by using the object lock. */ + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); + + + if (!klass->is_config_ready (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT)) { + if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) { + goto config_not_ready; + } else { + /* This should not be reached if the property config is active */ + g_assert_not_reached (); + } + } + + if (G_UNLIKELY (src_format == dest_format)) { + *dest_value = src_value; + } else if ((src_format == GST_FORMAT_TIME || dest_format == GST_FORMAT_TIME) + && gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format) + && gst_raw_base_parse_is_gstformat_supported (raw_base_parse, src_format)) { + /* Perform conversions here if either the src or dest format + * are GST_FORMAT_TIME and the other format is supported by + * the subclass. This is because we perform TIME<->non-TIME + * conversions here. Typically, subclasses only support + * BYTES and DEFAULT formats. */ + + if (src_format == GST_FORMAT_TIME) { + /* The source format is time, so perform a TIME -> non-TIME conversion */ + klass->get_units_per_second (raw_base_parse, dest_format, + GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d); + *dest_value = (units_n == 0 + || units_d == 0) ? src_value : gst_util_uint64_scale (src_value, + units_n, GST_SECOND * units_d); + } else { + /* The dest format is time, so perform a non-TIME -> TIME conversion */ + klass->get_units_per_second (raw_base_parse, src_format, + GST_RAW_BASE_PARSE_CONFIG_CURRENT, &units_n, &units_d); + *dest_value = (units_n == 0 + || units_d == 0) ? src_value : gst_util_uint64_scale (src_value, + GST_SECOND * units_d, units_n); + } + } else { + /* Fallback for other conversions */ + ret = + gst_base_parse_convert_default (parse, src_format, src_value, + dest_format, dest_value); + } + + GST_DEBUG_OBJECT (parse, + "converted %s -> %s %" G_GINT64_FORMAT " -> %" GST_TIME_FORMAT, + gst_format_get_name (src_format), gst_format_get_name (dest_format), + src_value, GST_TIME_ARGS (*dest_value)); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + return ret; + + +config_not_ready: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + GST_ELEMENT_ERROR (parse, STREAM, FORMAT, + ("sink caps config is the current config, and it is not ready - " + "upstream may not have pushed a caps event yet"), (NULL)); + return FALSE; +} + + +static gboolean +gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * raw_base_parse) +{ + /* must be called with lock */ + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (raw_base_parse); + g_assert (klass->get_current_config); + return klass->get_current_config (raw_base_parse) == + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS; +} + + +static gboolean +gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * raw_base_parse, + GstFormat format) +{ + /* must be called with lock */ + GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (raw_base_parse); + g_assert (klass->is_unit_format_supported); + return klass->is_unit_format_supported (raw_base_parse, format); +} + + + + + +/** + * gst_raw_base_parse_invalidate_src_caps: + * @raw_base_parse: a #GstRawBaseParse instance + * + * Flags the current source caps as invalid. Before the next downstream + * buffer push, @get_caps_from_config is called, and the created caps are + * pushed downstream in a new caps event, This is used if for example the + * properties configuration is modified in the subclass. + * + * Note that this must be called with the parser lock held. Use the + * GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK() and GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK() + * macros for this purpose. + */ +void +gst_raw_base_parse_invalidate_src_caps (GstRawBaseParse * raw_base_parse) +{ + /* must be called with lock */ + g_assert (raw_base_parse != NULL); + raw_base_parse->src_caps_set = FALSE; +} diff --git a/gst/rawparse/gstrawbaseparse.h b/gst/rawparse/gstrawbaseparse.h new file mode 100644 index 0000000000..519b409ed2 --- /dev/null +++ b/gst/rawparse/gstrawbaseparse.h @@ -0,0 +1,207 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RAW_BASE_PARSE_H__ +#define __GST_RAW_BASE_PARSE_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RAW_BASE_PARSE \ + (gst_raw_base_parse_get_type()) +#define GST_RAW_BASE_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParse)) +#define GST_RAW_BASE_PARSE_CAST(obj) \ + ((GstRawBaseParse *)(obj)) +#define GST_RAW_BASE_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParseClass)) +#define GST_RAW_BASE_PARSE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_RAW_BASE_PARSE, GstRawBaseParseClass)) +#define GST_IS_RAW_BASE_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_BASE_PARSE)) +#define GST_IS_RAW_BASE_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_BASE_PARSE)) + +#define GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK(obj) g_mutex_lock(&(((GstRawBaseParse *)(obj))->config_mutex)) +#define GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK(obj) g_mutex_unlock(&(((GstRawBaseParse *)(obj))->config_mutex)) + + +typedef enum _GstRawBaseParseConfig GstRawBaseParseConfig; +typedef struct _GstRawBaseParse GstRawBaseParse; +typedef struct _GstRawBaseParseClass GstRawBaseParseClass; + + +/** + * GstRawBaseParseConfig: + * @GST_RAW_BASE_PARSE_CONFIG_CURRENT: configuration that is currently active + * @GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: configuration that is defined by the input sink caps + * @GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: configuration that is defined by class properties + * + * Identifier for the type of parser configuration. + */ +enum _GstRawBaseParseConfig +{ + GST_RAW_BASE_PARSE_CONFIG_CURRENT = 1, + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES +}; + + +/** + * GstRawBaseParse: + * + * The opaque #GstRawBaseParse data structure. + */ +struct _GstRawBaseParse +{ + GstBaseParse parent; + + /*< private > */ + + /* TRUE if the source pad caps have been set already. This is used + * for checking if the source pad caps have to be set. */ + gboolean src_caps_set; + + /* Mutex which protects access to and modifications on the configs. */ + GMutex config_mutex; +}; + + +/** + * GstRawBaseParseClass: + * @parent_class: The parent class structure + * @set_current_config: Sets the new current configuration. Subclasses must internally + * switch to this new configuration. Return FALSE if this failed, + * TRUE otherwise. + * @get_current_config: Gets the current configuration. All return values except + * except GST_RAW_BASE_PARSE_CONFIG_CURRENT are valid. + * @set_config_from_caps: Parses the caps and copies its information to the configuration. + * Returns FALSE if this failed, TRUE otheriwse. Specified caps + * are not unref'd. + * @get_caps_from_config: Creates a new caps structure out of the information from the + * specified configuration. Ownership over the returned caps are + * transferred to the caller. If something fails during the caps + * creation, the vfunc must make sure to destroy any partially + * created caps; the *caps value is always set to NULL in case of + * failure. Returns FALSE in case of failure, + * TRUE in case of success. + * @get_config_frame_size: Gets the size of one frame, in bytes, from the specified + * configuration. This must be the size of the complete frame, + * including any overhead (metadata, headers, padding bytes etc.). + * @get_max_frames_per_buffer: Optional. + * Returns up to how many complete frames one output buffer may + * contain. The value must be nonzero. This is useful for example + * with video parsers which need to ensure that one output buffer + * contains only one video frame, even if the input buffer contains + * several complete frames. If this vfunc is not set, then there + * is no maximum number of frames per buffer - the parser reads + * as many complete frames as possible from the input buffer. + * @is_config_ready: Returns TRUE if the specified configuration is ready, FALSE + * otherwise. + * @process: Optional. + * This is useful to do any last minute processing before the + * data is pushed downstream. One example is channel reordering + * in audio parsers. + * in_data is the complete input buffer, total_num_in_bytes is + * the total amount of bytes this input buffer contains (including + * excess bytes that form an incomplete rame). num_valid_in_bytes + * is the subset of these bytes that are to be pushed downstream. + * If for example the frame size is 4, and total_num_in_bytes is + * 411, then num_valid_in_bytes will be 408, since the last 3 + * bytes form an incomplete frame. + * The value of num_valid_in_bytes excludes the overhead bytes + * indicated by @get_overhead_size. + * If the subclass creates a new buffer here, *processed_data + * must be set to the new buffer's pointer. If the subclass does + * not create any new buffer, and just expects the first + * num_valid_in_bytes of the input buffer to be pushed downstream, + * then *processed_data must be set to NULL. + * If this vfunc is not set, then the parser behaves as if this + * vfunc set *processed_data data to NULL. + * @is_unit_format_supported: Returns TRUE if the given format is supported by the + * @get_units_per_second function, FALSE otherwise. + * @get_units_per_second: Returns how many units per second exist for a given format. + * For example, with an audio parser and format DEFAULT, the units + * per second are typically the number of samples per second + * (= the sample rate). For video parsers, this would be the frame + * rate. If BYTES or TIME are used as format, then the result must + * not include any extra overhead (metadata, headers, padding etc.) + * @get_overhead_size: Optional. + * Returns the number of bytes that make up the portion of a frame + * that isn't payload. Examples are padding bytes, headers, and + * other kinds of metadata. If this vfunc isn't defined, then an + * overhead size of 0 bytes is assumed. + * + * Subclasses are required to override all vfuncs except for @process, which is optional. + * The raw base parser lock is held during all vfunc calls. + */ +struct _GstRawBaseParseClass +{ + GstBaseParseClass parent_class; + + gboolean (*set_current_config) (GstRawBaseParse *raw_base_parse, + GstRawBaseParseConfig config); + GstRawBaseParseConfig (*get_current_config) (GstRawBaseParse *raw_base_parse); + + gboolean (*set_config_from_caps) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, + GstCaps * caps); + gboolean (*get_caps_from_config) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, + GstCaps ** caps); + + gsize (*get_config_frame_size) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); + guint (*get_max_frames_per_buffer) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); + + gboolean (*is_config_ready) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); + + gboolean (*process) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, + GstBuffer * in_data, + gsize total_num_in_bytes, + gsize num_valid_in_bytes, + GstBuffer ** processed_data); + + gboolean (*is_unit_format_supported) (GstRawBaseParse * raw_base_parse, + GstFormat format); + void (*get_units_per_second) (GstRawBaseParse * raw_base_parse, + GstFormat format, + GstRawBaseParseConfig config, + gsize * units_per_sec_n, + gsize * units_per_sec_d); + + gint (*get_overhead_size) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); +}; + + +void gst_raw_base_parse_invalidate_src_caps (GstRawBaseParse * raw_base_parse); + + +GType gst_raw_base_parse_get_type (void); + + +G_END_DECLS + +#endif diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c new file mode 100644 index 0000000000..03db577b05 --- /dev/null +++ b/gst/rawparse/gstrawvideoparse.c @@ -0,0 +1,1131 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rawvideoparse + * + * This element parses incoming data as raw video frames and timestamps these. + * It also handles seek queries in said raw video data, and ensures that output + * buffers contain exactly one frame, even if the input buffers contain only + * partial frames or multiple frames. In the former case, it will continue to + * receive buffers until there is enough input data to output one frame. In the + * latter case, it will extract the first frame in the buffer and output it, then + * the second one etc. until the remaining unparsed bytes aren't enough to form + * a complete frame, and it will then continue as described in the earlier case. + * + * The element implements the properties and sink caps configuration as specified + * in the #GstRawBaseParse documentation. The properties configuration can be + * modified by using the width, height, pixel-aspect-ratio, framerate, interlaced, + * top-field-first, plane-strides, plane-offsets, and frame-stride properties. + * + * If the properties configuration is used, be sure to set valid plane stride + * offsets and values, otherwise the produced frames will not have a correct size. + * Merely setting the format is not enough. + * + * The frame stride property is useful in cases where there is extra data between + * the frames (for example, trailing metadata, or headers). The parser calculates + * the actual frame size out of the other properties and compares it with this + * frame-stride value. If the frame stride is larger than the calculated size, + * then the extra bytes after the end of the frame are skipped. For example, with + * 8-bit grayscale frames and a frame size of 100x10 pixels and a frame stride of + * 1500 bytes, there are 500 excess bytes at the end of the actual frame which + * are then skipped. It is safe to set the frame stride to a value that is smaller + * than the actual frame size (in fact, its default value is 0); if it is smaller, + * then no trailing data will be skipped. + * + * If a framerate of 0 Hz is set (for example, 0/1), then output buffers will have + * no duration set. The first output buffer will have a PTS 0, all subsequent ones + * an unset PTS. + * + * + * Example pipelines + * |[ + * gst-launch-1.0 filesrc location=video.raw ! rawvideoparse use-sink-caps=false \ + * width=500 height=400 format=y444 ! autovideosink + * ]| Read raw data from a local file and parse it as video data with 500x400 pixels + * and Y444 video format. + * |[ + * gst-launch-1.0 filesrc location=video.raw ! queue ! "video/x-raw, width=320, \ + * height=240, format=I420, framerate=1/1" ! rawvideoparse \ + * use-sink-caps=true ! autovideosink + * ]| Read raw data from a local file and parse it as video data with 320x240 pixels + * and I420 video format. The queue element here is to force push based scheduling. + * See the documentation in #GstRawBaseParse for the reason why. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative + * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include "gstrawvideoparse.h" +#include "unalignedvideo.h" + + +GST_DEBUG_CATEGORY_STATIC (raw_video_parse_debug); +#define GST_CAT_DEFAULT raw_video_parse_debug + + +enum +{ + PROP_0, + PROP_WIDTH, + PROP_HEIGHT, + PROP_FORMAT, + PROP_PIXEL_ASPECT_RATIO, + PROP_FRAMERATE, + PROP_INTERLACED, + PROP_TOP_FIELD_FIRST, + PROP_PLANE_STRIDES, + PROP_PLANE_OFFSETS, + PROP_FRAME_STRIDE +}; + + +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 +#define DEFAULT_FORMAT GST_VIDEO_FORMAT_I420 +#define DEFAULT_PIXEL_ASPECT_RATIO_N 1 +#define DEFAULT_PIXEL_ASPECT_RATIO_D 1 +#define DEFAULT_FRAMERATE_N 25 +#define DEFAULT_FRAMERATE_D 1 +#define DEFAULT_INTERLACED FALSE +#define DEFAULT_TOP_FIELD_FIRST FALSE +#define DEFAULT_FRAME_STRIDE 0 + + +#define GST_RAW_VIDEO_PARSE_CAPS \ + GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; " + + +static GstStaticPadTemplate static_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UNALIGNED_RAW_VIDEO_CAPS "; " GST_RAW_VIDEO_PARSE_CAPS) + ); + + +static GstStaticPadTemplate static_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_RAW_VIDEO_PARSE_CAPS) + ); + + +#define gst_raw_video_parse_parent_class parent_class +G_DEFINE_TYPE (GstRawVideoParse, gst_raw_video_parse, GST_TYPE_RAW_BASE_PARSE); + + +static void gst_raw_video_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec); +static void gst_raw_video_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_raw_video_parse_stop (GstBaseParse * parse); + +static gboolean gst_raw_video_parse_set_current_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static GstRawBaseParseConfig +gst_raw_video_parse_get_current_config (GstRawBaseParse * raw_base_parse); +static gboolean gst_raw_video_parse_set_config_from_caps (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps * caps); +static gboolean gst_raw_video_parse_get_caps_from_config (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config, GstCaps ** caps); +static gsize gst_raw_video_parse_get_config_frame_size (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static guint gst_raw_video_parse_get_max_frames_per_buffer (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_video_parse_is_config_ready (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); +static gboolean gst_raw_video_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, + gsize num_valid_in_bytes, GstBuffer ** processed_data); +static gboolean gst_raw_video_parse_is_unit_format_supported (GstRawBaseParse * + raw_base_parse, GstFormat format); +static void gst_raw_video_parse_get_units_per_second (GstRawBaseParse * + raw_base_parse, GstFormat format, GstRawBaseParseConfig config, + gsize * units_per_sec_n, gsize * units_per_sec_d); + +static gint gst_raw_video_parse_get_overhead_size (GstRawBaseParse * + raw_base_parse, GstRawBaseParseConfig config); + +static gboolean gst_raw_video_parse_is_using_sink_caps (GstRawVideoParse * + raw_video_parse); +static GstRawVideoParseConfig + * gst_raw_video_parse_get_config_ptr (GstRawVideoParse * raw_video_parse, + GstRawBaseParseConfig config); + +static void gst_raw_video_parse_init_config (GstRawVideoParseConfig * config); +static void gst_raw_video_parse_update_info (GstRawVideoParseConfig * config); + + + +static void +gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) +{ + GObjectClass *object_class; + GstElementClass *element_class; + GstBaseParseClass *baseparse_class; + GstRawBaseParseClass *rawbaseparse_class; + + GST_DEBUG_CATEGORY_INIT (raw_video_parse_debug, "rawvideoparse", 0, + "rawvideoparse element"); + + object_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + baseparse_class = GST_BASE_PARSE_CLASS (klass); + rawbaseparse_class = GST_RAW_BASE_PARSE_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_src_template)); + + object_class->set_property = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_set_property); + object_class->get_property = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_property); + + baseparse_class->stop = GST_DEBUG_FUNCPTR (gst_raw_video_parse_stop); + + rawbaseparse_class->set_current_config = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_set_current_config); + rawbaseparse_class->get_current_config = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_current_config); + rawbaseparse_class->set_config_from_caps = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_set_config_from_caps); + rawbaseparse_class->get_caps_from_config = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_caps_from_config); + rawbaseparse_class->get_config_frame_size = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_config_frame_size); + rawbaseparse_class->get_max_frames_per_buffer = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_max_frames_per_buffer); + rawbaseparse_class->is_config_ready = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_is_config_ready); + rawbaseparse_class->process = GST_DEBUG_FUNCPTR (gst_raw_video_parse_process); + rawbaseparse_class->is_unit_format_supported = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_is_unit_format_supported); + rawbaseparse_class->get_units_per_second = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_units_per_second); + rawbaseparse_class->get_overhead_size = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_overhead_size); + + g_object_class_install_property (object_class, + PROP_WIDTH, + g_param_spec_int ("width", + "Width", + "Width of frames in raw stream", + 0, G_MAXINT, DEFAULT_WIDTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_HEIGHT, + g_param_spec_int ("height", + "Height", + "Height of frames in raw stream", + 0, G_MAXINT, + DEFAULT_HEIGHT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_FORMAT, + g_param_spec_enum ("format", + "Format", + "Format of frames in raw stream", + GST_TYPE_VIDEO_FORMAT, + DEFAULT_FORMAT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_FRAMERATE, + gst_param_spec_fraction ("framerate", + "Frame rate", + "Rate of frames in raw stream", + 0, 1, G_MAXINT, 1, + DEFAULT_FRAMERATE_N, DEFAULT_FRAMERATE_D, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction ("pixel-aspect-ratio", + "Pixel aspect ratio", + "Pixel aspect ratio of frames in raw stream", + 1, 100, 100, 1, + DEFAULT_PIXEL_ASPECT_RATIO_N, DEFAULT_PIXEL_ASPECT_RATIO_D, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_INTERLACED, + g_param_spec_boolean ("interlaced", + "Interlaced flag", + "True if frames in raw stream are interlaced", + DEFAULT_INTERLACED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_TOP_FIELD_FIRST, + g_param_spec_boolean ("top-field-first", + "Top field first", + "True if top field in frames in raw stream come first (not used if frames aren't interlaced)", + DEFAULT_INTERLACED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_PLANE_STRIDES, + g_param_spec_value_array ("plane-strides", + "Plane strides", + "Strides of the planets in bytes", + g_param_spec_uint ("plane-stride", + "Plane stride", + "Stride of the n-th plane in bytes (0 = stride equals width*bytes-per-pixel)", + 0, G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_PLANE_OFFSETS, + g_param_spec_value_array ("plane-offsets", + "Plane offsets", + "Offsets of the planets in bytes", + g_param_spec_uint ("plane-offset", + "Plane offset", + "Offset of the n-th plane in bytes", + 0, G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + g_object_class_install_property (object_class, + PROP_FRAME_STRIDE, + g_param_spec_uint ("frame-stride", + "Frame stride", + "Stride between whole frames (0 = frames are tightly packed together)", + 0, G_MAXUINT, + DEFAULT_FRAME_STRIDE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gst_element_class_set_static_metadata (element_class, + "rawvideoparse", + "Codec/Parser/Video", + "Converts unformatted data streams into timestamped raw video frames", + "Carlos Rafael Giani "); +} + + +static void +gst_raw_video_parse_init (GstRawVideoParse * raw_video_parse) +{ + gst_raw_video_parse_init_config (&(raw_video_parse->properties_config)); + gst_raw_video_parse_init_config (&(raw_video_parse->sink_caps_config)); + + /* As required by GstRawBaseParse, ensure that the current configuration + * is initially set to be the properties config */ + raw_video_parse->current_config = &(raw_video_parse->properties_config); + + /* Properties config must be valid from the start, so set its ready value + * to TRUE, and make sure its bpf value is valid. */ + raw_video_parse->properties_config.ready = TRUE; + raw_video_parse->properties_config.top_field_first = DEFAULT_TOP_FIELD_FIRST; + raw_video_parse->properties_config.frame_stride = DEFAULT_FRAME_STRIDE; +} + + +static void +gst_raw_video_parse_set_property (GObject * object, guint prop_id, + GValue const *value, GParamSpec * pspec) +{ + GstBaseParse *base_parse = GST_BASE_PARSE (object); + GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (object); + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (object); + GstRawVideoParseConfig *props_cfg = &(raw_video_parse->properties_config); + + /* All properties are handled similarly: + * - if the new value is the same as the current value, nothing is done + * - the parser lock is held while the new value is set + * - if the properties config is the current config, the source caps are + * invalidated to ensure that the code in handle_frame pushes a new CAPS + * event out + * - properties that affect the video frame size call the function to update + * the info and also call gst_base_parse_set_min_frame_size() to ensure + * that the minimum frame size can hold 1 frame (= one sample for each + * channel); to ensure that the min frame size includes any extra padding, + * it is set to the result of gst_raw_video_parse_get_config_frame_size() + * - property configuration values that require video info updates aren't + * written directory into the video info structure, but in the extra + * fields instead (gst_raw_video_parse_update_info() then copies the values + * from these fields into the video info); see the documentation inside + * gst_raw_video_parse_update_info() for the reason why + */ + + switch (prop_id) { + case PROP_WIDTH: + { + gint new_width = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_width != props_cfg->width) { + props_cfg->width = new_width; + gst_raw_video_parse_update_info (props_cfg); + + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_HEIGHT: + { + gint new_height = g_value_get_int (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_height != props_cfg->height) { + props_cfg->height = new_height; + gst_raw_video_parse_update_info (props_cfg); + + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_FORMAT: + { + GstVideoFormat new_format = g_value_get_enum (value); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + if (new_format != props_cfg->format) { + props_cfg->format = new_format; + gst_raw_video_parse_update_info (props_cfg); + + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) { + gst_raw_base_parse_invalidate_src_caps (raw_base_parse); + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + } + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_PIXEL_ASPECT_RATIO: + { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + /* The pixel aspect ratio does not affect the video frame size, + * so it is just set directly without any updates */ + props_cfg->pixel_aspect_ratio_n = + GST_VIDEO_INFO_PAR_N (&(props_cfg->info)) = + gst_value_get_fraction_numerator (value); + props_cfg->pixel_aspect_ratio_d = + GST_VIDEO_INFO_PAR_D (&(props_cfg->info)) = + gst_value_get_fraction_denominator (value); + GST_DEBUG_OBJECT (raw_video_parse, "setting pixel aspect ratio to %u/%u", + props_cfg->pixel_aspect_ratio_n, props_cfg->pixel_aspect_ratio_d); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_FRAMERATE: + { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + /* The framerate does not affect the video frame size, + * so it is just set directly without any updates */ + props_cfg->framerate_n = GST_VIDEO_INFO_FPS_N (&(props_cfg->info)) = + gst_value_get_fraction_numerator (value); + props_cfg->framerate_d = GST_VIDEO_INFO_FPS_D (&(props_cfg->info)) = + gst_value_get_fraction_denominator (value); + GST_DEBUG_OBJECT (raw_video_parse, "setting framerate to %u/%u", + props_cfg->framerate_n, props_cfg->framerate_d); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_INTERLACED: + { + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + /* Interlacing does not affect the video frame size, + * so it is just set directly without any updates */ + props_cfg->interlaced = g_value_get_boolean (value); + GST_VIDEO_INFO_INTERLACE_MODE (&(props_cfg->info)) = + props_cfg->interlaced ? GST_VIDEO_INTERLACE_MODE_INTERLEAVED : + GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + break; + } + + case PROP_TOP_FIELD_FIRST: + { + /* The top-field-first flag is a detail related to + * interlacing, so no video info update is needed */ + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + props_cfg->top_field_first = g_value_get_boolean (value); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_PLANE_STRIDES: + { + GValueArray *valarray = g_value_get_boxed (value); + guint n_planes; + guint i; + + /* Sanity check - reject empty arrays */ + if ((valarray != NULL) && (valarray->n_values == 0)) { + GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, + ("plane strides property holds an empty array"), (NULL)); + break; + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + n_planes = GST_VIDEO_INFO_N_PLANES (&(props_cfg->info)); + + /* Check that the valarray holds the right number of values */ + if (valarray->n_values != n_planes) { + GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, + ("incorrect number of elements in plane strides property"), + ("expected: %u, got: %u", n_planes, valarray->n_values)); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + /* Copy the values to the stride array */ + for (i = 0; i < n_planes; ++i) { + GValue *val = g_value_array_get_nth (valarray, i); + props_cfg->plane_strides[i] = g_value_get_uint (val); + GST_DEBUG_OBJECT (raw_video_parse, "plane #%u stride: %d", i, + props_cfg->plane_strides[i]); + } + + gst_raw_video_parse_update_info (props_cfg); + + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_PLANE_OFFSETS: + { + GValueArray *valarray = g_value_get_boxed (value); + guint n_planes; + guint i; + + /* Sanity check - reject empty arrays */ + if ((valarray != NULL) && (valarray->n_values == 0)) { + GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, + ("plane offsets property holds an empty array"), (NULL)); + break; + } + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + n_planes = GST_VIDEO_INFO_N_PLANES (&(props_cfg->info)); + + /* Check that the valarray holds the right number of values */ + if (valarray->n_values != n_planes) { + GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, + ("incorrect number of elements in plane offsets property"), + ("expected: %u, got: %u", n_planes, valarray->n_values)); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + /* Copy the values to the offset array */ + for (i = 0; i < n_planes; ++i) { + GValue *val = g_value_array_get_nth (valarray, i); + props_cfg->plane_offsets[i] = g_value_get_uint (val); + GST_DEBUG_OBJECT (raw_video_parse, "plane #%u offset: %" G_GSIZE_FORMAT, + i, props_cfg->plane_offsets[i]); + } + + gst_raw_video_parse_update_info (props_cfg); + + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + } + + case PROP_FRAME_STRIDE: + { + /* The frame stride does not affect the video frame size, + * so it is just set directly without any updates */ + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + props_cfg->frame_stride = g_value_get_uint (value); + gst_raw_video_parse_update_info (props_cfg); + if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) + gst_base_parse_set_min_frame_size (base_parse, + gst_raw_video_parse_get_config_frame_size (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_PROPERTIES)); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_raw_video_parse_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (object); + GstRawVideoParseConfig *props_cfg = &(raw_video_parse->properties_config); + + switch (prop_id) { + case PROP_WIDTH: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, props_cfg->width); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_HEIGHT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_int (value, props_cfg->height); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_FORMAT: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_enum (value, props_cfg->format); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_PIXEL_ASPECT_RATIO: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + gst_value_set_fraction (value, props_cfg->pixel_aspect_ratio_n, + props_cfg->pixel_aspect_ratio_d); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + break; + + case PROP_FRAMERATE: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + gst_value_set_fraction (value, props_cfg->framerate_n, + props_cfg->framerate_d); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_INTERLACED: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_boolean (value, props_cfg->interlaced); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_TOP_FIELD_FIRST: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_boolean (value, props_cfg->top_field_first); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + case PROP_PLANE_STRIDES: + { + guint i, n_planes; + GValue val = G_VALUE_INIT; + GValueArray *valarray; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + n_planes = GST_VIDEO_INFO_N_PLANES (&(props_cfg->info)); + valarray = g_value_array_new (n_planes); + g_value_init (&val, G_TYPE_UINT); + + for (i = 0; i < n_planes; ++i) { + g_value_set_uint (&val, props_cfg->plane_strides[i]); + g_value_array_insert (valarray, i, &val); + } + + g_value_unset (&val); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + /* Pass on ownership to the value array, + * since we don't need it anymore */ + g_value_take_boxed (value, valarray); + break; + } + + case PROP_PLANE_OFFSETS: + { + guint i, n_planes; + GValue val = G_VALUE_INIT; + GValueArray *valarray; + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + + n_planes = GST_VIDEO_INFO_N_PLANES (&(props_cfg->info)); + valarray = g_value_array_new (n_planes); + g_value_init (&val, G_TYPE_UINT); + + for (i = 0; i < n_planes; ++i) { + g_value_set_uint (&val, props_cfg->plane_offsets[i]); + g_value_array_insert (valarray, i, &val); + } + + g_value_unset (&val); + + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + + /* Pass on ownership to the value array, + * since we don't need it anymore */ + g_value_take_boxed (value, valarray); + break; + } + + case PROP_FRAME_STRIDE: + GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (object); + g_value_set_uint (value, raw_video_parse->properties_config.frame_stride); + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (object); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +gst_raw_video_parse_stop (GstBaseParse * parse) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (parse); + + /* Sink caps config is not ready until caps come in. + * We are stopping processing, the element is being reset, + * so the config has to be un-readied. + * (Since the properties config is not depending on caps, + * its ready status is always TRUE.) */ + raw_video_parse->sink_caps_config.ready = FALSE; + + return GST_BASE_PARSE_CLASS (parent_class)->stop (parse); +} + + +static gboolean +gst_raw_video_parse_set_current_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + raw_video_parse->current_config = &(raw_video_parse->properties_config); + break; + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + raw_video_parse->current_config = &(raw_video_parse->sink_caps_config); + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + + +static GstRawBaseParseConfig +gst_raw_video_parse_get_current_config (GstRawBaseParse * raw_base_parse) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + return gst_raw_video_parse_is_using_sink_caps (raw_video_parse) ? + GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; +} + + +static gboolean +gst_raw_video_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps * caps) +{ + int i; + GstStructure *structure; + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + + g_assert (caps != NULL); + + /* Caps might get copied, and the copy needs to be unref'd. + * Also, the caller retains ownership over the original caps. + * So, to make this mechanism also work with cases where the + * caps are *not* copied, ref the original caps here first. */ + gst_caps_ref (caps); + + structure = gst_caps_get_structure (caps, 0); + + /* For unaligned raw data, the output caps stay the same, + * except that video/x-unaligned-raw becomes video/x-raw, + * since the parser aligns the frame data */ + if (gst_structure_has_name (structure, "video/x-unaligned-raw")) { + /* Copy the caps to be able to modify them */ + GstCaps *new_caps = gst_caps_copy (caps); + gst_caps_unref (caps); + caps = new_caps; + + /* Change the media type to video/x-raw , otherwise + * gst_video_info_from_caps() won't work */ + structure = gst_caps_get_structure (caps, 0); + gst_structure_set_name (structure, "video/x-raw"); + } + + config_ptr->ready = gst_video_info_from_caps (&(config_ptr->info), caps); + + if (config_ptr->ready) { + config_ptr->width = GST_VIDEO_INFO_WIDTH (&(config_ptr->info)); + config_ptr->height = GST_VIDEO_INFO_HEIGHT (&(config_ptr->info)); + config_ptr->pixel_aspect_ratio_n = + GST_VIDEO_INFO_PAR_N (&(config_ptr->info)); + config_ptr->pixel_aspect_ratio_d = + GST_VIDEO_INFO_PAR_D (&(config_ptr->info)); + config_ptr->framerate_n = GST_VIDEO_INFO_FPS_N (&(config_ptr->info)); + config_ptr->framerate_d = GST_VIDEO_INFO_FPS_D (&(config_ptr->info)); + config_ptr->interlaced = GST_VIDEO_INFO_IS_INTERLACED (&(config_ptr->info)); + config_ptr->height = GST_VIDEO_INFO_HEIGHT (&(config_ptr->info)); + config_ptr->top_field_first = 0; + config_ptr->frame_stride = 0; + + for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { + config_ptr->plane_offsets[i] = + GST_VIDEO_INFO_PLANE_OFFSET (&(config_ptr->info), i); + config_ptr->plane_strides[i] = + GST_VIDEO_INFO_PLANE_STRIDE (&(config_ptr->info), i); + } + } + + gst_caps_unref (caps); + + return config_ptr->ready; +} + + +static gboolean +gst_raw_video_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstCaps ** caps) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + + g_assert (caps != NULL); + + *caps = gst_video_info_to_caps (&(config_ptr->info)); + + return *caps != NULL; +} + + +static gsize +gst_raw_video_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + return MAX (GST_VIDEO_INFO_SIZE (&(config_ptr->info)), + (gsize) (config_ptr->frame_stride)); +} + + +static guint +gst_raw_video_parse_get_max_frames_per_buffer (G_GNUC_UNUSED GstRawBaseParse * + raw_base_parse, G_GNUC_UNUSED GstRawBaseParseConfig config) +{ + /* We want exactly one frame per buffer */ + return 1; +} + + +static gboolean +gst_raw_video_parse_is_config_ready (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + return gst_raw_video_parse_get_config_ptr (raw_video_parse, config)->ready; +} + + +static gboolean +gst_raw_video_parse_process (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config, GstBuffer * in_data, + G_GNUC_UNUSED gsize total_num_in_bytes, + G_GNUC_UNUSED gsize num_valid_in_bytes, GstBuffer ** processed_data) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + guint frame_flags = 0; + GstVideoInfo *video_info = &(config_ptr->info); + GstVideoMeta *videometa; + GstBuffer *out_data; + + /* In case of extra padding bytes, get a subbuffer without the padding bytes. + * Otherwise, just add the video meta. */ + if (GST_VIDEO_INFO_SIZE (video_info) < config_ptr->frame_stride) { + *processed_data = out_data = + gst_buffer_copy_region (in_data, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | + GST_BUFFER_COPY_MEMORY, 0, GST_VIDEO_INFO_SIZE (video_info)); + } else { + out_data = in_data; + *processed_data = NULL; + } + + if (config_ptr->interlaced) { + GST_BUFFER_FLAG_SET (out_data, GST_VIDEO_BUFFER_FLAG_INTERLACED); + frame_flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; + + if (config_ptr->top_field_first) { + GST_BUFFER_FLAG_SET (out_data, GST_VIDEO_BUFFER_FLAG_TFF); + frame_flags |= GST_VIDEO_FRAME_FLAG_TFF; + } else + GST_BUFFER_FLAG_UNSET (out_data, GST_VIDEO_BUFFER_FLAG_TFF); + } + + /* Remove any existing videometa - it will be replaced by the new videometa + * from here */ + while ((videometa = gst_buffer_get_video_meta (out_data))) { + GST_LOG_OBJECT (raw_base_parse, "removing existing videometa from buffer"); + gst_buffer_remove_meta (out_data, (GstMeta *) videometa); + } + + gst_buffer_add_video_meta_full (out_data, + frame_flags, + config_ptr->format, + config_ptr->width, + config_ptr->height, + GST_VIDEO_INFO_N_PLANES (video_info), + config_ptr->plane_offsets, config_ptr->plane_strides); + + + return TRUE; +} + + +static gboolean +gst_raw_video_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * + raw_base_parse, GstFormat format) +{ + switch (format) { + case GST_FORMAT_BYTES: + case GST_FORMAT_DEFAULT: + return TRUE; + default: + return FALSE; + } +} + + +static void +gst_raw_video_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, + GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, + gsize * units_per_sec_d) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + + switch (format) { + case GST_FORMAT_BYTES: + { + gsize framesize = GST_VIDEO_INFO_SIZE (&(config_ptr->info)); + gint64 n = framesize * config_ptr->framerate_n; + gint64 d = config_ptr->framerate_d; + gint64 common_div = gst_util_greatest_common_divisor_int64 (n, d); + GST_DEBUG_OBJECT (raw_video_parse, + "n: %" G_GINT64_FORMAT " d: %" G_GINT64_FORMAT " common divisor: %" + G_GINT64_FORMAT, n, d, common_div); + + /* Divide numerator and denominator by greatest common divisor. + * This minimizes the risk of integer overflows in the baseparse class. */ + *units_per_sec_n = n / common_div; + *units_per_sec_d = d / common_div; + + break; + } + + case GST_FORMAT_DEFAULT: + { + *units_per_sec_n = config_ptr->framerate_n; + *units_per_sec_d = config_ptr->framerate_d; + break; + } + + default: + g_assert_not_reached (); + } +} + + +static gint +gst_raw_video_parse_get_overhead_size (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); + GstRawVideoParseConfig *config_ptr = + gst_raw_video_parse_get_config_ptr (raw_video_parse, config); + gint64 frame_size = GST_VIDEO_INFO_SIZE (&(config_ptr->info)); + gint64 frame_stride = config_ptr->frame_stride; + + /* In the video parser, the overhead is defined by the difference between + * the frame stride and the actual frame size. If the former is larger, + * then the additional bytes are considered padding bytes and get ignored + * by the base class. */ + + GST_LOG_OBJECT (raw_video_parse, + "frame size: %" G_GINT64_FORMAT " frame stride: %" G_GINT64_FORMAT, + frame_size, frame_stride); + + return (frame_size < frame_stride) ? (gint) (frame_stride - frame_size) : 0; +} + + +static gboolean +gst_raw_video_parse_is_using_sink_caps (GstRawVideoParse * raw_video_parse) +{ + return raw_video_parse->current_config == + &(raw_video_parse->sink_caps_config); +} + + +static GstRawVideoParseConfig * +gst_raw_video_parse_get_config_ptr (GstRawVideoParse * raw_video_parse, + GstRawBaseParseConfig config) +{ + g_assert (raw_video_parse->current_config != NULL); + + switch (config) { + case GST_RAW_BASE_PARSE_CONFIG_PROPERTIES: + return &(raw_video_parse->properties_config); + + case GST_RAW_BASE_PARSE_CONFIG_SINKCAPS: + return &(raw_video_parse->sink_caps_config); + + default: + g_assert (raw_video_parse->current_config != NULL); + return raw_video_parse->current_config; + } +} + + +static void +gst_raw_video_parse_init_config (GstRawVideoParseConfig * config) +{ + int i; + + config->ready = FALSE; + config->width = DEFAULT_WIDTH; + config->height = DEFAULT_HEIGHT; + config->format = DEFAULT_FORMAT; + config->pixel_aspect_ratio_n = DEFAULT_PIXEL_ASPECT_RATIO_N; + config->pixel_aspect_ratio_d = DEFAULT_PIXEL_ASPECT_RATIO_D; + config->framerate_n = DEFAULT_FRAMERATE_N; + config->framerate_d = DEFAULT_FRAMERATE_D; + config->interlaced = DEFAULT_INTERLACED; + + config->top_field_first = DEFAULT_TOP_FIELD_FIRST; + config->frame_stride = DEFAULT_FRAME_STRIDE; + + gst_video_info_set_format (&(config->info), DEFAULT_FORMAT, DEFAULT_WIDTH, + DEFAULT_HEIGHT); + for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { + config->plane_offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (&(config->info), i); + config->plane_strides[i] = GST_VIDEO_INFO_PLANE_STRIDE (&(config->info), i); + } +} + + +static void +gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) +{ + int i; + guint n_planes; + gsize last_plane_offset, last_plane_size; + GstVideoInfo *info = &(config->info); + + gst_video_info_set_format (info, config->format, config->width, + config->height); + + GST_VIDEO_INFO_PAR_N (info) = config->pixel_aspect_ratio_n; + GST_VIDEO_INFO_PAR_D (info) = config->pixel_aspect_ratio_d; + GST_VIDEO_INFO_FPS_N (info) = config->framerate_n; + GST_VIDEO_INFO_FPS_D (info) = config->framerate_d; + GST_VIDEO_INFO_INTERLACE_MODE (info) = + config->interlaced ? GST_VIDEO_INTERLACE_MODE_INTERLEAVED : + GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { + GST_VIDEO_INFO_PLANE_OFFSET (info, i) = config->plane_offsets[i]; + GST_VIDEO_INFO_PLANE_STRIDE (info, i) = config->plane_strides[i]; + } + + n_planes = GST_VIDEO_INFO_N_PLANES (info); + if (n_planes < 1) + n_planes = 1; + + last_plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, n_planes - 1); + last_plane_size = + GST_VIDEO_INFO_PLANE_STRIDE (info, + n_planes - 1) * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, + n_planes - 1, config->height); + + GST_VIDEO_INFO_SIZE (info) = last_plane_offset + last_plane_size; + + GST_DEBUG ("last plane offset: %" G_GSIZE_FORMAT " last plane size: %" + G_GSIZE_FORMAT " => frame size minus extra padding: %" G_GSIZE_FORMAT, + last_plane_offset, last_plane_size, GST_VIDEO_INFO_SIZE (info)); +} diff --git a/gst/rawparse/gstrawvideoparse.h b/gst/rawparse/gstrawvideoparse.h new file mode 100644 index 0000000000..fea395598c --- /dev/null +++ b/gst/rawparse/gstrawvideoparse.h @@ -0,0 +1,116 @@ +/* GStreamer + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RAW_VIDEO_PARSE_H__ +#define __GST_RAW_VIDEO_PARSE_H__ + +#include +#include +#include "gstrawbaseparse.h" + +G_BEGIN_DECLS + +#define GST_TYPE_RAW_VIDEO_PARSE \ + (gst_raw_video_parse_get_type()) +#define GST_RAW_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RAW_VIDEO_PARSE, GstRawVideoParse)) +#define GST_RAW_VIDEO_PARSE_CAST(obj) \ + ((GstRawVideoParse *)(obj)) +#define GST_RAW_VIDEO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RAW_VIDEO_PARSE, GstRawVideoParseClass)) +#define GST_IS_RAW_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RAW_VIDEO_PARSE)) +#define GST_IS_RAW_VIDEO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_VIDEO_PARSE)) + + +typedef struct _GstRawVideoParseConfig GstRawVideoParseConfig; +typedef struct _GstRawVideoParse GstRawVideoParse; +typedef struct _GstRawVideoParseClass GstRawVideoParseClass; + + +/* Contains information about the video frame format. */ +struct _GstRawVideoParseConfig +{ + /* If TRUE, then this configuration is ready to use */ + gboolean ready; + + /* FIXME: These values should not be necessary, since there's + * GstVideoInfo. However, setting these values in the video + * info independently is currently difficult. For example, + * setting the video format requires the gst_video_info_set_format() + * function, but this function also overwrites plane strides + * and offsets. */ + gint width, height; + GstVideoFormat format; + gint pixel_aspect_ratio_n, pixel_aspect_ratio_d; + gint framerate_n, framerate_d; + gboolean interlaced; + gsize plane_offsets[GST_VIDEO_MAX_PLANES]; + gint plane_strides[GST_VIDEO_MAX_PLANES]; + + /* If TRUE, then TFF flags are added to outgoing buffers and + * their video metadata */ + gboolean top_field_first; + + /* Distance between the start of each frame, in bytes. If this value + * is larger than the actual size of a frame, then the extra bytes + * are skipped. For example, with frames that have 115200 bytes, a + * frame_stride value of 120000 means that 4800 trailing bytes are + * skipped after the 115200 frame bytes. This is useful to skip + * metadata in between frames. */ + guint frame_stride; + + GstVideoInfo info; +}; + + +struct _GstRawVideoParse +{ + GstRawBaseParse parent; + + /*< private > */ + + /* Configuration controlled by the object properties. Its ready value + * is set to TRUE from the start, so it can be used right away. + */ + GstRawVideoParseConfig properties_config; + /* Configuration controlled by the sink caps. Its ready value is + * initially set to FALSE until valid sink caps come in. It is set to + * FALSE again when the stream-start event is observed. + */ + GstRawVideoParseConfig sink_caps_config; + /* Currently active configuration. Points either to properties_config + * or to sink_caps_config. This is never NULL. */ + GstRawVideoParseConfig *current_config; +}; + + +struct _GstRawVideoParseClass +{ + GstRawBaseParseClass parent_class; +}; + + +GType gst_raw_video_parse_get_type (void); + + +G_END_DECLS + +#endif diff --git a/gst/rawparse/gstunalignedaudioparse.c b/gst/rawparse/gstunalignedaudioparse.c index 62a83f3638..786f4f4441 100644 --- a/gst/rawparse/gstunalignedaudioparse.c +++ b/gst/rawparse/gstunalignedaudioparse.c @@ -96,7 +96,7 @@ gst_unaligned_audio_parse_init (GstUnalignedAudioParse * unaligned_audio_parse) GstPad *ghostpad; unaligned_audio_parse->inner_parser = - gst_element_factory_make ("audioparse", "inner_parser"); + gst_element_factory_make ("rawaudioaudioparse", "inner_parser"); g_assert (unaligned_audio_parse->inner_parser != NULL); g_object_set (G_OBJECT (unaligned_audio_parse->inner_parser), diff --git a/gst/rawparse/gstunalignedvideoparse.c b/gst/rawparse/gstunalignedvideoparse.c new file mode 100644 index 0000000000..d6e8cf36eb --- /dev/null +++ b/gst/rawparse/gstunalignedvideoparse.c @@ -0,0 +1,124 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * gstunalignedvideoparse.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include "gstunalignedvideoparse.h" +#include "unalignedvideo.h" + + +GST_DEBUG_CATEGORY (unaligned_video_parse_debug); +#define GST_CAT_DEFAULT unaligned_video_parse_debug + + +struct _GstUnalignedVideoParse +{ + GstBin parent; + GstElement *inner_parser; +}; + + +struct _GstUnalignedVideoParseClass +{ + GstBinClass parent_class; +}; + + +static GstStaticPadTemplate static_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_UNALIGNED_RAW_VIDEO_CAPS) + ); + + +static GstStaticPadTemplate static_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) + ); + + + + +G_DEFINE_TYPE (GstUnalignedVideoParse, gst_unaligned_video_parse, GST_TYPE_BIN); + + +static void +gst_unaligned_video_parse_class_init (GstUnalignedVideoParseClass * klass) +{ + GstElementClass *element_class; + + GST_DEBUG_CATEGORY_INIT (unaligned_video_parse_debug, "unalignedvideoparse", + 0, "Unaligned raw video parser"); + + element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&static_src_template)); + + gst_element_class_set_static_metadata (element_class, + "unalignedvideoparse", + "Codec/Parser/Bin/Video", + "Parse unaligned raw video data", + "Carlos Rafael Giani "); +} + + +static void +gst_unaligned_video_parse_init (GstUnalignedVideoParse * unaligned_video_parse) +{ + GstPad *inner_pad; + GstPad *ghostpad; + + unaligned_video_parse->inner_parser = + gst_element_factory_make ("rawvideoparse", "inner_parser"); + g_assert (unaligned_video_parse->inner_parser != NULL); + + g_object_set (G_OBJECT (unaligned_video_parse->inner_parser), + "use-sink-caps", TRUE, NULL); + + gst_bin_add (GST_BIN (unaligned_video_parse), + unaligned_video_parse->inner_parser); + + inner_pad = + gst_element_get_static_pad (unaligned_video_parse->inner_parser, "sink"); + ghostpad = + gst_ghost_pad_new_from_template ("sink", inner_pad, + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS + (unaligned_video_parse), "sink")); + gst_element_add_pad (GST_ELEMENT (unaligned_video_parse), ghostpad); + gst_object_unref (GST_OBJECT (inner_pad)); + + inner_pad = gst_element_get_static_pad (unaligned_video_parse->inner_parser, + "src"); + ghostpad = + gst_ghost_pad_new_from_template ("src", inner_pad, + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS + (unaligned_video_parse), "src")); + gst_element_add_pad (GST_ELEMENT (unaligned_video_parse), ghostpad); + gst_object_unref (GST_OBJECT (inner_pad)); +} diff --git a/gst/rawparse/gstunalignedvideoparse.h b/gst/rawparse/gstunalignedvideoparse.h new file mode 100644 index 0000000000..da40ac9421 --- /dev/null +++ b/gst/rawparse/gstunalignedvideoparse.h @@ -0,0 +1,49 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * gstunalignedvideoparse.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_UNALIGNED_VIDEO_PARSE_H___ +#define __GST_UNALIGNED_VIDEO_PARSE_H___ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_UNALIGNED_VIDEO_PARSE \ + (gst_unaligned_video_parse_get_type()) +#define GST_UNALIGNED_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_UNALIGNED_VIDEO_PARSE, GstUnalignedVideoParse)) +#define GST_UNALIGNED_VIDEO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_UNALIGNED_VIDEO_PARSE, GstUnalignedVideoParseClass)) +#define GST_UNALIGNED_VIDEO_PARSE_CAST(obj) \ + ((GstUnalignedVideoParse *)(obj)) +#define GST_IS_UNALIGNED_VIDEO_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_UNALIGNED_VIDEO_PARSE)) +#define GST_IS_UNALIGNED_VIDEO_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_UNALIGNED_VIDEO_PARSE)) + +typedef struct _GstUnalignedVideoParse GstUnalignedVideoParse; +typedef struct _GstUnalignedVideoParseClass GstUnalignedVideoParseClass; + +GType gst_unaligned_video_parse_get_type (void); + +G_END_DECLS + +#endif /* __GST_UNALIGNED_VIDEO_PARSE_H___ */ diff --git a/gst/rawparse/plugin.c b/gst/rawparse/plugin.c index bb21f77b15..72b3e49ea0 100644 --- a/gst/rawparse/plugin.c +++ b/gst/rawparse/plugin.c @@ -3,7 +3,10 @@ #endif #include +#include "gstrawaudioparse.h" +#include "gstrawvideoparse.h" #include "gstunalignedaudioparse.h" +#include "gstunalignedvideoparse.h" #include "gstaudioparse.h" #include "gstvideoparse.h" @@ -18,6 +21,12 @@ plugin_init (GstPlugin * plugin) gst_audio_parse_get_type ()); ret &= gst_element_register (plugin, "unalignedaudioparse", GST_RANK_MARGINAL, gst_unaligned_audio_parse_get_type ()); + ret &= gst_element_register (plugin, "unalignedvideoparse", GST_RANK_MARGINAL, + gst_unaligned_video_parse_get_type ()); + ret &= gst_element_register (plugin, "rawaudioparse", GST_RANK_NONE, + gst_raw_audio_parse_get_type ()); + ret &= gst_element_register (plugin, "rawvideoparse", GST_RANK_NONE, + gst_raw_video_parse_get_type ()); return ret; } diff --git a/gst/rawparse/unalignedvideo.h b/gst/rawparse/unalignedvideo.h new file mode 100644 index 0000000000..a5c1005322 --- /dev/null +++ b/gst/rawparse/unalignedvideo.h @@ -0,0 +1,35 @@ +/* GStreamer + * Copyright (C) 2016 Carlos Rafael Giani + * + * unalignedvideo.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_UNALIGNED_VIDEO_H__ +#define __GST_UNALIGNED_VIDEO_H__ + +#include +#include + +#define GST_UNALIGNED_RAW_VIDEO_CAPS \ + "video/x-unaligned-raw" \ + ", format = (string) " GST_VIDEO_FORMATS_ALL \ + ", width = " GST_VIDEO_SIZE_RANGE \ + ", height = " GST_VIDEO_SIZE_RANGE \ + ", framerate = " GST_VIDEO_FPS_RANGE + +#endif /* __GST_UNALIGNED_VIDEO_H__ */ diff --git a/tests/check/elements/rawaudioparse.c b/tests/check/elements/rawaudioparse.c new file mode 100644 index 0000000000..62ab975e8e --- /dev/null +++ b/tests/check/elements/rawaudioparse.c @@ -0,0 +1,339 @@ +/* GStreamer + * + * unit test for rawaudioparse + * + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative + * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include + +/* Checks are hardcoded to expect stereo 16-bit data. The sample rate + * however varies from the default of 40 kHz in some tests to see the + * differences in calculated buffer durations. */ +#define NUM_TEST_SAMPLES 512 +#define NUM_TEST_CHANNELS 2 +#define TEST_SAMPLE_RATE 40000 +#define TEST_SAMPLE_FORMAT GST_AUDIO_FORMAT_S16 + +/* For ease of programming we use globals to keep refs for our floating + * src and sink pads we create; otherwise we always have to do get_pad, + * get_peer, and then remove references in every test function */ +static GstPad *mysrcpad, *mysinkpad; + +typedef struct +{ + GstElement *rawaudioparse; + GstAdapter *test_data_adapter; +} +RawAudParseTestCtx; + +/* Sets up a rawaudioparse element and a GstAdapter that contains 512 test + * audio samples. The samples a monotonically increasing set from the values + * 0 to 511 for the left and 512 to 1023 for the right channel. The result + * is a GstAdapter that contains the interleaved 16-bit integer values: + * 0,512,1,513,2,514, ... 511,1023 . This set is used in the checks to see + * if rawaudioparse's output buffers contain valid data. */ +static void +setup_rawaudioparse (RawAudParseTestCtx * testctx, gboolean use_sink_caps, + gboolean set_properties, GstCaps * incaps, GstFormat format) +{ + GstElement *rawaudioparse; + GstAdapter *test_data_adapter; + GstBuffer *buffer; + guint i; + guint16 samples[NUM_TEST_SAMPLES * NUM_TEST_CHANNELS]; + + + /* Setup the rawaudioparse element and the pads */ + + static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)) + ); + static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + + rawaudioparse = gst_check_setup_element ("rawaudioparse"); + + g_object_set (G_OBJECT (rawaudioparse), "use-sink-caps", use_sink_caps, NULL); + if (set_properties) + g_object_set (G_OBJECT (rawaudioparse), "sample-rate", TEST_SAMPLE_RATE, + "num-channels", NUM_TEST_CHANNELS, "pcm-format", TEST_SAMPLE_FORMAT, + NULL); + + fail_unless (gst_element_set_state (rawaudioparse, + GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS, + "could not set to paused"); + + mysrcpad = gst_check_setup_src_pad (rawaudioparse, &srctemplate); + mysinkpad = gst_check_setup_sink_pad (rawaudioparse, &sinktemplate); + + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + + gst_check_setup_events (mysrcpad, rawaudioparse, incaps, format); + if (incaps) + gst_caps_unref (incaps); + + + /* Fill the adapter with the interleaved 0..511 and + * 512..1023 samples */ + for (i = 0; i < NUM_TEST_SAMPLES; ++i) { + guint c; + for (c = 0; c < NUM_TEST_CHANNELS; ++c) + samples[i * NUM_TEST_CHANNELS + c] = c * NUM_TEST_SAMPLES + i; + } + + test_data_adapter = gst_adapter_new (); + buffer = gst_buffer_new_allocate (NULL, sizeof (samples), NULL); + gst_buffer_fill (buffer, 0, samples, sizeof (samples)); + gst_adapter_push (test_data_adapter, buffer); + + + testctx->rawaudioparse = rawaudioparse; + testctx->test_data_adapter = test_data_adapter; +} + +static void +cleanup_rawaudioparse (RawAudParseTestCtx * testctx) +{ + gst_pad_set_active (mysrcpad, FALSE); + gst_pad_set_active (mysinkpad, FALSE); + gst_check_teardown_src_pad (testctx->rawaudioparse); + gst_check_teardown_sink_pad (testctx->rawaudioparse); + gst_check_teardown_element (testctx->rawaudioparse); + + g_object_unref (G_OBJECT (testctx->test_data_adapter)); +} + + +static void +push_data_and_check_output (RawAudParseTestCtx * testctx, gsize num_in_bytes, + gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur, + guint expected_num_buffers_in_list, guint bpf, guint16 channel0_start, + guint16 channel1_start) +{ + GstBuffer *inbuf, *outbuf; + guint num_buffers; + + /* Simulate upstream input by taking num_in_bytes bytes from the adapter */ + inbuf = gst_adapter_take_buffer (testctx->test_data_adapter, num_in_bytes); + fail_unless (inbuf != NULL); + + /* Push the input data and check that the output buffers list grew as + * expected */ + fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); + num_buffers = g_list_length (buffers); + fail_unless_equals_int (num_buffers, expected_num_buffers_in_list); + + /* Take the latest output buffer */ + outbuf = g_list_nth_data (buffers, num_buffers - 1); + fail_unless (outbuf != NULL); + + /* Verify size, PTS, duration of the output buffer */ + fail_unless_equals_uint64 (expected_num_out_bytes, + gst_buffer_get_size (outbuf)); + fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf)); + fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf)); + + /* Go through all of the samples in the output buffer and check that they are + * valid. The samples are interleaved. The offsets specified by channel0_start + * and channel1_start are the expected values of the first sample for each + * channel in the buffer. So, if channel0_start is 512, then sample #0 in the + * buffer must have value 512, and if channel1_start is 700, then sample #1 + * in the buffer must have value 700 etc. */ + { + guint i, num_frames; + guint16 *s; + GstMapInfo map_info; + guint channel_starts[2] = { channel0_start, channel1_start }; + + gst_buffer_map (outbuf, &map_info, GST_MAP_READ); + num_frames = map_info.size / bpf; + s = (guint16 *) (map_info.data); + + for (i = 0; i < num_frames; ++i) { + guint c; + + for (c = 0; i < NUM_TEST_CHANNELS; ++i) { + guint16 expected = channel_starts[c] + i; + guint16 actual = s[i * NUM_TEST_CHANNELS + c]; + + fail_unless_equals_int (expected, actual); + } + } + + gst_buffer_unmap (outbuf, &map_info); + } +} + + +GST_START_TEST (test_push_unaligned_data_properties_config) +{ + RawAudParseTestCtx testctx; + + setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES); + + /* Send in data buffers that are not aligned to multiples of the + * frame size (= sample size * num_channels). This tests if rawaudioparse + * aligns output data properly. + * + * The second line sends in 99 bytes, and expects 100 bytes in the + * output buffer. This is because the first buffer contains 45 bytes, + * and rawaudioparse is expected to output 44 bytes (which is an integer + * multiple of the frame size). The leftover 1 byte then gets prepended + * to the input buffer with 99 bytes, resulting in 100 bytes, which is + * an integer multiple of the frame size. + */ + + push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0, + GST_USECOND * 275, 1, 4, 0, 512); + push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275, + GST_USECOND * 625, 2, 4, 11, 523); + push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900, + GST_USECOND * 100, 3, 4, 36, 548); + + cleanup_rawaudioparse (&testctx); +} + +GST_END_TEST; + +GST_START_TEST (test_push_unaligned_data_sink_caps_config) +{ + RawAudParseTestCtx testctx; + GstAudioInfo ainfo; + GstCaps *caps; + + /* This test is essentially the same as test_push_unaligned_data_properties_config, + * except that rawaudioparse uses the sink caps config instead of the property config. */ + + gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE, + NUM_TEST_CHANNELS, NULL); + caps = gst_audio_info_to_caps (&ainfo); + + setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES); + + push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0, + GST_USECOND * 275, 1, 4, 0, 512); + push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275, + GST_USECOND * 625, 2, 4, 11, 523); + push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900, + GST_USECOND * 100, 3, 4, 36, 548); + + cleanup_rawaudioparse (&testctx); +} + +GST_END_TEST; + +GST_START_TEST (test_push_swapped_channels) +{ + RawAudParseTestCtx testctx; + GValueArray *valarray; + GValue val = G_VALUE_INIT; + + /* Send in 40 bytes and use a nonstandard channel order (left and right channels + * swapped). Expected behavior is for rawaudioparse to reorder the samples inside + * output buffers to conform to the GStreamer channel order. For this reason, + * channel0 offset is 512 and channel1 offset is 0 in the check below. */ + + setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES); + + valarray = g_value_array_new (2); + g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION); + g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT); + g_value_array_insert (valarray, 0, &val); + g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT); + g_value_array_insert (valarray, 1, &val); + g_object_set (G_OBJECT (testctx.rawaudioparse), "channel-positions", + valarray, NULL); + g_value_array_free (valarray); + g_value_unset (&val); + + push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0, + GST_USECOND * 250, 1, 4, 512, 0); + + cleanup_rawaudioparse (&testctx); +} + +GST_END_TEST; + +GST_START_TEST (test_config_switch) +{ + RawAudParseTestCtx testctx; + GstAudioInfo ainfo; + GstCaps *caps; + + /* Start processing with the properties config active, then mid-stream switch to + * the sink caps config. The properties config is altered to have a different + * sample rate than the sink caps to be able to detect the switch. The net effect + * is that output buffer durations are altered. For example, 40 bytes equal + * 10 samples, and this equals 500 us with 20 kHz or 250 us with 40 kHz. */ + + gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE, + NUM_TEST_CHANNELS, NULL); + caps = gst_audio_info_to_caps (&ainfo); + + setup_rawaudioparse (&testctx, FALSE, TRUE, caps, GST_FORMAT_BYTES); + + g_object_set (G_OBJECT (testctx.rawaudioparse), "sample-rate", 20000, NULL); + + /* Push in data with properties config active, expecting duration calculations + * to be based on the 20 kHz sample rate */ + push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0, + GST_USECOND * 500, 1, 4, 0, 512); + push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500, + GST_USECOND * 250, 2, 4, 10, 522); + + /* Perform the switch */ + g_object_set (G_OBJECT (testctx.rawaudioparse), "use-sink-caps", TRUE, NULL); + + /* Push in data with sink caps config active, expecting duration calculations + * to be based on the 40 kHz sample rate */ + push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750, + GST_USECOND * 250, 3, 4, 15, 527); + + cleanup_rawaudioparse (&testctx); +} + +GST_END_TEST; + + +static Suite * +rawaudioparse_suite (void) +{ + Suite *s = suite_create ("rawaudioparse"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_push_unaligned_data_properties_config); + tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config); + tcase_add_test (tc_chain, test_push_swapped_channels); + tcase_add_test (tc_chain, test_config_switch); + + return s; +} + +GST_CHECK_MAIN (rawaudioparse); diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c new file mode 100644 index 0000000000..b071370dba --- /dev/null +++ b/tests/check/elements/rawvideoparse.c @@ -0,0 +1,459 @@ +/* GStreamer + * + * unit test for rawvideoparse + * + * Copyright (C) <2016> Carlos Rafael Giani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative + * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include +#include + +/* The checks use as test data an 8x8 Y444 image, with 25 Hz framerate. In the + * sink caps configuration, the stride is 8 bytes, and the frames are tightly + * packed together. In the properties configuration, the stride is 10 bytes, the + * planes aren't tightly packed (there are 20 bytes between the planes), and the + * frames overall have padding between them (the overall frame size is + * stride (10) * height (8) * num-planes (3) + bytes-between-planes (20) * 2 + * = 280 bytes, and the frame stride is 500 bytes, so there are 220 bytes of + * extra padding between frames). + * + * In the test 8x8 frame, the pixels are all set to #000000, except for two + * pixels: (xofs+1 yofs+0) is set to #8899AA, (xofs+0 yofs+1) is set to #112233. + * The first frame uses the offsets xofs=0 yofs=0. The second frame uses + * xofs=1 yofs=0 etc. For each configuration, there is a separate set of frames, + * each stored in the GstAdapter in the Context struct. + * + * During the tests, as part of the checks, the pixels are verified to have the + * right values. The pattern of the pixels was chosen to easily detect stride + * errors, incorrect plane offsets etc. + */ + +#define TEST_WIDTH 8 +#define TEST_HEIGHT 8 +#define TEST_FRAMERATE_N 25 +#define TEST_FRAMERATE_D 1 +#define TEST_FRAME_FORMAT GST_VIDEO_FORMAT_Y444 +#define NUM_TEST_PLANES 3 + +#define PROP_CTX_PLANE_STRIDE 10 +#define PROP_CTX_FRAME_STRIDE 500 +#define PROP_CTX_PLANE_PADDING 20 +#define PROP_CTX_PLANE_SIZE (PROP_CTX_PLANE_STRIDE * TEST_HEIGHT + PROP_CTX_PLANE_PADDING) + +GstElement *rawvideoparse; + +/* For ease of programming we use globals to keep refs for our floating + * src and sink pads we create; otherwise we always have to do get_pad, + * get_peer, and then remove references in every test function */ +static GstPad *mysrcpad, *mysinkpad; + +typedef struct +{ + GstAdapter *data; + guint plane_stride; + guint plane_size; +} +Context; + +static Context properties_ctx, sinkcaps_ctx; + +static void +set_pixel (Context const *ctx, guint8 * pixels, guint x, guint y, guint32 color) +{ + guint i; + guint ofs = y * ctx->plane_stride + x; + for (i = 0; i < NUM_TEST_PLANES; ++i) + pixels[ctx->plane_size * i + ofs] = + (color >> ((NUM_TEST_PLANES - 1 - i) * 8)) & 0xFF; +} + +static guint32 +get_pixel (Context const *ctx, const guint8 * pixels, guint x, guint y) +{ + guint i; + guint ofs = y * ctx->plane_stride + x; + guint32 color = 0; + for (i = 0; i < NUM_TEST_PLANES; ++i) + color |= + ((guint32) (pixels[ctx->plane_size * i + ofs])) << ((NUM_TEST_PLANES - + 1 - i) * 8); + return color; +} + +static void +fill_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs, + guint yofs) +{ + guint8 *pixels; + GstMapInfo map_info; + + gst_buffer_map (buffer, &map_info, GST_MAP_WRITE); + pixels = map_info.data; + + memset (pixels, 0, ctx->plane_size * NUM_TEST_PLANES); + set_pixel (ctx, pixels, 1 + xofs, 0 + yofs, 0x8899AA); + set_pixel (ctx, pixels, 0 + xofs, 1 + yofs, 0x112233); + + gst_buffer_unmap (buffer, &map_info); +} + +static void +check_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs, + guint yofs) +{ + guint x, y; + guint8 *pixels; + GstMapInfo map_info; + + gst_buffer_map (buffer, &map_info, GST_MAP_READ); + pixels = map_info.data; + + fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 1 + xofs, 0 + yofs), + 0x8899AA); + fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 0 + xofs, 1 + yofs), + 0x112233); + + for (y = 0; y < TEST_HEIGHT; ++y) { + for (x = 0; x < TEST_WIDTH; ++x) { + if ((x == (1 + xofs) && y == (0 + yofs)) || (x == (0 + xofs) + && y == (1 + yofs))) + continue; + + fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, x, y), 0x000000); + } + } + + gst_buffer_unmap (buffer, &map_info); +} + + +static void +setup_rawvideoparse (gboolean use_sink_caps, + gboolean set_properties, GstCaps * incaps, GstFormat format) +{ + guint i; + + + /* Setup the rawvideoparse element and the pads */ + + static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) + ); + static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + + rawvideoparse = gst_check_setup_element ("rawvideoparse"); + + properties_ctx.plane_stride = PROP_CTX_PLANE_STRIDE; + properties_ctx.plane_size = PROP_CTX_PLANE_SIZE; + properties_ctx.data = gst_adapter_new (); + + sinkcaps_ctx.plane_stride = TEST_WIDTH; + sinkcaps_ctx.plane_size = TEST_WIDTH * TEST_HEIGHT; + sinkcaps_ctx.data = gst_adapter_new (); + + g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", use_sink_caps, NULL); + if (set_properties) { + GValueArray *plane_offsets, *plane_strides; + GValue val = G_VALUE_INIT; + + g_value_init (&val, G_TYPE_UINT); + + plane_offsets = g_value_array_new (NUM_TEST_PLANES); + for (i = 0; i < NUM_TEST_PLANES; ++i) { + g_value_set_uint (&val, properties_ctx.plane_size * i); + g_value_array_insert (plane_offsets, i, &val); + } + + plane_strides = g_value_array_new (NUM_TEST_PLANES); + for (i = 0; i < NUM_TEST_PLANES; ++i) { + g_value_set_uint (&val, properties_ctx.plane_stride); + g_value_array_insert (plane_strides, i, &val); + } + + g_value_unset (&val); + + g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH, "height", + TEST_HEIGHT, "frame-stride", PROP_CTX_FRAME_STRIDE, "framerate", + TEST_FRAMERATE_N, TEST_FRAMERATE_D, "plane-offsets", plane_offsets, + "plane-strides", plane_strides, "format", TEST_FRAME_FORMAT, NULL); + + g_value_array_free (plane_offsets); + g_value_array_free (plane_strides); + } + + /* Check that the plane stride/offset values are correct */ + { + GValueArray *plane_offsets_array; + GValueArray *plane_strides_array; + /* By default, 320x240 i420 is used as format */ + guint plane_offsets[3] = { 0, 76800, 96000 }; + guint plane_strides[3] = { 320, 160, 160 }; + + if (set_properties) { + /* When properties are explicitely set, we use Y444 as video format, + * so in that case, plane stride values are all the same */ + plane_offsets[0] = properties_ctx.plane_size * 0; + plane_offsets[1] = properties_ctx.plane_size * 1; + plane_offsets[2] = properties_ctx.plane_size * 2; + plane_strides[0] = plane_strides[1] = plane_strides[2] = + properties_ctx.plane_stride; + } + + g_object_get (G_OBJECT (rawvideoparse), "plane-offsets", + &plane_offsets_array, "plane-strides", &plane_strides_array, NULL); + fail_unless (plane_offsets_array != NULL); + fail_unless (plane_strides_array != NULL); + fail_unless (plane_offsets_array->n_values == + plane_strides_array->n_values); + + for (i = 0; i < plane_offsets_array->n_values; ++i) { + GValue *gvalue; + + gvalue = g_value_array_get_nth (plane_offsets_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (plane_offsets[i], g_value_get_uint (gvalue)); + + gvalue = g_value_array_get_nth (plane_strides_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (plane_strides[i], g_value_get_uint (gvalue)); + } + + g_value_array_free (plane_offsets_array); + g_value_array_free (plane_strides_array); + } + + fail_unless (gst_element_set_state (rawvideoparse, + GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS, + "could not set to paused"); + + mysrcpad = gst_check_setup_src_pad (rawvideoparse, &srctemplate); + mysinkpad = gst_check_setup_sink_pad (rawvideoparse, &sinktemplate); + + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + + gst_check_setup_events (mysrcpad, rawvideoparse, incaps, format); + if (incaps) + gst_caps_unref (incaps); + + + /* Fill the adapters with test frames */ + + for (i = 0; i < 10; ++i) { + GstBuffer *buffer = + gst_buffer_new_allocate (NULL, PROP_CTX_FRAME_STRIDE, NULL); + gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer)); + fill_test_pattern (&properties_ctx, buffer, i, 0); + gst_adapter_push (properties_ctx.data, buffer); + } + + for (i = 0; i < 10; ++i) { + GstBuffer *buffer = + gst_buffer_new_allocate (NULL, sinkcaps_ctx.plane_size * 3, NULL); + gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer)); + fill_test_pattern (&sinkcaps_ctx, buffer, i, 0); + gst_adapter_push (sinkcaps_ctx.data, buffer); + } +} + +static void +cleanup_rawvideoparse (void) +{ + gst_pad_set_active (mysrcpad, FALSE); + gst_pad_set_active (mysinkpad, FALSE); + gst_check_teardown_src_pad (rawvideoparse); + gst_check_teardown_sink_pad (rawvideoparse); + gst_check_teardown_element (rawvideoparse); + + g_object_unref (G_OBJECT (properties_ctx.data)); + g_object_unref (G_OBJECT (sinkcaps_ctx.data)); +} + +static void +push_data_and_check_output (Context * ctx, gsize num_in_bytes, + gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur, + guint expected_num_buffers_in_list, guint buf_idx, guint xofs, guint yofs) +{ + GstBuffer *inbuf, *outbuf; + guint num_buffers; + + /* Simulate upstream input by taking num_in_bytes bytes from the adapter */ + inbuf = gst_adapter_take_buffer (ctx->data, num_in_bytes); + fail_unless (inbuf != NULL); + + /* Push the input data and check that the output buffers list grew as + * expected */ + fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); + num_buffers = g_list_length (buffers); + fail_unless_equals_int (num_buffers, expected_num_buffers_in_list); + + /* Take the output buffer */ + outbuf = g_list_nth_data (buffers, buf_idx); + fail_unless (outbuf != NULL); + + /* Verify size, PTS, duration of the output buffer */ + fail_unless_equals_uint64 (expected_num_out_bytes, + gst_buffer_get_size (outbuf)); + fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf)); + fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf)); + + /* Check that the pixels have the correct values */ + check_test_pattern (ctx, outbuf, xofs, yofs); +} + + +GST_START_TEST (test_push_unaligned_data_properties_config) +{ + setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES); + + /* Send in data buffers that are not aligned to multiples of the + * frame size (= sample size * num_channels). This tests if rawvideoparse + * aligns output data properly. + * + * The second line sends a buffer with multiple frames inside. + * rawvideoparse will then parse this buffer repeatedly (and prepend + * leftover data from the earlier parse iteration), explaining why + * all of a sudden there are 4 output buffers, compared to just one + * earlier. The output data is expected to be 280 bytes large, since this + * is the size of the actual frame, without extra padding at the end. + */ + push_data_and_check_output (&properties_ctx, 511, 280, GST_MSECOND * 0, + GST_MSECOND * 40, 1, 0, 0, 0); + push_data_and_check_output (&properties_ctx, 1940, 280, GST_MSECOND * 40, + GST_MSECOND * 40, 4, 1, 1, 0); + push_data_and_check_output (&properties_ctx, 10, 280, GST_MSECOND * 80, + GST_MSECOND * 40, 4, 2, 2, 0); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + +GST_START_TEST (test_push_unaligned_data_sink_caps_config) +{ + GstVideoInfo vinfo; + GstCaps *caps; + + /* This test is essentially the same as test_push_unaligned_data_properties_config, + * except that rawvideoparse uses the sink caps config instead of the property config. + * Also, the input sizes are different, since the sink caps config does not use extra + * padding between planes and does use a stride that directly corresponds to the width, + * resulting in smaller frame size (192 bytes vs 280 bytes). */ + + gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH, + TEST_HEIGHT); + GST_VIDEO_INFO_FPS_N (&vinfo) = 25; + GST_VIDEO_INFO_FPS_D (&vinfo) = 1; + caps = gst_video_info_to_caps (&vinfo); + + setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES); + + push_data_and_check_output (&sinkcaps_ctx, 250, 192, GST_MSECOND * 0, + GST_MSECOND * 40, 1, 0, 0, 0); + push_data_and_check_output (&sinkcaps_ctx, 811, 192, GST_MSECOND * 40, + GST_MSECOND * 40, 5, 1, 1, 0); + push_data_and_check_output (&sinkcaps_ctx, 10, 192, GST_MSECOND * 80, + GST_MSECOND * 40, 5, 2, 2, 0); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + +GST_START_TEST (test_config_switch) +{ + GstVideoInfo vinfo; + GstCaps *caps; + + /* Start processing with the properties config active, then mid-stream switch to + * the sink caps config. Since the sink caps config does not use padding, its + * frame size is smaller. The buffer duration stays the same (since it only depends + * on the framerate), but the expected output buffer size is different). */ + + gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH, + TEST_HEIGHT); + GST_VIDEO_INFO_FPS_N (&vinfo) = 25; + GST_VIDEO_INFO_FPS_D (&vinfo) = 1; + caps = gst_video_info_to_caps (&vinfo); + + setup_rawvideoparse (FALSE, TRUE, caps, GST_FORMAT_BYTES); + + /* Push in data with properties config active */ + push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 0, + GST_MSECOND * 40, 1, 0, 0, 0); + push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 40, + GST_MSECOND * 40, 2, 1, 1, 0); + + /* Perform the switch */ + g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", TRUE, NULL); + + /* Push in data with sink caps config active, expecting a different frame size */ + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80, + GST_MSECOND * 40, 3, 2, 0, 0); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + +GST_START_TEST (test_push_with_no_framerate) +{ + /* Test the special case when no framerate is set. The parser is expected to + * still work then, but without setting duration or PTS/DTS (it cannot do that, + * because these require a nonzero framerate). The output buffers have PTS 0, + * all subsequent ones have no set PTS. */ + + setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES); + g_object_set (G_OBJECT (rawvideoparse), "framerate", 0, 1, NULL); + + push_data_and_check_output (&properties_ctx, 500, 280, 0, GST_CLOCK_TIME_NONE, + 1, 0, 0, 0); + push_data_and_check_output (&properties_ctx, 500, 280, GST_CLOCK_TIME_NONE, + GST_CLOCK_TIME_NONE, 2, 1, 1, 0); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + + +static Suite * +rawvideoparse_suite (void) +{ + Suite *s = suite_create ("rawvideoparse"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_push_unaligned_data_properties_config); + tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config); + tcase_add_test (tc_chain, test_config_switch); + tcase_add_test (tc_chain, test_push_with_no_framerate); + + return s; +} + +GST_CHECK_MAIN (rawvideoparse); From 3dfcc7920705005891434db3ea37f615aa0d07df Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Mon, 25 Jul 2016 17:54:09 +0200 Subject: [PATCH 14/29] rawparse: Remove old parser code and wrap new parsers in old elements https://bugzilla.gnome.org/show_bug.cgi?id=767011 --- gst/rawparse/Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index f3ad3c4142..623dc949cc 100644 --- a/gst/rawparse/Makefile.am +++ b/gst/rawparse/Makefile.am @@ -2,7 +2,6 @@ plugin_LTLIBRARIES = libgstrawparse.la libgstrawparse_la_SOURCES = \ - gstrawparse.c \ gstunalignedaudioparse.c \ gstunalignedvideoparse.c \ gstaudioparse.c \ @@ -29,7 +28,6 @@ noinst_HEADERS = \ gstunalignedaudioparse.h \ gstunalignedvideoparse.h \ gstaudioparse.h \ - gstrawparse.h \ gstvideoparse.h \ gstrawbaseparse.h \ gstrawaudioparse.h \ From e04d992ddcc8dda0d3cdc50c7ea529a19448fe6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 Jul 2016 10:07:23 +0300 Subject: [PATCH 15/29] unalignedaudioparse: Fix element factory name of inner parser --- gst/rawparse/gstunalignedaudioparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/gstunalignedaudioparse.c b/gst/rawparse/gstunalignedaudioparse.c index 786f4f4441..8847e1f9f7 100644 --- a/gst/rawparse/gstunalignedaudioparse.c +++ b/gst/rawparse/gstunalignedaudioparse.c @@ -96,7 +96,7 @@ gst_unaligned_audio_parse_init (GstUnalignedAudioParse * unaligned_audio_parse) GstPad *ghostpad; unaligned_audio_parse->inner_parser = - gst_element_factory_make ("rawaudioaudioparse", "inner_parser"); + gst_element_factory_make ("rawaudioparse", "inner_parser"); g_assert (unaligned_audio_parse->inner_parser != NULL); g_object_set (G_OBJECT (unaligned_audio_parse->inner_parser), From 4a0b3da3ae8b3f5225ca74b1ce8819485731fef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 Jul 2016 10:49:26 +0300 Subject: [PATCH 16/29] rawaudioparse: Use G_GINT64_MODIFIER for portability https://bugzilla.gnome.org/show_bug.cgi?id=769295 --- gst/rawparse/gstrawaudioparse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gst/rawparse/gstrawaudioparse.c b/gst/rawparse/gstrawaudioparse.c index d955f0e59c..98575ddb73 100644 --- a/gst/rawparse/gstrawaudioparse.c +++ b/gst/rawparse/gstrawaudioparse.c @@ -968,15 +968,15 @@ gst_raw_audio_parse_caps_to_config (GstRawAudioParse * raw_audio_parse, &channel_mask, NULL)) { channel_mask = gst_audio_channel_get_fallback_mask (num_channels); GST_DEBUG_OBJECT (raw_audio_parse, - "input caps have no channel mask - using fallback mask %#lx for %u channels", - channel_mask, num_channels); + "input caps have no channel mask - using fallback mask %#" + G_GINT64_MODIFIER "x for %u channels", channel_mask, num_channels); } if (!gst_raw_audio_parse_set_config_channels (config, num_channels, channel_mask, TRUE)) { GST_ERROR_OBJECT (raw_audio_parse, - "could not use channel mask %#lx for channel positions", - channel_mask); + "could not use channel mask %#" G_GINT64_MODIFIER + "x for channel positions", channel_mask); goto done; } From e5b70d384c71fd6d05c5f8ae5d7849022222c943 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Tue, 16 Aug 2016 09:31:40 +0200 Subject: [PATCH 17/29] rawvideoparse: Compute plane offsets & strides if no custom ones are set This is useful to ensure that the offsets and strides are computed if only width, height, format etc. in the property config are set. https://bugzilla.gnome.org/show_bug.cgi?id=769797 --- gst/rawparse/gstrawvideoparse.c | 91 ++++++++++++++++++---- gst/rawparse/gstrawvideoparse.h | 2 + tests/check/elements/rawvideoparse.c | 112 +++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 13 deletions(-) diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 03db577b05..20f3dcf8b0 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -34,9 +34,13 @@ * modified by using the width, height, pixel-aspect-ratio, framerate, interlaced, * top-field-first, plane-strides, plane-offsets, and frame-stride properties. * - * If the properties configuration is used, be sure to set valid plane stride - * offsets and values, otherwise the produced frames will not have a correct size. - * Merely setting the format is not enough. + * If the properties configuration is used, plane strides and offsets will be + * computed by using gst_video_info_set_format(). This can be overridden by passing + * GValueArrays to the plane-offsets and plane-strides properties. When this is + * done, these custom offsets and strides are used later even if new width, + * height, format etc. property values might be set. To switch back to computed + * plane strides & offsets, pass NULL to one or both of the plane-offset and + * plane-array properties. * * The frame stride property is useful in cases where there is extra data between * the frames (for example, trailing metadata, or headers). The parser calculates @@ -116,7 +120,7 @@ enum #define GST_RAW_VIDEO_PARSE_CAPS \ - GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; " + GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; " static GstStaticPadTemplate static_sink_template = @@ -513,6 +517,17 @@ gst_raw_video_parse_set_property (GObject * object, guint prop_id, guint n_planes; guint i; + /* If no valarray is given, then disable custom + * plane strides & offsets and stick to the + * standard computed ones */ + if (valarray == NULL) { + GST_DEBUG_OBJECT (raw_video_parse, + "custom plane strides & offsets disabled"); + props_cfg->custom_plane_strides = FALSE; + gst_raw_video_parse_update_info (props_cfg); + break; + } + /* Sanity check - reject empty arrays */ if ((valarray != NULL) && (valarray->n_values == 0)) { GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, @@ -541,6 +556,8 @@ gst_raw_video_parse_set_property (GObject * object, guint prop_id, props_cfg->plane_strides[i]); } + props_cfg->custom_plane_strides = TRUE; + gst_raw_video_parse_update_info (props_cfg); if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) @@ -558,6 +575,17 @@ gst_raw_video_parse_set_property (GObject * object, guint prop_id, guint n_planes; guint i; + /* If no valarray is given, then disable custom + * plane strides & offsets and stick to the + * standard computed ones */ + if (valarray == NULL) { + GST_DEBUG_OBJECT (raw_video_parse, + "custom plane strides & offsets disabled"); + props_cfg->custom_plane_strides = FALSE; + gst_raw_video_parse_update_info (props_cfg); + break; + } + /* Sanity check - reject empty arrays */ if ((valarray != NULL) && (valarray->n_values == 0)) { GST_ELEMENT_ERROR (raw_video_parse, LIBRARY, SETTINGS, @@ -586,6 +614,8 @@ gst_raw_video_parse_set_property (GObject * object, guint prop_id, i, props_cfg->plane_offsets[i]); } + props_cfg->custom_plane_strides = TRUE; + gst_raw_video_parse_update_info (props_cfg); if (!gst_raw_video_parse_is_using_sink_caps (raw_video_parse)) @@ -1093,11 +1123,17 @@ gst_raw_video_parse_init_config (GstRawVideoParseConfig * config) static void gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) { - int i; + guint i; guint n_planes; + guint last_plane; gsize last_plane_offset, last_plane_size; GstVideoInfo *info = &(config->info); + GST_DEBUG ("updating info with width %u height %u format %s " + " custom plane strides&offsets %d", config->width, config->height, + gst_video_format_to_string (config->format), + config->custom_plane_strides); + gst_video_info_set_format (info, config->format, config->width, config->height); @@ -1108,24 +1144,53 @@ gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) GST_VIDEO_INFO_INTERLACE_MODE (info) = config->interlaced ? GST_VIDEO_INTERLACE_MODE_INTERLEAVED : GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; - for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { - GST_VIDEO_INFO_PLANE_OFFSET (info, i) = config->plane_offsets[i]; - GST_VIDEO_INFO_PLANE_STRIDE (info, i) = config->plane_strides[i]; + + /* Check if there are custom plane strides & offsets that need to be preserved */ + if (config->custom_plane_strides) { + /* In case there are, overwrite the offsets&strides computed by + * gst_video_info_set_format with the custom ones */ + for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { + GST_VIDEO_INFO_PLANE_OFFSET (info, i) = config->plane_offsets[i]; + GST_VIDEO_INFO_PLANE_STRIDE (info, i) = config->plane_strides[i]; + } + } else { + /* No custom planes&offsets; copy the computed ones into + * the plane_offsets & plane_strides arrays to ensure they + * are equal to the ones in the videoinfo */ + for (i = 0; i < GST_VIDEO_MAX_PLANES; ++i) { + config->plane_offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (info, i); + config->plane_strides[i] = GST_VIDEO_INFO_PLANE_STRIDE (info, i); + } } n_planes = GST_VIDEO_INFO_N_PLANES (info); if (n_planes < 1) n_planes = 1; - last_plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, n_planes - 1); + /* Figure out what plane is the physically last one. Typically, the + * this is the last plane in the list (= at index n_planes-1). + * However, this is not guaranteed, so we have to scan the offsets + * to find the last plane. */ + last_plane_offset = 0; + last_plane = 0; + for (i = 0; i < n_planes; ++i) { + gsize plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); + if (plane_offset >= last_plane_offset) { + last_plane = i; + last_plane_offset = plane_offset; + } + } + last_plane = n_planes - 1; + last_plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, - n_planes - 1) * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, - n_planes - 1, config->height); + last_plane) * GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info->finfo, + last_plane, config->height); GST_VIDEO_INFO_SIZE (info) = last_plane_offset + last_plane_size; - GST_DEBUG ("last plane offset: %" G_GSIZE_FORMAT " last plane size: %" + GST_DEBUG ("last plane #%u: offset: %" G_GSIZE_FORMAT " size: %" G_GSIZE_FORMAT " => frame size minus extra padding: %" G_GSIZE_FORMAT, - last_plane_offset, last_plane_size, GST_VIDEO_INFO_SIZE (info)); + last_plane, last_plane_offset, last_plane_size, + GST_VIDEO_INFO_SIZE (info)); } diff --git a/gst/rawparse/gstrawvideoparse.h b/gst/rawparse/gstrawvideoparse.h index fea395598c..32605a66ac 100644 --- a/gst/rawparse/gstrawvideoparse.h +++ b/gst/rawparse/gstrawvideoparse.h @@ -78,6 +78,8 @@ struct _GstRawVideoParseConfig guint frame_stride; GstVideoInfo info; + + gboolean custom_plane_strides; }; diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c index b071370dba..d2926c8e03 100644 --- a/tests/check/elements/rawvideoparse.c +++ b/tests/check/elements/rawvideoparse.c @@ -440,6 +440,117 @@ GST_START_TEST (test_push_with_no_framerate) GST_END_TEST; +GST_START_TEST (test_computed_plane_strides) +{ + /* Test how plane strides & offsets are (re)computed if custom offsets/strides + * are disabled, and how they are preserved if they are enabled. */ + + GValueArray *plane_offsets_array; + GValueArray *plane_strides_array; + guint i; + guint const expected_comp_psize = TEST_WIDTH * TEST_HEIGHT; + + setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES); + + + /* The setup set a custom set of plane offsets and strides together with + * width=TEST_WIDTH and height=TEST_HEIGHT. Check that the offsets & strides + * are preserved even after setting new, different width & height values. */ + + g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH * 2, + "height", TEST_HEIGHT * 2, NULL); + + g_object_get (G_OBJECT (rawvideoparse), "plane-offsets", + &plane_offsets_array, "plane-strides", &plane_strides_array, NULL); + + for (i = 0; i < plane_offsets_array->n_values; ++i) { + GValue *gvalue; + + /* See setup_rawvideoparse() for how the offsets & strides are defined + * there. Offsets are set to plane_size*plane_index, and strides are + * set to the properties_ctx.plane_stride value. */ + + gvalue = g_value_array_get_nth (plane_offsets_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (properties_ctx.plane_size * i, + g_value_get_uint (gvalue)); + + gvalue = g_value_array_get_nth (plane_strides_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (properties_ctx.plane_stride, + g_value_get_uint (gvalue)); + } + + g_value_array_free (plane_offsets_array); + g_value_array_free (plane_strides_array); + + + /* Discard the custom planes&offsets, re-enabling computed values. */ + g_object_set (G_OBJECT (rawvideoparse), "plane-offsets", (GValueArray *) NULL, + "plane-strides", (GValueArray *) NULL, NULL); + + + /* The strides & offsets should have been recomputed by now. Since the Y444 + * format is used, all strides are the same, and should equal the frame width + * (which was set to TEST_WIDTH*2 earlier). Plane offsets should be + * plane_size*plane_index, with plane_size set to (TEST_WIDTH*2 * TEST_HEIGHT*2), + * or TEST_WIDTH*TEST_HEIGHT*4 (-> expected_comp_psize*4). */ + + g_object_get (G_OBJECT (rawvideoparse), "plane-offsets", + &plane_offsets_array, "plane-strides", &plane_strides_array, NULL); + + for (i = 0; i < plane_offsets_array->n_values; ++i) { + GValue *gvalue; + + gvalue = g_value_array_get_nth (plane_offsets_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (expected_comp_psize * 4 * i, + g_value_get_uint (gvalue)); + + gvalue = g_value_array_get_nth (plane_strides_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (TEST_WIDTH * 2, g_value_get_uint (gvalue)); + } + + g_value_array_free (plane_offsets_array); + g_value_array_free (plane_strides_array); + + + /* Again change the width & height values. width=TEST_WIDTH, height=TEST_HEIGHT. + * However, this time, offsets&strides are computed; the current values should + * not be preserved. Expected plane stride and offset values are similar to + * above, expect that no multiplications by 2 are present (since the TEST_WIDTH + * and TEST_HEIGHT values were passed without multiplying them). */ + + g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH, + "height", TEST_HEIGHT, NULL); + + + g_object_get (G_OBJECT (rawvideoparse), "plane-offsets", + &plane_offsets_array, "plane-strides", &plane_strides_array, NULL); + + for (i = 0; i < plane_offsets_array->n_values; ++i) { + GValue *gvalue; + + gvalue = g_value_array_get_nth (plane_offsets_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (expected_comp_psize * i, + g_value_get_uint (gvalue)); + + gvalue = g_value_array_get_nth (plane_strides_array, i); + fail_unless (gvalue != NULL); + fail_unless_equals_uint64 (TEST_WIDTH, g_value_get_uint (gvalue)); + } + + g_value_array_free (plane_offsets_array); + g_value_array_free (plane_strides_array); + + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + static Suite * rawvideoparse_suite (void) @@ -452,6 +563,7 @@ rawvideoparse_suite (void) tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config); tcase_add_test (tc_chain, test_config_switch); tcase_add_test (tc_chain, test_push_with_no_framerate); + tcase_add_test (tc_chain, test_computed_plane_strides); return s; } From 232902369c649d06ed887da53f4c921d36db9861 Mon Sep 17 00:00:00 2001 From: Carlos Rafael Giani Date: Mon, 8 Aug 2016 17:54:46 +0200 Subject: [PATCH 18/29] rawparse: Fix and extend unit tests * Add caps change test to unit tests * Cleanup leftover buffers after each unit test * Add missing rawvideoparse entry in .gitignore https://bugzilla.gnome.org/show_bug.cgi?id=769637 --- tests/check/elements/rawaudioparse.c | 56 +++++++++++++++++++++++++++ tests/check/elements/rawvideoparse.c | 57 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/tests/check/elements/rawaudioparse.c b/tests/check/elements/rawaudioparse.c index 62ab975e8e..029aab5564 100644 --- a/tests/check/elements/rawaudioparse.c +++ b/tests/check/elements/rawaudioparse.c @@ -120,6 +120,8 @@ setup_rawaudioparse (RawAudParseTestCtx * testctx, gboolean use_sink_caps, static void cleanup_rawaudioparse (RawAudParseTestCtx * testctx) { + int num_buffers, i; + gst_pad_set_active (mysrcpad, FALSE); gst_pad_set_active (mysinkpad, FALSE); gst_check_teardown_src_pad (testctx->rawaudioparse); @@ -127,6 +129,18 @@ cleanup_rawaudioparse (RawAudParseTestCtx * testctx) gst_check_teardown_element (testctx->rawaudioparse); g_object_unref (G_OBJECT (testctx->test_data_adapter)); + + if (buffers != NULL) { + num_buffers = g_list_length (buffers); + for (i = 0; i < num_buffers; ++i) { + GstBuffer *buf = GST_BUFFER (buffers->data); + buffers = g_list_remove (buffers, buf); + gst_buffer_unref (buf); + } + + g_list_free (buffers); + buffers = NULL; + } } @@ -320,6 +334,47 @@ GST_START_TEST (test_config_switch) GST_END_TEST; +GST_START_TEST (test_change_caps) +{ + RawAudParseTestCtx testctx; + GstAudioInfo ainfo; + GstCaps *caps; + + /* Start processing with the sink caps config active, using the + * default channel count and sample format and 20 kHz sample rate + * for the caps. Push some data, then change caps (20 kHz -> 40 kHz). + * Check that the changed caps are handled properly. */ + + gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, 20000, + NUM_TEST_CHANNELS, NULL); + caps = gst_audio_info_to_caps (&ainfo); + + setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES); + + /* Push in data with caps sink config active, expecting duration calculations + * to be based on the 20 kHz sample rate */ + push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0, + GST_USECOND * 500, 1, 4, 0, 512); + push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500, + GST_USECOND * 250, 2, 4, 10, 522); + + /* Change caps */ + gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, 40000, + NUM_TEST_CHANNELS, NULL); + caps = gst_audio_info_to_caps (&ainfo); + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps))); + gst_caps_unref (caps); + + /* Push in data with the new caps, expecting duration calculations + * to be based on the 40 kHz sample rate */ + push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750, + GST_USECOND * 250, 3, 4, 15, 527); + + cleanup_rawaudioparse (&testctx); +} + +GST_END_TEST; + static Suite * rawaudioparse_suite (void) @@ -332,6 +387,7 @@ rawaudioparse_suite (void) tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config); tcase_add_test (tc_chain, test_push_swapped_channels); tcase_add_test (tc_chain, test_config_switch); + tcase_add_test (tc_chain, test_change_caps); return s; } diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c index d2926c8e03..477e7678e8 100644 --- a/tests/check/elements/rawvideoparse.c +++ b/tests/check/elements/rawvideoparse.c @@ -283,6 +283,8 @@ setup_rawvideoparse (gboolean use_sink_caps, static void cleanup_rawvideoparse (void) { + int num_buffers, i; + gst_pad_set_active (mysrcpad, FALSE); gst_pad_set_active (mysinkpad, FALSE); gst_check_teardown_src_pad (rawvideoparse); @@ -291,6 +293,18 @@ cleanup_rawvideoparse (void) g_object_unref (G_OBJECT (properties_ctx.data)); g_object_unref (G_OBJECT (sinkcaps_ctx.data)); + + if (buffers != NULL) { + num_buffers = g_list_length (buffers); + for (i = 0; i < num_buffers; ++i) { + GstBuffer *buf = GST_BUFFER (buffers->data); + buffers = g_list_remove (buffers, buf); + gst_buffer_unref (buf); + } + + g_list_free (buffers); + buffers = NULL; + } } static void @@ -551,6 +565,48 @@ GST_START_TEST (test_computed_plane_strides) GST_END_TEST; +GST_START_TEST (test_change_caps) +{ + GstVideoInfo vinfo; + GstCaps *caps; + + /* Start processing with the sink caps config active, using the + * default width/height/format and 25 Hz frame rate for the caps. + * Push some data, then change caps (25 Hz -> 50 Hz). + * Check that the changed caps are handled properly. */ + + gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH, + TEST_HEIGHT); + GST_VIDEO_INFO_FPS_N (&vinfo) = 25; + GST_VIDEO_INFO_FPS_D (&vinfo) = 1; + caps = gst_video_info_to_caps (&vinfo); + + setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES); + + /* Push in data with sink config active, expecting duration calculations + * to be based on the 25 Hz frame rate */ + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 0, + GST_MSECOND * 40, 1, 0, 0, 0); + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 40, + GST_MSECOND * 40, 2, 1, 1, 0); + + /* Change caps */ + GST_VIDEO_INFO_FPS_N (&vinfo) = 50; + GST_VIDEO_INFO_FPS_D (&vinfo) = 1; + caps = gst_video_info_to_caps (&vinfo); + fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps))); + gst_caps_unref (caps); + + /* Push in data with sink config active, expecting duration calculations + * to be based on the 50 Hz frame rate */ + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80, + GST_MSECOND * 20, 3, 2, 2, 0); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; + static Suite * rawvideoparse_suite (void) @@ -564,6 +620,7 @@ rawvideoparse_suite (void) tcase_add_test (tc_chain, test_config_switch); tcase_add_test (tc_chain, test_push_with_no_framerate); tcase_add_test (tc_chain, test_computed_plane_strides); + tcase_add_test (tc_chain, test_change_caps); return s; } From 99e2745636b74efadfe59240488f93abfb36a895 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 19 Aug 2016 11:57:33 +0100 Subject: [PATCH 19/29] rawvideoparse: remove unused assignment Value in last_plane will be overwritten before used, remove unused asignment. Fixes commit 91cf5ac69f9c99fe41d60f42b4174915dd135e7b CID 1371462 --- gst/rawparse/gstrawvideoparse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 20f3dcf8b0..7e3d2efee6 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -1176,7 +1176,6 @@ gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) for (i = 0; i < n_planes; ++i) { gsize plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); if (plane_offset >= last_plane_offset) { - last_plane = i; last_plane_offset = plane_offset; } } From d594d4df6c1bcd8e6f8276126011665451d3e6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 19 Aug 2016 14:19:38 +0300 Subject: [PATCH 20/29] rawvideoparse: Revert last commit and actually remember the physically last plane Instead of just always taking the last one as before. --- gst/rawparse/gstrawvideoparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 7e3d2efee6..acec6d27ec 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -1176,10 +1176,10 @@ gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) for (i = 0; i < n_planes; ++i) { gsize plane_offset = GST_VIDEO_INFO_PLANE_OFFSET (info, i); if (plane_offset >= last_plane_offset) { + last_plane = i; last_plane_offset = plane_offset; } } - last_plane = n_planes - 1; last_plane_size = GST_VIDEO_INFO_PLANE_STRIDE (info, From 685bb8edb3b0b8ff38143c220f917ed31491ac86 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 19 Aug 2016 12:24:58 +0100 Subject: [PATCH 21/29] rawvideoparse: fix typo in comment Small typo in the comment explaining the code fixed by the previous commit. Fixing it. --- gst/rawparse/gstrawvideoparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index acec6d27ec..2fb58c1383 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -1167,7 +1167,7 @@ gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) if (n_planes < 1) n_planes = 1; - /* Figure out what plane is the physically last one. Typically, the + /* Figure out what plane is the physically last one. Typically * this is the last plane in the list (= at index n_planes-1). * However, this is not guaranteed, so we have to scan the offsets * to find the last plane. */ From 7d8cc8a2e656889d6385b9613517e0fac888066b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 29 Oct 2016 11:31:28 +0100 Subject: [PATCH 22/29] rawparse: pass flow returns upstream rawvideoparse wouldn't error out on not-negotiated, but would just keep on going, because it didn't pass the flow return value back to the parent class and thus upstream, so the source wouldnt' stop streaming. --- gst/rawparse/gstrawbaseparse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c index e90f400fab..c2ccee511c 100644 --- a/gst/rawparse/gstrawbaseparse.c +++ b/gst/rawparse/gstrawbaseparse.c @@ -590,8 +590,8 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, new_caps_event = NULL; } - gst_base_parse_finish_frame (parse, frame, out_size + frame->overhead); - + flow_ret = + gst_base_parse_finish_frame (parse, frame, out_size + frame->overhead); return flow_ret; From f324c88d924f52a193cedf0bef6282af62aa79ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sat, 29 Oct 2016 11:33:18 +0100 Subject: [PATCH 23/29] tests: rawvideoparse: add test for flow error handling Also needs fixes in baseparse: https://bugzilla.gnome.org/show_bug.cgi?id=773666 --- tests/check/elements/rawvideoparse.c | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c index 477e7678e8..91c71ab2b4 100644 --- a/tests/check/elements/rawvideoparse.c +++ b/tests/check/elements/rawvideoparse.c @@ -608,6 +608,41 @@ GST_START_TEST (test_change_caps) GST_END_TEST; +GST_START_TEST (test_not_negotiated) +{ + GstElement *pipeline, *src, *parse, *sink; + GstMessage *msg; + + pipeline = gst_pipeline_new ("pipeline"); + src = gst_element_factory_make ("fakesrc", NULL); + parse = gst_element_factory_make ("rawvideoparse", NULL); + sink = gst_element_factory_make ("appsink", NULL); + fail_unless (src != NULL && parse != NULL && sink != NULL); + gst_bin_add_many (GST_BIN (pipeline), src, parse, sink, NULL); + gst_element_link_many (src, parse, sink, NULL); + + gst_util_set_object_arg (G_OBJECT (src), "sizetype", "random"); + gst_util_set_object_arg (G_OBJECT (src), "filltype", "random"); + + /* fakesrc pull mode is broken, it will create random-sized buffers */ + gst_util_set_object_arg (G_OBJECT (src), "can-activate-pull", "false"); + + gst_util_set_object_arg (G_OBJECT (parse), "format", "rgb"); + + gst_util_set_object_arg (G_OBJECT (sink), "caps", "video/x-raw,format=I420"); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline), + GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR); + + gst_message_unref (msg); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + static Suite * rawvideoparse_suite (void) { @@ -621,6 +656,7 @@ rawvideoparse_suite (void) tcase_add_test (tc_chain, test_push_with_no_framerate); tcase_add_test (tc_chain, test_computed_plane_strides); tcase_add_test (tc_chain, test_change_caps); + tcase_add_test (tc_chain, test_not_negotiated); return s; } From 33f4b9cc8ea69f90442da86ad01cd995ad9f1ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 2 Nov 2016 09:36:04 +0200 Subject: [PATCH 24/29] Revert "tests: rawvideoparse: add test for flow error handling" This reverts commit 280b4ac2ffc63908a74944f50589b2630be16232. https://bugzilla.gnome.org/show_bug.cgi?id=773666 --- tests/check/elements/rawvideoparse.c | 36 ---------------------------- 1 file changed, 36 deletions(-) diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c index 91c71ab2b4..477e7678e8 100644 --- a/tests/check/elements/rawvideoparse.c +++ b/tests/check/elements/rawvideoparse.c @@ -608,41 +608,6 @@ GST_START_TEST (test_change_caps) GST_END_TEST; -GST_START_TEST (test_not_negotiated) -{ - GstElement *pipeline, *src, *parse, *sink; - GstMessage *msg; - - pipeline = gst_pipeline_new ("pipeline"); - src = gst_element_factory_make ("fakesrc", NULL); - parse = gst_element_factory_make ("rawvideoparse", NULL); - sink = gst_element_factory_make ("appsink", NULL); - fail_unless (src != NULL && parse != NULL && sink != NULL); - gst_bin_add_many (GST_BIN (pipeline), src, parse, sink, NULL); - gst_element_link_many (src, parse, sink, NULL); - - gst_util_set_object_arg (G_OBJECT (src), "sizetype", "random"); - gst_util_set_object_arg (G_OBJECT (src), "filltype", "random"); - - /* fakesrc pull mode is broken, it will create random-sized buffers */ - gst_util_set_object_arg (G_OBJECT (src), "can-activate-pull", "false"); - - gst_util_set_object_arg (G_OBJECT (parse), "format", "rgb"); - - gst_util_set_object_arg (G_OBJECT (sink), "caps", "video/x-raw,format=I420"); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - - msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline), - GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR); - - gst_message_unref (msg); - gst_element_set_state (pipeline, GST_STATE_NULL); - gst_object_unref (pipeline); -} - -GST_END_TEST; - static Suite * rawvideoparse_suite (void) { @@ -656,7 +621,6 @@ rawvideoparse_suite (void) tcase_add_test (tc_chain, test_push_with_no_framerate); tcase_add_test (tc_chain, test_computed_plane_strides); tcase_add_test (tc_chain, test_change_caps); - tcase_add_test (tc_chain, test_not_negotiated); return s; } From 0c79a06dd1c4f1b16ada23f116c01808594846b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 27 Nov 2016 11:44:14 +0200 Subject: [PATCH 25/29] rawparse: Properly align raw audio/video output buffers That is, aligned to the basic type for audio and to 32 bytes for video. Fixes crashes if the raw buffers are passed to SIMD processing functions. https://bugzilla.gnome.org/show_bug.cgi?id=774428 --- gst/rawparse/gstrawaudioparse.c | 36 +++++++++++++++++++++++++ gst/rawparse/gstrawbaseparse.c | 47 +++++++++++++++++++++++++++++++++ gst/rawparse/gstrawbaseparse.h | 3 +++ gst/rawparse/gstrawvideoparse.c | 13 +++++++-- 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/gst/rawparse/gstrawaudioparse.c b/gst/rawparse/gstrawaudioparse.c index 98575ddb73..626604c02a 100644 --- a/gst/rawparse/gstrawaudioparse.c +++ b/gst/rawparse/gstrawaudioparse.c @@ -161,6 +161,8 @@ static gboolean gst_raw_audio_parse_is_unit_format_supported (GstRawBaseParse * static void gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, gsize * units_per_sec_d); +static gint gst_raw_audio_parse_get_alignment (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); static gboolean gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * raw_audio_parse); @@ -228,6 +230,8 @@ gst_raw_audio_parse_class_init (GstRawAudioParseClass * klass) GST_DEBUG_FUNCPTR (gst_raw_audio_parse_is_unit_format_supported); rawbaseparse_class->get_units_per_second = GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_units_per_second); + rawbaseparse_class->get_alignment = + GST_DEBUG_FUNCPTR (gst_raw_audio_parse_get_alignment); g_object_class_install_property (object_class, PROP_FORMAT, @@ -669,6 +673,38 @@ gst_raw_audio_parse_is_config_ready (GstRawBaseParse * raw_base_parse, return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->ready; } +static guint +round_up_pow2 (guint n) +{ + n = n - 1; + n = n | (n >> 1); + n = n | (n >> 2); + n = n | (n >> 4); + n = n | (n >> 8); + n = n | (n >> 16); + return n + 1; +} + +static gint +gst_raw_audio_parse_get_alignment (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); + GstRawAudioParseConfig *config_ptr = + gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); + gint width; + + if (config_ptr->format != GST_RAW_AUDIO_PARSE_FORMAT_PCM) + return 1; + + width = + GST_AUDIO_FORMAT_INFO_WIDTH (gst_audio_format_get_info + (config_ptr->pcm_format)) / 8; + width = GST_ROUND_UP_8 (width); + width = round_up_pow2 (width); + + return width; +} static gboolean gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c index c2ccee511c..99d64dad09 100644 --- a/gst/rawparse/gstrawbaseparse.c +++ b/gst/rawparse/gstrawbaseparse.c @@ -430,6 +430,43 @@ done: return ret; } +static GstBuffer * +gst_raw_base_parse_align_buffer (GstRawBaseParse * raw_base_parse, + gsize alignment, GstBuffer * buffer) +{ + GstMapInfo map; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (map.size < sizeof (guintptr)) { + gst_buffer_unmap (buffer, &map); + return buffer; + } + + if (((guintptr) map.data) & (alignment - 1)) { + GstBuffer *new_buffer; + GstAllocationParams params = { 0, alignment - 1, 0, 0, }; + + new_buffer = gst_buffer_new_allocate (NULL, + gst_buffer_get_size (buffer), ¶ms); + + /* Copy data "by hand", so ensure alignment is kept: */ + gst_buffer_fill (new_buffer, 0, map.data, map.size); + + gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1); + GST_DEBUG_OBJECT (raw_base_parse, + "We want output aligned on %" G_GSIZE_FORMAT ", reallocated", + alignment); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return new_buffer; + } + + gst_buffer_unmap (buffer, &map); + return buffer; +} static GstFlowReturn gst_raw_base_parse_handle_frame (GstBaseParse * parse, @@ -442,6 +479,7 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, guint64 buffer_duration; GstFlowReturn flow_ret = GST_FLOW_OK; GstEvent *new_caps_event = NULL; + gint alignment; GstRawBaseParse *raw_base_parse = GST_RAW_BASE_PARSE (parse); GstRawBaseParseClass *klass = GST_RAW_BASE_PARSE_GET_CLASS (parse); @@ -567,6 +605,15 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, frame->out_buffer = NULL; } + if (klass->get_alignment + && (alignment = + klass->get_alignment (raw_base_parse, + GST_RAW_BASE_PARSE_CONFIG_CURRENT)) != 1) { + frame->out_buffer = + gst_raw_base_parse_align_buffer (raw_base_parse, alignment, + gst_buffer_ref (frame->buffer)); + } + /* Set the duration of the output buffer, or if none exists, of * the input buffer. Do this after the process() call, since in * case out_buffer is set, the subclass has created a new buffer. diff --git a/gst/rawparse/gstrawbaseparse.h b/gst/rawparse/gstrawbaseparse.h index 519b409ed2..4af4408c23 100644 --- a/gst/rawparse/gstrawbaseparse.h +++ b/gst/rawparse/gstrawbaseparse.h @@ -193,6 +193,9 @@ struct _GstRawBaseParseClass gint (*get_overhead_size) (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config); + + gint (*get_alignment) (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); }; diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 2fb58c1383..38083699e7 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -182,11 +182,12 @@ static GstRawVideoParseConfig * gst_raw_video_parse_get_config_ptr (GstRawVideoParse * raw_video_parse, GstRawBaseParseConfig config); +static gint gst_raw_video_parse_get_alignment (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config); + static void gst_raw_video_parse_init_config (GstRawVideoParseConfig * config); static void gst_raw_video_parse_update_info (GstRawVideoParseConfig * config); - - static void gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) { @@ -236,6 +237,8 @@ gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_units_per_second); rawbaseparse_class->get_overhead_size = GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_overhead_size); + rawbaseparse_class->get_alignment = + GST_DEBUG_FUNCPTR (gst_raw_video_parse_get_alignment); g_object_class_install_property (object_class, PROP_WIDTH, @@ -929,6 +932,12 @@ gst_raw_video_parse_is_config_ready (GstRawBaseParse * raw_base_parse, return gst_raw_video_parse_get_config_ptr (raw_video_parse, config)->ready; } +static gint +gst_raw_video_parse_get_alignment (GstRawBaseParse * raw_base_parse, + GstRawBaseParseConfig config) +{ + return 32; +} static gboolean gst_raw_video_parse_process (GstRawBaseParse * raw_base_parse, From a4f1d4a26551bd1267c63df503a5e87eac6f9555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sun, 27 Nov 2016 12:40:53 +0200 Subject: [PATCH 26/29] rawbaseparse: Fix output buffer size trimming For frame->buffer, baseparse is doing that automatically for us. For frame->output_buffer it doesn't and assumes that the subclass is already doing that. Consistency! --- gst/rawparse/gstrawbaseparse.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c index 99d64dad09..5fb8e73c84 100644 --- a/gst/rawparse/gstrawbaseparse.c +++ b/gst/rawparse/gstrawbaseparse.c @@ -432,7 +432,7 @@ done: static GstBuffer * gst_raw_base_parse_align_buffer (GstRawBaseParse * raw_base_parse, - gsize alignment, GstBuffer * buffer) + gsize alignment, GstBuffer * buffer, gsize out_size) { GstMapInfo map; @@ -440,32 +440,32 @@ gst_raw_base_parse_align_buffer (GstRawBaseParse * raw_base_parse, if (map.size < sizeof (guintptr)) { gst_buffer_unmap (buffer, &map); - return buffer; + return NULL; } if (((guintptr) map.data) & (alignment - 1)) { GstBuffer *new_buffer; GstAllocationParams params = { 0, alignment - 1, 0, 0, }; - new_buffer = gst_buffer_new_allocate (NULL, - gst_buffer_get_size (buffer), ¶ms); + new_buffer = gst_buffer_new_allocate (NULL, out_size, ¶ms); /* Copy data "by hand", so ensure alignment is kept: */ - gst_buffer_fill (new_buffer, 0, map.data, map.size); + gst_buffer_fill (new_buffer, 0, map.data, out_size); - gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1); + gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, + out_size); GST_DEBUG_OBJECT (raw_base_parse, "We want output aligned on %" G_GSIZE_FORMAT ", reallocated", alignment); gst_buffer_unmap (buffer, &map); - gst_buffer_unref (buffer); return new_buffer; } gst_buffer_unmap (buffer, &map); - return buffer; + + return NULL; } static GstFlowReturn @@ -609,9 +609,17 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, && (alignment = klass->get_alignment (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT)) != 1) { - frame->out_buffer = + GstBuffer *aligned_buffer; + + aligned_buffer = gst_raw_base_parse_align_buffer (raw_base_parse, alignment, - gst_buffer_ref (frame->buffer)); + frame->out_buffer ? frame->out_buffer : frame->buffer, out_size); + + if (aligned_buffer) { + if (frame->out_buffer) + gst_buffer_unref (frame->out_buffer); + frame->out_buffer = aligned_buffer; + } } /* Set the duration of the output buffer, or if none exists, of From da981c85a1dbe1b9a870da90290b7b0488ac4a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 28 Nov 2016 14:25:49 +0200 Subject: [PATCH 27/29] rawparse: Whitespace cleanup --- gst/rawparse/gstrawaudioparse.c | 33 --------------------------- gst/rawparse/gstrawaudioparse.h | 7 ------ gst/rawparse/gstrawbaseparse.c | 31 ------------------------- gst/rawparse/gstrawbaseparse.h | 7 ------ gst/rawparse/gstrawvideoparse.c | 26 --------------------- gst/rawparse/gstrawvideoparse.h | 6 ----- gst/rawparse/gstunalignedaudioparse.c | 10 -------- gst/rawparse/gstunalignedvideoparse.c | 10 -------- 8 files changed, 130 deletions(-) diff --git a/gst/rawparse/gstrawaudioparse.c b/gst/rawparse/gstrawaudioparse.c index 626604c02a..393411dc23 100644 --- a/gst/rawparse/gstrawaudioparse.c +++ b/gst/rawparse/gstrawaudioparse.c @@ -83,11 +83,9 @@ #include "gstrawaudioparse.h" #include "unalignedaudio.h" - GST_DEBUG_CATEGORY_STATIC (raw_audio_parse_debug); #define GST_CAT_DEFAULT raw_audio_parse_debug - enum { PROP_0, @@ -99,21 +97,18 @@ enum PROP_CHANNEL_POSITIONS }; - #define DEFAULT_FORMAT GST_RAW_AUDIO_PARSE_FORMAT_PCM #define DEFAULT_PCM_FORMAT GST_AUDIO_FORMAT_S16 #define DEFAULT_SAMPLE_RATE 44100 #define DEFAULT_NUM_CHANNELS 2 #define DEFAULT_INTERLEAVED TRUE - #define GST_RAW_AUDIO_PARSE_CAPS \ GST_AUDIO_CAPS_MAKE(GST_AUDIO_FORMATS_ALL) \ ", layout = (string) { interleaved, non-interleaved }; " \ "audio/x-alaw, rate = (int) [ 1, MAX ], channels = (int) [ 1, MAX ]; " \ "audio/x-mulaw, rate = (int) [ 1, MAX ], channels = (int) [ 1, MAX ]; " - static GstStaticPadTemplate static_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -121,7 +116,6 @@ static GstStaticPadTemplate static_sink_template = GST_STATIC_CAPS (GST_UNALIGNED_RAW_AUDIO_CAPS "; " GST_RAW_AUDIO_PARSE_CAPS) ); - static GstStaticPadTemplate static_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -129,11 +123,9 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (GST_RAW_AUDIO_PARSE_CAPS) ); - #define gst_raw_audio_parse_parent_class parent_class G_DEFINE_TYPE (GstRawAudioParse, gst_raw_audio_parse, GST_TYPE_RAW_BASE_PARSE); - static void gst_raw_audio_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec); static void gst_raw_audio_parse_get_property (GObject * object, guint prop_id, @@ -183,8 +175,6 @@ static gboolean gst_raw_audio_parse_caps_to_config (GstRawAudioParse * static gboolean gst_raw_audio_parse_config_to_caps (GstRawAudioParse * raw_audio_parse, GstCaps ** caps, GstRawAudioParseConfig * config); - - static void gst_raw_audio_parse_class_init (GstRawAudioParseClass * klass) { @@ -295,7 +285,6 @@ gst_raw_audio_parse_class_init (GstRawAudioParseClass * klass) "Carlos Rafael Giani "); } - static void gst_raw_audio_parse_init (GstRawAudioParse * raw_audio_parse) { @@ -312,7 +301,6 @@ gst_raw_audio_parse_init (GstRawAudioParse * raw_audio_parse) gst_raw_audio_parse_update_config_bpf (&(raw_audio_parse->properties_config)); } - static void gst_raw_audio_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec) @@ -506,7 +494,6 @@ gst_raw_audio_parse_set_property (GObject * object, guint prop_id, } } - static void gst_raw_audio_parse_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -587,7 +574,6 @@ gst_raw_audio_parse_get_property (GObject * object, guint prop_id, } } - static gboolean gst_raw_audio_parse_stop (GstBaseParse * parse) { @@ -603,7 +589,6 @@ gst_raw_audio_parse_stop (GstBaseParse * parse) return GST_BASE_PARSE_CLASS (parent_class)->stop (parse); } - static gboolean gst_raw_audio_parse_set_current_config (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -626,7 +611,6 @@ gst_raw_audio_parse_set_current_config (GstRawBaseParse * raw_base_parse, return TRUE; } - static GstRawBaseParseConfig gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse) { @@ -635,7 +619,6 @@ gst_raw_audio_parse_get_current_config (GstRawBaseParse * raw_base_parse) GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; } - static gboolean gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstCaps * caps) @@ -645,7 +628,6 @@ gst_raw_audio_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); } - static gboolean gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstCaps ** caps) @@ -655,7 +637,6 @@ gst_raw_audio_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)); } - static gsize gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -664,7 +645,6 @@ gst_raw_audio_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, return gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config)->bpf; } - static gboolean gst_raw_audio_parse_is_config_ready (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -754,7 +734,6 @@ gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, return TRUE; } - static gboolean gst_raw_audio_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * raw_base_parse, GstFormat format) @@ -768,7 +747,6 @@ gst_raw_audio_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * } } - static void gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, @@ -794,7 +772,6 @@ gst_raw_audio_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, } } - static gboolean gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * raw_audio_parse) { @@ -802,7 +779,6 @@ gst_raw_audio_parse_is_using_sink_caps (GstRawAudioParse * raw_audio_parse) &(raw_audio_parse->sink_caps_config); } - static GstRawAudioParseConfig * gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, GstRawBaseParseConfig config) @@ -822,7 +798,6 @@ gst_raw_audio_parse_get_config_ptr (GstRawAudioParse * raw_audio_parse, } } - static void gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config) { @@ -839,7 +814,6 @@ gst_raw_audio_parse_init_config (GstRawAudioParseConfig * config) TRUE); } - static gboolean gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig * config, guint num_channels, guint64 channel_mask, gboolean set_positions) @@ -865,7 +839,6 @@ gst_raw_audio_parse_set_config_channels (GstRawAudioParseConfig * config, } } - static gboolean gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * config) @@ -895,7 +868,6 @@ gst_raw_audio_parse_update_channel_reordering_flag (GstRawAudioParseConfig * } } - static void gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * config) { @@ -923,7 +895,6 @@ gst_raw_audio_parse_update_config_bpf (GstRawAudioParseConfig * config) } } - static gboolean gst_raw_audio_parse_caps_to_config (GstRawAudioParse * raw_audio_parse, GstCaps * caps, GstRawAudioParseConfig * config) @@ -1034,7 +1005,6 @@ done: return ret; } - static gboolean gst_raw_audio_parse_config_to_caps (GstRawAudioParse * raw_audio_parse, GstCaps ** caps, GstRawAudioParseConfig * config) @@ -1102,9 +1072,6 @@ gst_raw_audio_parse_config_to_caps (GstRawAudioParse * raw_audio_parse, return ret; } - - - GType gst_raw_audio_parse_format_get_type (void) { diff --git a/gst/rawparse/gstrawaudioparse.h b/gst/rawparse/gstrawaudioparse.h index f1e0766e5e..f6d8b2e449 100644 --- a/gst/rawparse/gstrawaudioparse.h +++ b/gst/rawparse/gstrawaudioparse.h @@ -39,14 +39,12 @@ G_BEGIN_DECLS #define GST_IS_RAW_AUDIO_PARSE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_AUDIO_PARSE)) - typedef enum _GstRawAudioParseFormat GstRawAudioParseFormat; typedef struct _GstRawAudioParseConfig GstRawAudioParseConfig; typedef struct _GstRawAudioParse GstRawAudioParse; typedef struct _GstRawAudioParseClass GstRawAudioParseClass; - enum _GstRawAudioParseFormat { GST_RAW_AUDIO_PARSE_FORMAT_PCM, @@ -54,7 +52,6 @@ enum _GstRawAudioParseFormat GST_RAW_AUDIO_PARSE_FORMAT_ALAW }; - /* Contains information about the sample rate, format, and channel count to use. */ struct _GstRawAudioParseConfig { @@ -94,7 +91,6 @@ struct _GstRawAudioParseConfig gboolean needs_channel_reordering; }; - struct _GstRawAudioParse { GstRawBaseParse parent; @@ -115,17 +111,14 @@ struct _GstRawAudioParse GstRawAudioParseConfig *current_config; }; - struct _GstRawAudioParseClass { GstRawBaseParseClass parent_class; }; - GType gst_raw_audio_parse_get_type (void); GType gst_raw_audio_parse_format_get_type (void); - G_END_DECLS #endif diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c index 5fb8e73c84..f5bb202e61 100644 --- a/gst/rawparse/gstrawbaseparse.c +++ b/gst/rawparse/gstrawbaseparse.c @@ -109,29 +109,24 @@ #include #include "gstrawbaseparse.h" - GST_DEBUG_CATEGORY_STATIC (raw_base_parse_debug); #define GST_CAT_DEFAULT raw_base_parse_debug - enum { PROP_0, PROP_USE_SINK_CAPS }; - #define DEFAULT_USE_SINK_CAPS FALSE #define INITIAL_PARSER_CONFIG \ ((DEFAULT_USE_SINK_CAPS) ? GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : \ GST_RAW_BASE_PARSE_CONFIG_PROPERTIES) - #define gst_raw_base_parse_parent_class parent_class G_DEFINE_ABSTRACT_TYPE (GstRawBaseParse, gst_raw_base_parse, GST_TYPE_BASE_PARSE); - static void gst_raw_base_parse_finalize (GObject * object); static void gst_raw_base_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec); @@ -152,8 +147,6 @@ static gboolean gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * static gboolean gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * raw_base_parse, GstFormat format); - - static void gst_raw_base_parse_class_init (GstRawBaseParseClass * klass) { @@ -196,7 +189,6 @@ gst_raw_base_parse_class_init (GstRawBaseParseClass * klass) ); } - static void gst_raw_base_parse_init (GstRawBaseParse * raw_base_parse) { @@ -204,7 +196,6 @@ gst_raw_base_parse_init (GstRawBaseParse * raw_base_parse) g_mutex_init (&(raw_base_parse->config_mutex)); } - static void gst_raw_base_parse_finalize (GObject * object) { @@ -215,7 +206,6 @@ gst_raw_base_parse_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } - static void gst_raw_base_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec) @@ -291,7 +281,6 @@ gst_raw_base_parse_set_property (GObject * object, guint prop_id, } } - static void gst_raw_base_parse_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -312,7 +301,6 @@ gst_raw_base_parse_get_property (GObject * object, guint prop_id, } } - static gboolean gst_raw_base_parse_start (GstBaseParse * parse) { @@ -338,7 +326,6 @@ gst_raw_base_parse_start (GstBaseParse * parse) return TRUE; } - static gboolean gst_raw_base_parse_stop (GstBaseParse * parse) { @@ -351,7 +338,6 @@ gst_raw_base_parse_stop (GstBaseParse * parse) return TRUE; } - static gboolean gst_raw_base_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps) { @@ -363,7 +349,6 @@ gst_raw_base_parse_set_sink_caps (GstBaseParse * parse, GstCaps * caps) g_assert (klass->get_caps_from_config); g_assert (klass->get_config_frame_size); - GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); GST_DEBUG_OBJECT (parse, "getting config from new sink caps"); @@ -488,18 +473,15 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, g_assert (klass->get_config_frame_size); g_assert (klass->get_units_per_second); - /* We never skip any bytes this way. Instead, subclass takes care * of skipping any overhead (necessary, since the way it needs to * be skipped is completely subclass specific). */ *skipsize = 0; - /* The operations below access the current config. Protect * against race conditions by using the object lock. */ GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); - /* If the source pad caps haven't been set yet, or need to be * set again, do so now, BEFORE any buffers are pushed out */ if (G_UNLIKELY (!raw_base_parse->src_caps_set)) { @@ -544,7 +526,6 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, klass->get_config_frame_size (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT); - in_size = gst_buffer_get_size (frame->buffer); /* gst_base_parse_set_min_frame_size() is called when the current @@ -636,7 +617,6 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, * operations, so the lock can be released */ GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); - /* If any new caps have to be pushed downstrean, do so * *before* the frame is finished */ if (G_UNLIKELY (new_caps_event != NULL)) { @@ -650,7 +630,6 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, return flow_ret; - config_not_ready: GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); GST_ELEMENT_ERROR (parse, STREAM, FORMAT, @@ -676,7 +655,6 @@ error_end: return flow_ret; } - static gboolean gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format, gint64 src_value, GstFormat dest_format, gint64 * dest_value) @@ -689,12 +667,10 @@ gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format, g_assert (klass->is_config_ready); g_assert (klass->get_units_per_second); - /* The operations below access the current config. Protect * against race conditions by using the object lock. */ GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK (raw_base_parse); - if (!klass->is_config_ready (raw_base_parse, GST_RAW_BASE_PARSE_CONFIG_CURRENT)) { if (gst_raw_base_parse_is_using_sink_caps (raw_base_parse)) { @@ -746,7 +722,6 @@ gst_raw_base_parse_convert (GstBaseParse * parse, GstFormat src_format, GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); return ret; - config_not_ready: GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); GST_ELEMENT_ERROR (parse, STREAM, FORMAT, @@ -755,7 +730,6 @@ config_not_ready: return FALSE; } - static gboolean gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * raw_base_parse) { @@ -766,7 +740,6 @@ gst_raw_base_parse_is_using_sink_caps (GstRawBaseParse * raw_base_parse) GST_RAW_BASE_PARSE_CONFIG_SINKCAPS; } - static gboolean gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * raw_base_parse, GstFormat format) @@ -777,10 +750,6 @@ gst_raw_base_parse_is_gstformat_supported (GstRawBaseParse * raw_base_parse, return klass->is_unit_format_supported (raw_base_parse, format); } - - - - /** * gst_raw_base_parse_invalidate_src_caps: * @raw_base_parse: a #GstRawBaseParse instance diff --git a/gst/rawparse/gstrawbaseparse.h b/gst/rawparse/gstrawbaseparse.h index 4af4408c23..7aeedea589 100644 --- a/gst/rawparse/gstrawbaseparse.h +++ b/gst/rawparse/gstrawbaseparse.h @@ -43,12 +43,10 @@ G_BEGIN_DECLS #define GST_RAW_BASE_PARSE_CONFIG_MUTEX_LOCK(obj) g_mutex_lock(&(((GstRawBaseParse *)(obj))->config_mutex)) #define GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK(obj) g_mutex_unlock(&(((GstRawBaseParse *)(obj))->config_mutex)) - typedef enum _GstRawBaseParseConfig GstRawBaseParseConfig; typedef struct _GstRawBaseParse GstRawBaseParse; typedef struct _GstRawBaseParseClass GstRawBaseParseClass; - /** * GstRawBaseParseConfig: * @GST_RAW_BASE_PARSE_CONFIG_CURRENT: configuration that is currently active @@ -64,7 +62,6 @@ enum _GstRawBaseParseConfig GST_RAW_BASE_PARSE_CONFIG_PROPERTIES }; - /** * GstRawBaseParse: * @@ -84,7 +81,6 @@ struct _GstRawBaseParse GMutex config_mutex; }; - /** * GstRawBaseParseClass: * @parent_class: The parent class structure @@ -198,13 +194,10 @@ struct _GstRawBaseParseClass GstRawBaseParseConfig config); }; - void gst_raw_base_parse_invalidate_src_caps (GstRawBaseParse * raw_base_parse); - GType gst_raw_base_parse_get_type (void); - G_END_DECLS #endif diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 38083699e7..95304bebb2 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -86,11 +86,9 @@ #include "gstrawvideoparse.h" #include "unalignedvideo.h" - GST_DEBUG_CATEGORY_STATIC (raw_video_parse_debug); #define GST_CAT_DEFAULT raw_video_parse_debug - enum { PROP_0, @@ -106,7 +104,6 @@ enum PROP_FRAME_STRIDE }; - #define DEFAULT_WIDTH 320 #define DEFAULT_HEIGHT 240 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_I420 @@ -118,11 +115,9 @@ enum #define DEFAULT_TOP_FIELD_FIRST FALSE #define DEFAULT_FRAME_STRIDE 0 - #define GST_RAW_VIDEO_PARSE_CAPS \ GST_VIDEO_CAPS_MAKE(GST_VIDEO_FORMATS_ALL) "; " - static GstStaticPadTemplate static_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -130,7 +125,6 @@ static GstStaticPadTemplate static_sink_template = GST_STATIC_CAPS (GST_UNALIGNED_RAW_VIDEO_CAPS "; " GST_RAW_VIDEO_PARSE_CAPS) ); - static GstStaticPadTemplate static_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -138,11 +132,9 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (GST_RAW_VIDEO_PARSE_CAPS) ); - #define gst_raw_video_parse_parent_class parent_class G_DEFINE_TYPE (GstRawVideoParse, gst_raw_video_parse, GST_TYPE_RAW_BASE_PARSE); - static void gst_raw_video_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec); static void gst_raw_video_parse_get_property (GObject * object, guint prop_id, @@ -338,7 +330,6 @@ gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) "Carlos Rafael Giani "); } - static void gst_raw_video_parse_init (GstRawVideoParse * raw_video_parse) { @@ -356,7 +347,6 @@ gst_raw_video_parse_init (GstRawVideoParse * raw_video_parse) raw_video_parse->properties_config.frame_stride = DEFAULT_FRAME_STRIDE; } - static void gst_raw_video_parse_set_property (GObject * object, guint prop_id, GValue const *value, GParamSpec * pspec) @@ -653,7 +643,6 @@ gst_raw_video_parse_set_property (GObject * object, guint prop_id, } } - static void gst_raw_video_parse_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) @@ -773,7 +762,6 @@ gst_raw_video_parse_get_property (GObject * object, guint prop_id, } } - static gboolean gst_raw_video_parse_stop (GstBaseParse * parse) { @@ -789,7 +777,6 @@ gst_raw_video_parse_stop (GstBaseParse * parse) return GST_BASE_PARSE_CLASS (parent_class)->stop (parse); } - static gboolean gst_raw_video_parse_set_current_config (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -812,7 +799,6 @@ gst_raw_video_parse_set_current_config (GstRawBaseParse * raw_base_parse, return TRUE; } - static GstRawBaseParseConfig gst_raw_video_parse_get_current_config (GstRawBaseParse * raw_base_parse) { @@ -821,7 +807,6 @@ gst_raw_video_parse_get_current_config (GstRawBaseParse * raw_base_parse) GST_RAW_BASE_PARSE_CONFIG_SINKCAPS : GST_RAW_BASE_PARSE_CONFIG_PROPERTIES; } - static gboolean gst_raw_video_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstCaps * caps) @@ -886,7 +871,6 @@ gst_raw_video_parse_set_config_from_caps (GstRawBaseParse * raw_base_parse, return config_ptr->ready; } - static gboolean gst_raw_video_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstCaps ** caps) @@ -902,7 +886,6 @@ gst_raw_video_parse_get_caps_from_config (GstRawBaseParse * raw_base_parse, return *caps != NULL; } - static gsize gst_raw_video_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -914,7 +897,6 @@ gst_raw_video_parse_get_config_frame_size (GstRawBaseParse * raw_base_parse, (gsize) (config_ptr->frame_stride)); } - static guint gst_raw_video_parse_get_max_frames_per_buffer (G_GNUC_UNUSED GstRawBaseParse * raw_base_parse, G_GNUC_UNUSED GstRawBaseParseConfig config) @@ -923,7 +905,6 @@ gst_raw_video_parse_get_max_frames_per_buffer (G_GNUC_UNUSED GstRawBaseParse * return 1; } - static gboolean gst_raw_video_parse_is_config_ready (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -995,7 +976,6 @@ gst_raw_video_parse_process (GstRawBaseParse * raw_base_parse, return TRUE; } - static gboolean gst_raw_video_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * raw_base_parse, GstFormat format) @@ -1009,7 +989,6 @@ gst_raw_video_parse_is_unit_format_supported (G_GNUC_UNUSED GstRawBaseParse * } } - static void gst_raw_video_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, GstFormat format, GstRawBaseParseConfig config, gsize * units_per_sec_n, @@ -1050,7 +1029,6 @@ gst_raw_video_parse_get_units_per_second (GstRawBaseParse * raw_base_parse, } } - static gint gst_raw_video_parse_get_overhead_size (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config) @@ -1073,7 +1051,6 @@ gst_raw_video_parse_get_overhead_size (GstRawBaseParse * raw_base_parse, return (frame_size < frame_stride) ? (gint) (frame_stride - frame_size) : 0; } - static gboolean gst_raw_video_parse_is_using_sink_caps (GstRawVideoParse * raw_video_parse) { @@ -1081,7 +1058,6 @@ gst_raw_video_parse_is_using_sink_caps (GstRawVideoParse * raw_video_parse) &(raw_video_parse->sink_caps_config); } - static GstRawVideoParseConfig * gst_raw_video_parse_get_config_ptr (GstRawVideoParse * raw_video_parse, GstRawBaseParseConfig config) @@ -1101,7 +1077,6 @@ gst_raw_video_parse_get_config_ptr (GstRawVideoParse * raw_video_parse, } } - static void gst_raw_video_parse_init_config (GstRawVideoParseConfig * config) { @@ -1128,7 +1103,6 @@ gst_raw_video_parse_init_config (GstRawVideoParseConfig * config) } } - static void gst_raw_video_parse_update_info (GstRawVideoParseConfig * config) { diff --git a/gst/rawparse/gstrawvideoparse.h b/gst/rawparse/gstrawvideoparse.h index 32605a66ac..034bba1ae0 100644 --- a/gst/rawparse/gstrawvideoparse.h +++ b/gst/rawparse/gstrawvideoparse.h @@ -39,12 +39,10 @@ G_BEGIN_DECLS #define GST_IS_RAW_VIDEO_PARSE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RAW_VIDEO_PARSE)) - typedef struct _GstRawVideoParseConfig GstRawVideoParseConfig; typedef struct _GstRawVideoParse GstRawVideoParse; typedef struct _GstRawVideoParseClass GstRawVideoParseClass; - /* Contains information about the video frame format. */ struct _GstRawVideoParseConfig { @@ -82,7 +80,6 @@ struct _GstRawVideoParseConfig gboolean custom_plane_strides; }; - struct _GstRawVideoParse { GstRawBaseParse parent; @@ -103,16 +100,13 @@ struct _GstRawVideoParse GstRawVideoParseConfig *current_config; }; - struct _GstRawVideoParseClass { GstRawBaseParseClass parent_class; }; - GType gst_raw_video_parse_get_type (void); - G_END_DECLS #endif diff --git a/gst/rawparse/gstunalignedaudioparse.c b/gst/rawparse/gstunalignedaudioparse.c index 8847e1f9f7..38f387f143 100644 --- a/gst/rawparse/gstunalignedaudioparse.c +++ b/gst/rawparse/gstunalignedaudioparse.c @@ -26,24 +26,20 @@ #include "gstunalignedaudioparse.h" #include "unalignedaudio.h" - GST_DEBUG_CATEGORY (unaligned_audio_parse_debug); #define GST_CAT_DEFAULT unaligned_audio_parse_debug - struct _GstUnalignedAudioParse { GstBin parent; GstElement *inner_parser; }; - struct _GstUnalignedAudioParseClass { GstBinClass parent_class; }; - static GstStaticPadTemplate static_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -51,7 +47,6 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS (GST_UNALIGNED_RAW_AUDIO_CAPS) ); - static GstStaticPadTemplate static_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -60,12 +55,8 @@ GST_STATIC_PAD_TEMPLATE ("src", ", layout = (string) { interleaved, non-interleaved }") ); - - - G_DEFINE_TYPE (GstUnalignedAudioParse, gst_unaligned_audio_parse, GST_TYPE_BIN); - static void gst_unaligned_audio_parse_class_init (GstUnalignedAudioParseClass * klass) { @@ -88,7 +79,6 @@ gst_unaligned_audio_parse_class_init (GstUnalignedAudioParseClass * klass) "Carlos Rafael Giani "); } - static void gst_unaligned_audio_parse_init (GstUnalignedAudioParse * unaligned_audio_parse) { diff --git a/gst/rawparse/gstunalignedvideoparse.c b/gst/rawparse/gstunalignedvideoparse.c index d6e8cf36eb..81ff3eafe8 100644 --- a/gst/rawparse/gstunalignedvideoparse.c +++ b/gst/rawparse/gstunalignedvideoparse.c @@ -26,24 +26,20 @@ #include "gstunalignedvideoparse.h" #include "unalignedvideo.h" - GST_DEBUG_CATEGORY (unaligned_video_parse_debug); #define GST_CAT_DEFAULT unaligned_video_parse_debug - struct _GstUnalignedVideoParse { GstBin parent; GstElement *inner_parser; }; - struct _GstUnalignedVideoParseClass { GstBinClass parent_class; }; - static GstStaticPadTemplate static_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -51,7 +47,6 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS (GST_UNALIGNED_RAW_VIDEO_CAPS) ); - static GstStaticPadTemplate static_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -59,12 +54,8 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) ); - - - G_DEFINE_TYPE (GstUnalignedVideoParse, gst_unaligned_video_parse, GST_TYPE_BIN); - static void gst_unaligned_video_parse_class_init (GstUnalignedVideoParseClass * klass) { @@ -87,7 +78,6 @@ gst_unaligned_video_parse_class_init (GstUnalignedVideoParseClass * klass) "Carlos Rafael Giani "); } - static void gst_unaligned_video_parse_init (GstUnalignedVideoParse * unaligned_video_parse) { From f14a307638596930412564329b7fabe0f23d82f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Mon, 5 Dec 2016 18:17:30 +0000 Subject: [PATCH 28/29] rawvideoparse: fix typos in property description --- gst/rawparse/gstrawvideoparse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/rawparse/gstrawvideoparse.c b/gst/rawparse/gstrawvideoparse.c index 95304bebb2..38fc5589ad 100644 --- a/gst/rawparse/gstrawvideoparse.c +++ b/gst/rawparse/gstrawvideoparse.c @@ -292,7 +292,7 @@ gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) PROP_PLANE_STRIDES, g_param_spec_value_array ("plane-strides", "Plane strides", - "Strides of the planets in bytes", + "Strides of the planes in bytes", g_param_spec_uint ("plane-stride", "Plane stride", "Stride of the n-th plane in bytes (0 = stride equals width*bytes-per-pixel)", @@ -305,7 +305,7 @@ gst_raw_video_parse_class_init (GstRawVideoParseClass * klass) PROP_PLANE_OFFSETS, g_param_spec_value_array ("plane-offsets", "Plane offsets", - "Offsets of the planets in bytes", + "Offsets of the planes in bytes", g_param_spec_uint ("plane-offset", "Plane offset", "Offset of the n-th plane in bytes", From 4cde35553f93c823ba8ad10a5d0e74ef6ce1548d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 18 Feb 2017 20:18:50 +0200 Subject: [PATCH 29/29] rawbaseparse: Drop incomplete frames at EOS See https://bugzilla.gnome.org/show_bug.cgi?id=773666 This would ideally be solved in baseparse but that requires further thought at this point, and in the meantime it would be good to have rawbaseparse not assert on this but handle it gracefully instead. --- gst/rawparse/gstrawbaseparse.c | 12 +++++++++++ tests/check/elements/rawvideoparse.c | 32 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/gst/rawparse/gstrawbaseparse.c b/gst/rawparse/gstrawbaseparse.c index f5bb202e61..b41e4fa1c5 100644 --- a/gst/rawparse/gstrawbaseparse.c +++ b/gst/rawparse/gstrawbaseparse.c @@ -528,6 +528,18 @@ gst_raw_base_parse_handle_frame (GstBaseParse * parse, in_size = gst_buffer_get_size (frame->buffer); + /* drop incomplete frame at the end of the stream + * https://bugzilla.gnome.org/show_bug.cgi?id=773666 + */ + if (GST_BASE_PARSE_DRAINING (parse) && in_size < frame_size) { + GST_DEBUG_OBJECT (raw_base_parse, + "Dropping %" G_GSIZE_FORMAT " bytes at EOS", in_size); + frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; + GST_RAW_BASE_PARSE_CONFIG_MUTEX_UNLOCK (raw_base_parse); + + return gst_base_parse_finish_frame (parse, frame, in_size); + } + /* gst_base_parse_set_min_frame_size() is called when the current * configuration changes and the change affects the frame size. This * means that a buffer must contain at least as many bytes as indicated diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c index 477e7678e8..bca199921e 100644 --- a/tests/check/elements/rawvideoparse.c +++ b/tests/check/elements/rawvideoparse.c @@ -607,6 +607,37 @@ GST_START_TEST (test_change_caps) GST_END_TEST; +GST_START_TEST (test_incomplete_last_buffer) +{ + GstVideoInfo vinfo; + GstCaps *caps; + + /* Start processing with the sink caps config active, using the + * default width/height/format and 25 Hz frame rate for the caps. + * Push some data, then change caps (25 Hz -> 50 Hz). + * Check that the changed caps are handled properly. */ + + gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH, + TEST_HEIGHT); + GST_VIDEO_INFO_FPS_N (&vinfo) = 25; + GST_VIDEO_INFO_FPS_D (&vinfo) = 1; + caps = gst_video_info_to_caps (&vinfo); + + setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES); + + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 0, + GST_MSECOND * 40, 1, 0, 0, 0); + push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 40, + GST_MSECOND * 40, 2, 1, 1, 0); + push_data_and_check_output (&sinkcaps_ctx, 100, 192, GST_MSECOND * 40, + GST_MSECOND * 40, 2, 1, 1, 0); + gst_pad_push_event (mysrcpad, gst_event_new_eos ()); + fail_unless_equals_int (g_list_length (buffers), 2); + + cleanup_rawvideoparse (); +} + +GST_END_TEST; static Suite * rawvideoparse_suite (void) @@ -621,6 +652,7 @@ rawvideoparse_suite (void) tcase_add_test (tc_chain, test_push_with_no_framerate); tcase_add_test (tc_chain, test_computed_plane_strides); tcase_add_test (tc_chain, test_change_caps); + tcase_add_test (tc_chain, test_incomplete_last_buffer); return s; }