From d8b285d7fde00119654f099a465a41221a19c671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 23 Jul 2009 09:52:36 +0200 Subject: mpegdemux: Implement query type function for the src pads --- gst/mpegdemux/gstmpegdemux.c | 16 ++++++++++++++++ gst/mpegdemux/gstmpegtsdemux.c | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 75d5960f..85e8325c 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -211,6 +211,7 @@ static void gst_flups_demux_loop (GstPad * pad); static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event); static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query); +static const GstQueryType *gst_flups_demux_src_query_type (GstPad * pad); static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element, GstStateChange transition); @@ -454,6 +455,8 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) GST_DEBUG_FUNCPTR (gst_flups_demux_src_event)); gst_pad_set_query_function (stream->pad, GST_DEBUG_FUNCPTR (gst_flups_demux_src_query)); + gst_pad_set_query_type_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_flups_demux_src_query_type)); gst_pad_use_fixed_caps (stream->pad); gst_pad_set_caps (stream->pad, caps); gst_caps_unref (caps); @@ -1283,6 +1286,19 @@ gst_flups_demux_src_event (GstPad * pad, GstEvent * event) return res; } +static const GstQueryType * +gst_flups_demux_src_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_SEEKING, + 0 + }; + + return types; +} + static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query) { diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 8de7cc75..c5a160ec 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -212,6 +212,7 @@ static gboolean gst_mpegts_demux_sink_setcaps (GstPad * pad, GstCaps * caps); static GstClock *gst_mpegts_demux_provide_clock (GstElement * element); static gboolean gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query); +static const GstQueryType *gst_mpegts_demux_src_pad_query_type (GstPad * pad); static GstStateChangeReturn gst_mpegts_demux_change_state (GstElement * element, GstStateChange transition); @@ -713,6 +714,8 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, gst_caps_unref (caps); gst_pad_set_query_function (stream->pad, GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query)); + gst_pad_set_query_type_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_pad_query_type)); gst_pad_set_event_function (stream->pad, GST_DEBUG_FUNCPTR (gst_mpegts_demux_src_event)); g_free (name); @@ -2701,6 +2704,19 @@ gst_mpegts_demux_provide_clock (GstElement * element) return NULL; } +static const GstQueryType * +gst_mpegts_demux_src_pad_query_type (GstPad * pad) +{ + static const GstQueryType types[] = { + GST_QUERY_LATENCY, + GST_QUERY_DURATION, + GST_QUERY_SEEKING, + 0 + }; + + return types; +} + static gboolean gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) { -- cgit v1.2.1 From da7263b51ff0b5ea801098c7eb380f7b34d70996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 23 Jul 2009 09:58:38 +0200 Subject: mpegdemux: Remove some backward compatibility code Also we always require liboil so use it unconditionally. --- gst/mpegdemux/gstmpegdemux.c | 73 ------------------------------------------ gst/mpegdemux/gstmpegtsdemux.c | 44 +------------------------ 2 files changed, 1 insertion(+), 116 deletions(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 85e8325c..f8cba31b 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -77,42 +77,6 @@ typedef enum GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug); #define GST_CAT_DEFAULT (gstflupsdemux_debug) -#ifndef GST_CHECK_VERSION -#define GST_CHECK_VERSION(major,minor,micro) \ - (GST_VERSION_MAJOR > (major) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \ - GST_VERSION_MICRO >= (micro))) -#endif - -#if !GST_CHECK_VERSION(0,10,9) -#define GST_BUFFER_IS_DISCONT(buffer) \ - (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) -#endif - -#if GST_CHECK_VERSION(0,10,6) -#define HAVE_NEWSEG_FULL -#else -static GstBuffer * -gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes) -{ - GstBuffer *buf = NULL; - - if (G_UNLIKELY (nbytes > adapter->size)) - return NULL; - - buf = gst_buffer_new_and_alloc (nbytes); - - if (G_UNLIKELY (!buf)) - return NULL; - - /* Slow... */ - memcpy (GST_BUFFER_DATA (buf), gst_adapter_peek (adapter, nbytes), nbytes); - - return buf; -} -#endif - /* elementfactory information */ static GstElementDetails flups_demux_details = { "The Fluendo MPEG Program Stream Demuxer", @@ -548,7 +512,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, else time = 0; -#ifdef HAVE_NEWSEG_FULL GST_INFO_OBJECT (demux, "sending new segment: rate %g applied_rate %g " "start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT ", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT, @@ -559,16 +522,6 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, newsegment = gst_event_new_new_segment_full (FALSE, demux->sink_segment.rate, demux->sink_segment.applied_rate, GST_FORMAT_TIME, start, stop, time); -#else - GST_INFO_OBJECT (demux, "sending new segment: rate %g " - "start: %" GST_TIME_FORMAT ", stop: %" GST_TIME_FORMAT - ", time: %" GST_TIME_FORMAT " to pad %" GST_PTR_FORMAT, - demux->sink_segment.rate, GST_TIME_ARGS (start), - GST_TIME_ARGS (stop), GST_TIME_ARGS (time), stream->pad); - - newsegment = gst_event_new_new_segment (FALSE, - demux->sink_segment.rate, GST_FORMAT_TIME, start, stop, time); -#endif gst_pad_push_event (stream->pad, newsegment); @@ -850,10 +803,8 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) GstEvent *event = NULL; guint64 base_time; -#if POST_10_10 GST_INFO_OBJECT (demux, "closing running segment %" GST_SEGMENT_FORMAT, &demux->src_segment); -#endif /* FIXME: Need to send a different segment-close to each pad where the * last_seg_start != clock_time_none, as that indicates a sparse-stream @@ -945,7 +896,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) /* Close current segment */ gst_flups_demux_close_segment (demux); -#ifdef HAVE_NEWSEG_FULL { gdouble arate; @@ -963,19 +913,6 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) } } -#else - gst_event_parse_new_segment (event, &update, &rate, &format, - &start, &stop, &time); - gst_segment_set_newsegment (&demux->sink_segment, update, rate, - format, start, stop, time); - if (format == GST_FORMAT_BYTES && demux->scr_rate_n != G_MAXUINT64 - && demux->scr_rate_d != G_MAXUINT64) { - - gst_segment_set_newsegment (&demux->src_segment, update, rate, - GST_FORMAT_TIME, BYTES_TO_GSTTIME (start), BYTES_TO_GSTTIME (stop), - BYTES_TO_GSTTIME (time)); - } -#endif GST_INFO_OBJECT (demux, "received new segment: rate %g " "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT @@ -1099,10 +1036,8 @@ gst_flups_demux_do_seek (GstFluPSDemux * demux, GstSegment * seeksegment) scr = MAX (demux->first_scr, scr); fscr = scr; -#if POST_10_10 GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT ", trying to go at SCR: %" G_GUINT64_FORMAT, &demux->sink_segment, scr); -#endif offset = MIN (gst_util_uint64_scale (scr, scr_rate_n, scr_rate_d), demux->sink_segment.stop); @@ -1177,19 +1112,15 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) /* Work on a copy until we are sure the seek succeeded. */ memcpy (&seeksegment, &demux->src_segment, sizeof (GstSegment)); -#if POST_10_10 GST_DEBUG_OBJECT (demux, "segment before configure %" GST_SEGMENT_FORMAT, &demux->src_segment); -#endif /* Apply the seek to our segment */ gst_segment_set_seek (&seeksegment, rate, format, flags, start_type, start, stop_type, stop, &update); -#if POST_10_10 GST_DEBUG_OBJECT (demux, "seek segment configured %" GST_SEGMENT_FORMAT, &seeksegment); -#endif if (flush || seeksegment.last_stop != demux->src_segment.last_stop) { /* Do the actual seeking */ @@ -1207,10 +1138,8 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) /* update the rate in our src segment */ demux->sink_segment.rate = rate; -#if POST_10_10 GST_DEBUG_OBJECT (demux, "seek segment adjusted %" GST_SEGMENT_FORMAT, &seeksegment); -#endif if (flush) { /* Stop flushing, the sinks are at time 0 now */ @@ -2572,12 +2501,10 @@ gst_flups_sink_get_duration (GstFluPSDemux * demux) gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME, demux->src_segment.start); } -#if POST_10_10 GST_INFO_OBJECT (demux, "sink segment configured %" GST_SEGMENT_FORMAT, &demux->sink_segment); GST_INFO_OBJECT (demux, "src segment configured %" GST_SEGMENT_FORMAT, &demux->src_segment); -#endif res = TRUE; diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index c5a160ec..eb40b186 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -48,37 +48,17 @@ #include #include -#ifdef USE_LIBOIL #include -#endif #include "gstmpegdefs.h" #include "gstmpegtsdemux.h" #include "flutspatinfo.h" #include "flutspmtinfo.h" -#ifndef GST_CHECK_VERSION -#define GST_CHECK_VERSION(major,minor,micro) \ - (GST_VERSION_MAJOR > (major) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \ - (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && \ - GST_VERSION_MICRO >= (micro))) -#endif - -#ifndef GST_BUFFER_IS_DISCONT -#define GST_BUFFER_IS_DISCONT(buffer) \ - (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) -#endif - GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug); #define GST_CAT_DEFAULT (gstmpegtsdemux_debug) /* elementfactory information */ -#ifdef USE_LIBOIL -#define LONGNAME "The Fluendo MPEG Transport stream demuxer (liboil build)" -#else -#define LONGNAME "The Fluendo MPEG Transport stream demuxer" -#endif #ifndef __always_inline #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) @@ -95,7 +75,7 @@ GST_DEBUG_CATEGORY_STATIC (gstmpegtsdemux_debug); #endif static GstElementDetails mpegts_demux_details = { - LONGNAME, + "The Fluendo MPEG Transport stream demuxer", "Codec/Demuxer", "Demultiplexes MPEG2 Transport Streams", "Wim Taymans " @@ -356,9 +336,7 @@ gst_mpegts_demux_init (GstMpegTSDemux * demux) demux->pcr[1] = -1; demux->cache_duration = GST_CLOCK_TIME_NONE; demux->base_pts = GST_CLOCK_TIME_NONE; -#ifdef USE_LIBOIL oil_init (); -#endif } static void @@ -858,10 +836,6 @@ gst_mpegts_demux_send_tags_for_stream (GstMpegTSDemux * demux, } } -#ifndef GST_FLOW_IS_SUCCESS -#define GST_FLOW_IS_SUCCESS(ret) ((ret) >= GST_FLOW_OK) -#endif - static GstFlowReturn gst_mpegts_demux_combine_flows (GstMpegTSDemux * demux, GstMpegTSStream * stream, GstFlowReturn ret) @@ -1540,11 +1514,7 @@ gst_mpegts_stream_parse_private_section (GstMpegTSStream * stream, /* just dump this down the pad */ if (gst_pad_alloc_buffer (stream->pad, 0, datalen, NULL, &buffer) == GST_FLOW_OK) { -#ifdef USE_LIBOIL oil_memcpy (buffer->data, data, datalen); -#else - memcpy (buffer->data, data, datalen); -#endif gst_pad_push (stream->pad, buffer); } @@ -2107,11 +2077,7 @@ gst_mpegts_stream_pes_buffer_push (GstMpegTSStream * stream, stream->pes_buffer_used = 0; } out_data = GST_BUFFER_DATA (stream->pes_buffer) + stream->pes_buffer_used; -#ifdef USE_LIBOIL oil_memcpy (out_data, in_data, in_size); -#else - memcpy (out_data, in_data, in_size); -#endif stream->pes_buffer_used += in_size; done: return ret; @@ -2139,11 +2105,7 @@ gst_mpegts_demux_push_fragment (GstMpegTSStream * stream, { GstFlowReturn ret; GstBuffer *es_buf = gst_buffer_new_and_alloc (in_size); -#ifdef USE_LIBOIL oil_memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size); -#else - memcpy (GST_BUFFER_DATA (es_buf), in_data, in_size); -#endif ret = gst_pes_filter_push (&stream->filter, es_buf); /* If PES filter return ok then PES fragment buffering @@ -2283,11 +2245,7 @@ gst_mpegts_demux_parse_stream (GstMpegTSDemux * demux, GstMpegTSStream * stream, /* FIXME: try to use data directly instead of creating a buffer and pushing in into adapter at section filter */ sec_buf = gst_buffer_new_and_alloc (datalen); -#ifdef USE_LIBOIL oil_memcpy (GST_BUFFER_DATA (sec_buf), data, datalen); -#else - memcpy (GST_BUFFER_DATA (sec_buf), data, datalen); -#endif if (gst_section_filter_push (&stream->section_filter, payload_unit_start_indicator, continuity_counter, sec_buf)) { GST_DEBUG_OBJECT (demux, "section finished"); -- cgit v1.2.1 From 2641cd9d94433820796c613d17c9a6227c2a8f27 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Fri, 24 Jul 2009 14:52:28 -0300 Subject: asfmux: Adds new plugin asfmux Adds the brand new asfmux plugin, containing 3 elements: asfmux, rtpasfpay and asfparse. This plugin was developed as a GSoC 2009 project, with David Schleef as the mentor and Thiago Santos as the student. --- gst/asfmux/Makefile.am | 24 + gst/asfmux/gstasf.c | 51 + gst/asfmux/gstasfmux.c | 2259 ++++++++++++++++++++++++++++++++++++++++++++ gst/asfmux/gstasfmux.h | 159 ++++ gst/asfmux/gstasfobjects.c | 803 ++++++++++++++++ gst/asfmux/gstasfobjects.h | 190 ++++ gst/asfmux/gstasfparse.c | 623 ++++++++++++ gst/asfmux/gstasfparse.h | 88 ++ gst/asfmux/gstrtpasfpay.c | 446 +++++++++ gst/asfmux/gstrtpasfpay.h | 87 ++ 10 files changed, 4730 insertions(+) create mode 100644 gst/asfmux/Makefile.am create mode 100644 gst/asfmux/gstasf.c create mode 100644 gst/asfmux/gstasfmux.c create mode 100644 gst/asfmux/gstasfmux.h create mode 100644 gst/asfmux/gstasfobjects.c create mode 100644 gst/asfmux/gstasfobjects.h create mode 100644 gst/asfmux/gstasfparse.c create mode 100644 gst/asfmux/gstasfparse.h create mode 100644 gst/asfmux/gstrtpasfpay.c create mode 100644 gst/asfmux/gstrtpasfpay.h (limited to 'gst') diff --git a/gst/asfmux/Makefile.am b/gst/asfmux/Makefile.am new file mode 100644 index 00000000..0246102b --- /dev/null +++ b/gst/asfmux/Makefile.am @@ -0,0 +1,24 @@ +# plugindir is set in configure + +plugin_LTLIBRARIES = libgstasfmux.la + +# sources used to compile this plug-in +libgstasfmux_la_SOURCES = gstasfmux.c \ + gstasfobjects.c \ + gstasfparse.c \ + gstrtpasfpay.c \ + gstasf.c + +# flags used to compile this plugin +# add other _CFLAGS and _LIBS as needed +libgstasfmux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) +libgstasfmux_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + -lgstrtp-@GST_MAJORMINOR@ +libgstasfmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +#libgstasfmux_la_LIBTOOLFLAGS = --tag=disable-static + +# headers we need but don't want installed +noinst_HEADERS = gstasfmux.h \ + gstasfobjects.h \ + gstasfparse.h \ + gstrtpasfpay.h diff --git a/gst/asfmux/gstasf.c b/gst/asfmux/gstasf.c new file mode 100644 index 00000000..fb935172 --- /dev/null +++ b/gst/asfmux/gstasf.c @@ -0,0 +1,51 @@ +/* GStreamer + * Copyright (C) 2009 Thiago Santos + * + * gstasf.c: plugin registering + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstasfmux.h" +#include "gstrtpasfpay.h" +#include "gstasfparse.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_asf_mux_plugin_init (plugin)) { + return FALSE; + } + if (!gst_rtp_asf_pay_plugin_init (plugin)) { + return FALSE; + } + if (!gst_asf_parse_plugin_init (plugin)) { + return FALSE; + } + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "asfmux", + "ASF Muxer Plugin", + plugin_init, VERSION, "LGPL", "gsoc2009 package", "embedded.ufcg.edu.br") diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c new file mode 100644 index 00000000..9e8218df --- /dev/null +++ b/gst/asfmux/gstasfmux.c @@ -0,0 +1,2259 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* based on: + * - avimux (by Ronald Bultje and Mark Nauwelaerts) + * - qtmux (by Thiago Santos and Mark Nauwelaerts) + */ + +/** + * SECTION:element-asfmux + * + * Muxes window media content into an ASF file/stream. + * + * + * Example launch lines + * (write everything in one line, without the backslash characters) + * |[ + * gst-launch videotestsrc num-buffers=250 \ + * ! "video/x-raw-yuv,format=(fourcc)I420,framerate=(fraction)25/1" ! ffenc_wmv2 \ + * ! asfmux name=mux ! filesink location=test.asf \ + * audiotestsrc num-buffers=440 ! audioconvert \ + * ! "audio/x-raw-int,rate=44100" ! ffenc_wmav2 ! mux. + * ]| This creates an ASF file containing an WMV video stream + * with a test picture and WMA audio stream of a test sound. + * + * Live streaming + * asfmux and rtpasfpay are capable of generating a live asf stream. + * asfmux has to set its 'is-live' property to true, because in this + * mode it won't try to seek back to the start of the file to replace + * some fields that couldn't be known at the file start. In this mode, + * it won't also send indexes at the end of the data packets (the actual + * media content) + * the following pipelines are an example of this usage. + * (write everything in one line, without the backslash characters) + * Server (sender) + * |[ + * gst-launch -ve videotestsrc ! ffenc_wmv2 ! asfmux name=mux is-live=true \ + * ! rtpasfpay ! udpsink host=127.0.0.1 port=3333 \ + * audiotestsrc ! ffenc_wmav2 ! mux. + * ]| + * Client (receiver) + * |[ + * gst-launch udpsrc port=3333 ! "caps_from_rtpasfpay_at_sender" \ + * ! rtpasfdepay ! decodebin2 name=d ! queue \ + * ! ffmpegcolorspace ! autovideosink \ + * d. ! queue ! audioconvert ! autoaudiosink + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstasfmux.h" + +#define DEFAULT_SIMPLE_INDEX_TIME_INTERVAL G_GUINT64_CONSTANT (10000000) +#define MAX_PAYLOADS_IN_A_PACKET 63 + +GST_DEBUG_CATEGORY_STATIC (asfmux_debug); +#define GST_CAT_DEFAULT asfmux_debug + +enum +{ + PROP_0, + PROP_PACKET_SIZE, + PROP_PREROLL, + PROP_MERGE_STREAM_TAGS, + PROP_PADDING, + PROP_IS_LIVE +}; + +/* Stores a tag list for the available/known tags + * in an ASF file + * Also stores the sizes those entries would use in a + * content description object and extended content + * description object + */ +typedef struct +{ + GstTagList *tags; + guint64 cont_desc_size; + guint64 ext_cont_desc_size; +} GstAsfTags; + +/* Helper struct to be used as user data + * in gst_tag_foreach function for writing + * each tag for the metadata objects + * + * stream_num is used only for stream dependent tags + */ +typedef struct +{ + GstAsfMux *asfmux; + guint8 *buf; + guint16 count; + guint64 size; + guint16 stream_num; +} GstAsfExtContDescData; + +typedef GstAsfExtContDescData GstAsfMetadataObjData; + +#define DEFAULT_PACKET_SIZE 4800 +#define DEFAULT_PREROLL 5000 +#define DEFAULT_MERGE_STREAM_TAGS TRUE +#define DEFAULT_PADDING 0 +#define DEFAULT_IS_LIVE FALSE + +static const GstElementDetails gst_asf_mux_details = +GST_ELEMENT_DETAILS ("ASF muxer", + "Codec/Muxer", + "Muxes audio and video into an ASF stream", + "Thiago Santos "); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true") + ); + +static GstStaticPadTemplate video_sink_factory = +GST_STATIC_PAD_TEMPLATE ("video_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("video/x-wmv, wmvversion = (int) [1,3]")); + +static GstStaticPadTemplate audio_sink_factory = + GST_STATIC_PAD_TEMPLATE ("audio_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("audio/x-wma, wmaversion = (int) [1,3]; " + "audio/mpeg, layer = (int) 3, mpegversion = (int) 1, " + "channels = (int) [1,2], rate = (int) [8000,96000]")); + +static void gst_asf_mux_base_init (gpointer g_class); +static void gst_asf_mux_class_init (GstAsfMuxClass * klass); +static void gst_asf_mux_init (GstAsfMux * asfmux); + +static GstPad *gst_asf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name); +static void gst_asf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_asf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_asf_mux_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_asf_mux_sink_event (GstPad * pad, GstEvent * event); + +static void gst_asf_mux_pad_reset (GstAsfPad * data); +static GstFlowReturn gst_asf_mux_collected (GstCollectPads * collect, + gpointer data); + +static GstElementClass *parent_class = NULL; + +GType +gst_asf_mux_get_type (void) +{ + static GType asfmux_type = 0; + + if (!asfmux_type) { + static const GTypeInfo asfmux_info = { + sizeof (GstAsfMuxClass), + gst_asf_mux_base_init, + NULL, + (GClassInitFunc) gst_asf_mux_class_init, + NULL, + NULL, + sizeof (GstAsfMux), + 0, + (GInstanceInitFunc) gst_asf_mux_init, + }; + static const GInterfaceInfo tag_setter_info = { + NULL, + NULL, + NULL + }; + + asfmux_type = + g_type_register_static (GST_TYPE_ELEMENT, "GstAsfMux", &asfmux_info, 0); + g_type_add_interface_static (asfmux_type, GST_TYPE_TAG_SETTER, + &tag_setter_info); + } + return asfmux_type; +} + +static void +gst_asf_mux_reset (GstAsfMux * asfmux) +{ + asfmux->state = GST_ASF_MUX_STATE_NONE; + asfmux->stream_number = 0; + asfmux->data_object_size = 0; + asfmux->data_object_position = 0; + asfmux->file_properties_object_position = 0; + asfmux->total_data_packets = 0; + asfmux->file_size = 0; + asfmux->packet_size = 0; + + if (asfmux->payloads) { + GSList *walk; + for (walk = asfmux->payloads; walk; walk = g_slist_next (walk)) { + gst_asf_payload_free ((AsfPayload *) walk->data); + walk->data = NULL; + } + g_slist_free (asfmux->payloads); + } + asfmux->payloads = NULL; + asfmux->payload_data_size = 0; + + asfmux->file_id.v1 = 0; + asfmux->file_id.v2 = 0; + asfmux->file_id.v3 = 0; + asfmux->file_id.v4 = 0; + + gst_tag_setter_reset_tags (GST_TAG_SETTER (asfmux)); +} + +static void +gst_asf_mux_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&audio_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&video_sink_factory)); + + gst_element_class_set_details (element_class, &gst_asf_mux_details); + + GST_DEBUG_CATEGORY_INIT (asfmux_debug, "asfmux", 0, "Muxer for ASF streams"); +} + +static void +gst_asf_mux_finalize (GObject * object) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + + gst_asf_mux_reset (asfmux); + gst_object_unref (asfmux->collect); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_asf_mux_class_init (GstAsfMuxClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gst_asf_mux_get_property; + gobject_class->set_property = gst_asf_mux_set_property; + gobject_class->finalize = gst_asf_mux_finalize; + + g_object_class_install_property (gobject_class, PROP_PACKET_SIZE, + g_param_spec_uint ("packet-size", "Packet size", + "The ASF packets size (bytes)", + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + 1, G_MAXUINT32, + DEFAULT_PACKET_SIZE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_PREROLL, + g_param_spec_uint64 ("preroll", "Preroll", + "The preroll time (milisecs)", + 0, G_MAXUINT64, + DEFAULT_PREROLL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_MERGE_STREAM_TAGS, + g_param_spec_boolean ("merge-stream-tags", "Merge Stream Tags", + "If the stream metadata (received as events in the sink) should be " + "merged to the main file metadata.", + DEFAULT_MERGE_STREAM_TAGS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_PADDING, + g_param_spec_uint64 ("padding", "Padding", + "Size of the padding object to be added to the end of the header. " + "If this less than 24 (the smaller size of an ASF object), " + "no padding is added.", + 0, G_MAXUINT64, + DEFAULT_PADDING, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (gobject_class, PROP_IS_LIVE, + g_param_spec_boolean ("is-live", "Is Live", + "If this stream should be threated as a live, meaning that it " + "doesn't need indexes nor late update of headers.", + DEFAULT_IS_LIVE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_asf_mux_request_new_pad); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_asf_mux_change_state); +} + +static void +gst_asf_mux_init (GstAsfMux * asfmux) +{ + GstCaps *caps; + + asfmux->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + caps = gst_caps_copy (gst_pad_get_pad_template_caps (asfmux->srcpad)); + gst_pad_set_caps (asfmux->srcpad, caps); + gst_caps_unref (caps); + gst_pad_use_fixed_caps (asfmux->srcpad); + gst_element_add_pad (GST_ELEMENT (asfmux), asfmux->srcpad); + + asfmux->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (asfmux->collect, + (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_asf_mux_collected), + asfmux); + + asfmux->payloads = NULL; + asfmux->prop_packet_size = DEFAULT_PACKET_SIZE; + asfmux->prop_preroll = DEFAULT_PREROLL; + asfmux->prop_merge_stream_tags = DEFAULT_MERGE_STREAM_TAGS; + asfmux->prop_padding = DEFAULT_PADDING; + asfmux->prop_is_live = DEFAULT_IS_LIVE; + gst_asf_mux_reset (asfmux); +} + +static gboolean +gst_asf_mux_sink_event (GstPad * pad, GstEvent * event) +{ + gboolean ret; + GstAsfMux *asfmux; + GstAsfPad *asfpad = (GstAsfPad *) gst_pad_get_element_private (pad); + + asfmux = GST_ASF_MUX_CAST (gst_pad_get_parent (pad)); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG:{ + GST_DEBUG_OBJECT (asfmux, "received tag event"); + /* we discard tag events that come after we started + * writing the headers, because tags are to be in + * the headers + */ + if (asfmux->state == GST_ASF_MUX_STATE_STARTED) { + GstTagList *list = NULL; + gst_event_parse_tag (event, &list); + if (asfmux->merge_stream_tags) { + GstTagSetter *setter = GST_TAG_SETTER (asfmux); + const GstTagMergeMode mode = + gst_tag_setter_get_tag_merge_mode (setter); + gst_tag_setter_merge_tags (setter, list, mode); + } else { + if (asfpad->taglist == NULL) { + asfpad->taglist = gst_tag_list_new (); + } + gst_tag_list_insert (asfpad->taglist, list, GST_TAG_MERGE_REPLACE); + } + } + break; + } + default: + break; + } + + ret = asfmux->collect_event (pad, event); + gst_object_unref (asfmux); + return ret; +} + +/** + * gst_asf_mux_push_buffer: + * @asfmux: #GstAsfMux that should push the buffer + * @buf: #GstBuffer to be pushed + * + * Pushes a buffer downstream and adds its size to the total file size + * + * Returns: the result of #gst_pad_push on the buffer + */ +static GstFlowReturn +gst_asf_mux_push_buffer (GstAsfMux * asfmux, GstBuffer * buf) +{ + GstFlowReturn ret; + gst_buffer_set_caps (buf, GST_PAD_CAPS (asfmux->srcpad)); + ret = gst_pad_push (asfmux->srcpad, buf); + if (ret == GST_FLOW_OK) + asfmux->file_size += GST_BUFFER_SIZE (buf); + return ret; +} + +/** + * content_description_calc_size_for_tag: + * @taglist: the #GstTagList that contains the tag + * @tag: the tag's name + * @user_data: a #GstAsfTags struct for putting the results + * + * Function that has the #GstTagForEach signature and + * is used to calculate the size in bytes for each tag + * that can be contained in asf's content description object + * and extended content description object. This size is added + * to the total size for each of that objects in the #GstAsfTags + * struct passed in the user_data pointer. + */ +static void +content_description_calc_size_for_tag (const GstTagList * taglist, + const gchar * tag, gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfTags *asftags = (GstAsfTags *) user_data; + guint content_size; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + + text = g_value_get_string (&value); + /* +1 -> because of the \0 at the end + * 2* -> because we have uft8, and asf demands utf16 + */ + content_size = 2 * (1 + g_utf8_strlen (text, -1)); + + if (gst_asf_tag_present_in_content_description (tag)) { + asftags->cont_desc_size += content_size; + } + } + break; + case ASF_TAG_TYPE_DWORD: + content_size = 4; + break; + default: + GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + if (asftag) { + /* size of the tag content in utf16 + + * size of the tag name + + * 3 uint16 (size of the tag name string, + * size of the tag content string and + * type of content + */ + asftags->ext_cont_desc_size += content_size + + (g_utf8_strlen (asftag, -1) + 1) * 2 + 6; + } + gst_tag_list_add_value (asftags->tags, GST_TAG_MERGE_REPLACE, tag, &value); + g_value_reset (&value); +} + +/* FIXME + * it is awful to keep track of the size here + * and get the same tags in the writing function */ +/** + * gst_asf_mux_get_content_description_tags: + * @asfmux: #GstAsfMux to have its tags proccessed + * @asftags: #GstAsfTags to hold the results + * + * Inspects the tags received by the GstTagSetter interface + * or possibly by sink tag events and calculates the total + * size needed for the default and extended content description objects. + * This results and a copy of the #GstTagList + * are stored in the #GstAsfTags. We store a copy so that + * the sizes estimated here mantain the same until they are + * written to the asf file. + */ +static void +gst_asf_mux_get_content_description_tags (GstAsfMux * asfmux, + GstAsfTags * asftags) +{ + const GstTagList *tags; + GstTagList *taglist = NULL; + + tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (asfmux)); + if (tags && !gst_tag_list_is_empty (tags)) { + if (asftags->tags != NULL) { + gst_tag_list_free (asftags->tags); + } + asftags->tags = gst_tag_list_new (); + asftags->cont_desc_size = 0; + asftags->ext_cont_desc_size = 0; + taglist = asftags->tags; + + GST_DEBUG_OBJECT (asfmux, "Processing tags"); + gst_tag_list_foreach (tags, content_description_calc_size_for_tag, asftags); + } else { + GST_DEBUG_OBJECT (asfmux, "No tags received"); + } + + if (asftags->cont_desc_size > 0) { + asftags->cont_desc_size += ASF_CONTENT_DESCRIPTION_OBJECT_SIZE; + } + if (asftags->ext_cont_desc_size > 0) { + asftags->ext_cont_desc_size += ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE; + } +} + +/** + * add_metadata_tag_size: + * @taglist: #GstTagList + * @tag: tag name + * @user_data: pointer to a guint to store the result + * + * GstTagForeachFunc implementation that accounts the size of + * each tag in the taglist and adds them to the guint pointed + * by the user_data + */ +static void +add_metadata_tag_size (const GstTagList * taglist, const gchar * tag, + gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + guint content_size; + guint *total_size = (guint *) user_data; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + + text = g_value_get_string (&value); + /* +1 -> because of the \0 at the end + * 2* -> because we have uft8, and asf demands utf16 + */ + content_size = 2 * (1 + g_utf8_strlen (text, -1)); + } + break; + case ASF_TAG_TYPE_DWORD: + content_size = 4; + break; + default: + GST_WARNING ("Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + /* size of reserved (2) + + * size of stream number (2) + + * size of the tag content in utf16 + + * size of the tag name + + * 2 uint16 (size of the tag name string and type of content) + + * 1 uint32 (size of the data) + */ + *total_size += + 4 + content_size + (g_utf8_strlen (asftag, -1) + 1) * 2 + 4 + 4; + g_value_reset (&value); +} + +/** + * gst_asf_mux_get_metadata_object_size: + * @asfmux: #GstAsfMux + * @asfpad: pad for which the metadata object size should be calculated + * + * Calculates the size of the metadata object for the tags of the stream + * handled by the asfpad in the parameter + * + * Returns: The size calculated + */ +static guint +gst_asf_mux_get_metadata_object_size (GstAsfMux * asfmux, GstAsfPad * asfpad) +{ + guint size = ASF_METADATA_OBJECT_SIZE; + if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist)) + return 0; + + gst_tag_list_foreach (asfpad->taglist, add_metadata_tag_size, &size); + return size; +} + +/** + * gst_asf_mux_get_headers_size: + * @asfmux: #GstAsfMux + * + * Calculates the size of the headers of the asf stream + * to be generated by this #GstAsfMux. + * Its used for determining the size of the buffer to allocate + * to exactly fit the headers in. + * Padding and metadata objects sizes are not included. + * + * Returns: the calculated size + */ +static guint +gst_asf_mux_get_headers_size (GstAsfMux * asfmux) +{ + GSList *walk; + gint stream_num = 0; + guint size = ASF_HEADER_OBJECT_SIZE + + ASF_FILE_PROPERTIES_OBJECT_SIZE + ASF_HEADER_EXTENSION_OBJECT_SIZE; + + /* per stream data */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + GstAsfPad *asfpad = (GstAsfPad *) walk->data; + + if (asfpad->is_audio) + size += ASF_AUDIO_SPECIFIC_DATA_SIZE; + else + size += ASF_VIDEO_SPECIFIC_DATA_SIZE; + + if (asfpad->codec_data) + size += GST_BUFFER_SIZE (asfpad->codec_data); + + stream_num++; + } + size += stream_num * (ASF_STREAM_PROPERTIES_OBJECT_SIZE + + ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE); + + return size; +} + +/** + * gst_asf_mux_write_header_object: + * @asfmux: + * @buf: pointer to the data pointer + * @size: size of the header object + * @child_objects: number of children objects inside the main header object + * + * Writes the main asf header object start. The buffer pointer + * is incremented to the next writing position. + */ +static void +gst_asf_mux_write_header_object (GstAsfMux * asfmux, guint8 ** buf, + guint64 size, guint32 child_objects) +{ + gst_asf_put_guid (*buf, guids[ASF_HEADER_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, size); /* object size */ + GST_WRITE_UINT32_LE (*buf + 24, child_objects); /* # of child objects */ + GST_WRITE_UINT8 (*buf + 28, 0x01); /* reserved */ + GST_WRITE_UINT8 (*buf + 29, 0x02); /* reserved */ + *buf += ASF_HEADER_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_file_properties: + * @asfmux: + * @buf: pointer to the data pointer + * + * Writes the file properties object to the buffer. The buffer pointer + * is incremented to the next writing position. + */ +static void +gst_asf_mux_write_file_properties (GstAsfMux * asfmux, guint8 ** buf) +{ + gst_asf_put_guid (*buf, guids[ASF_FILE_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_FILE_PROPERTIES_OBJECT_SIZE); /* object size */ + gst_asf_put_guid (*buf + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (*buf + 40, 0); /* file size - needs update */ + gst_asf_put_time (*buf + 48, gst_asf_get_current_time ()); /* creation time */ + GST_WRITE_UINT64_LE (*buf + 56, 0); /* data packets - needs update */ + GST_WRITE_UINT64_LE (*buf + 64, 0); /* play duration - needs update */ + GST_WRITE_UINT64_LE (*buf + 72, 0); /* send duration - needs update */ + GST_WRITE_UINT64_LE (*buf + 80, 0); /* preroll */ + GST_WRITE_UINT32_LE (*buf + 88, 0x1); /* flags - broadcast on */ + GST_WRITE_UINT32_LE (*buf + 92, asfmux->packet_size); /* minimum data packet size */ + GST_WRITE_UINT32_LE (*buf + 96, asfmux->packet_size); /* maximum data packet size */ + GST_WRITE_UINT32_LE (*buf + 100, 0); /* maximum bitrate TODO */ + + *buf += ASF_FILE_PROPERTIES_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_stream_properties: + * @param asfmux: + * @param buf: pointer to the data pointer + * @param asfpad: Pad that handles the stream + * + * Writes the stream properties object in the buffer + * for the stream handled by the #GstAsfPad passed. + * The pointer is incremented to the next writing position + */ +static void +gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + guint32 codec_data_length = 0; + guint32 media_specific_data_length = 0; + guint16 flags = 0; + + /* codec specific data length */ + if (asfpad->codec_data) + codec_data_length = GST_BUFFER_SIZE (asfpad->codec_data); + if (asfpad->is_audio) + media_specific_data_length = ASF_AUDIO_SPECIFIC_DATA_SIZE; + else + media_specific_data_length = ASF_VIDEO_SPECIFIC_DATA_SIZE; + + GST_DEBUG_OBJECT (asfmux, "Stream %" G_GUINT16_FORMAT " codec data length: %" + G_GUINT32_FORMAT ", media specific data length: %" G_GUINT32_FORMAT, + (guint16) asfpad->stream_number, codec_data_length, + media_specific_data_length); + + gst_asf_put_guid (*buf, guids[ASF_STREAM_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_STREAM_PROPERTIES_OBJECT_SIZE + codec_data_length + media_specific_data_length); /* object size */ + + /* stream type */ + if (asfpad->is_audio) + gst_asf_put_guid (*buf + 24, guids[ASF_AUDIO_MEDIA_INDEX]); + else + gst_asf_put_guid (*buf + 24, guids[ASF_VIDEO_MEDIA_INDEX]); + /* error correction */ + if (asfpad->is_audio) { + gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); /* TODO - use audio spread */ + } else { + gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); + } + GST_WRITE_UINT64_LE (*buf + 56, 0); /* time offset */ + + GST_WRITE_UINT32_LE (*buf + 64, codec_data_length + media_specific_data_length); /* type specific data length */ + GST_WRITE_UINT32_LE (*buf + 68, 0); /* error correction data length */ + + flags = (asfpad->stream_number & 0x7F); + GST_WRITE_UINT16_LE (*buf + 72, flags); + GST_WRITE_UINT32_LE (*buf + 74, 0); /* reserved */ + + *buf += ASF_STREAM_PROPERTIES_OBJECT_SIZE; + /* audio specific data */ + if (asfpad->is_audio) { + GstAsfAudioPad *audiopad = (GstAsfAudioPad *) asfpad; + GST_WRITE_UINT16_LE (*buf, audiopad->audioinfo.format); + GST_WRITE_UINT16_LE (*buf + 2, audiopad->audioinfo.channels); + GST_WRITE_UINT32_LE (*buf + 4, audiopad->audioinfo.rate); + GST_WRITE_UINT32_LE (*buf + 8, audiopad->audioinfo.av_bps); + GST_WRITE_UINT16_LE (*buf + 12, audiopad->audioinfo.blockalign); + GST_WRITE_UINT16_LE (*buf + 14, audiopad->audioinfo.size); + GST_WRITE_UINT16_LE (*buf + 16, codec_data_length); + + GST_DEBUG_OBJECT (asfmux, + "wave formatex values: codec_id=%" G_GUINT16_FORMAT ", channels=%" + G_GUINT16_FORMAT ", rate=%" G_GUINT32_FORMAT ", bytes_per_sec=%" + G_GUINT32_FORMAT ", block_alignment=%" G_GUINT16_FORMAT + ", bits_per_sample=%" G_GUINT16_FORMAT ", codec_data_length=%" + G_GUINT16_FORMAT, audiopad->audioinfo.format, + audiopad->audioinfo.channels, audiopad->audioinfo.rate, + audiopad->audioinfo.av_bps, audiopad->audioinfo.blockalign, + audiopad->audioinfo.size, codec_data_length); + + + *buf += ASF_AUDIO_SPECIFIC_DATA_SIZE; + } else { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) asfpad; + GST_WRITE_UINT32_LE (*buf, (guint32) videopad->vidinfo.width); + GST_WRITE_UINT32_LE (*buf + 4, (guint32) videopad->vidinfo.height); + GST_WRITE_UINT8 (*buf + 8, 2); + + /* the BITMAPINFOHEADER size + codec_data size */ + GST_WRITE_UINT16_LE (*buf + 9, + ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11); + + /* BITMAPINFOHEADER */ + GST_WRITE_UINT32_LE (*buf + 11, + ASF_VIDEO_SPECIFIC_DATA_SIZE + codec_data_length - 11); + gst_asf_put_i32 (*buf + 15, videopad->vidinfo.width); + gst_asf_put_i32 (*buf + 19, videopad->vidinfo.height); + GST_WRITE_UINT16_LE (*buf + 23, 1); /* reserved */ + GST_WRITE_UINT16_LE (*buf + 25, videopad->vidinfo.bit_cnt); + GST_WRITE_UINT32_LE (*buf + 27, videopad->vidinfo.compression); + GST_WRITE_UINT32_LE (*buf + 31, videopad->vidinfo.width * + videopad->vidinfo.height * videopad->vidinfo.bit_cnt); + GST_WRITE_UINT32_LE (*buf + 35, videopad->vidinfo.xpels_meter); + GST_WRITE_UINT32_LE (*buf + 39, videopad->vidinfo.ypels_meter); + GST_WRITE_UINT32_LE (*buf + 41, videopad->vidinfo.num_colors); + GST_WRITE_UINT32_LE (*buf + 45, videopad->vidinfo.num_colors); + + *buf += ASF_VIDEO_SPECIFIC_DATA_SIZE; + } + + if (codec_data_length > 0) + memcpy (*buf, GST_BUFFER_DATA (asfpad->codec_data), codec_data_length); + + *buf += codec_data_length; + /* TODO - error correction for audio */ +} + +/** + * gst_asf_mux_write_header_extension: + * @asfmux: + * @buf: pointer to the buffer pointer + * @extension_size: size of the extensions + * + * Writes the header of the header extension object. The buffer pointer + * is incremented to the next writing position (the header extension object + * childs should be writen from that point) + */ +static void +gst_asf_mux_write_header_extension (GstAsfMux * asfmux, guint8 ** buf, + guint64 extension_size) +{ + gst_asf_put_guid (*buf, guids[ASF_HEADER_EXTENSION_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_HEADER_EXTENSION_OBJECT_SIZE + extension_size); /* object size */ + gst_asf_put_guid (*buf + 24, guids[ASF_RESERVED_1_INDEX]); /* reserved */ + GST_WRITE_UINT16_LE (*buf + 40, 6); /* reserved */ + GST_WRITE_UINT32_LE (*buf + 42, extension_size); /* header extension data size */ + *buf += ASF_HEADER_EXTENSION_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_extended_stream_properties: + * @asfmux: + * @buf: pointer to the buffer pointer + * @asfpad: Pad that handles the stream of the properties to be writen + * + * Writes the extended stream properties object (that is part of the + * header extension objects) for the stream handled by asfpad + */ +static void +gst_asf_mux_write_extended_stream_properties (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + gst_asf_put_guid (*buf, guids[ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE); + GST_WRITE_UINT64_LE (*buf + 24, 0); /* start time */ + GST_WRITE_UINT64_LE (*buf + 32, 0); /* end time */ + GST_WRITE_UINT32_LE (*buf + 40, asfpad->bitrate); /* bitrate */ + GST_WRITE_UINT32_LE (*buf + 44, 0); /* buffer size */ + GST_WRITE_UINT32_LE (*buf + 48, 0); /* initial buffer fullness */ + GST_WRITE_UINT32_LE (*buf + 52, asfpad->bitrate); /* alternate data bitrate */ + GST_WRITE_UINT32_LE (*buf + 56, 0); /* alternate buffer size */ + GST_WRITE_UINT32_LE (*buf + 60, 0); /* alternate initial buffer fullness */ + GST_WRITE_UINT32_LE (*buf + 64, 0); /* maximum object size */ + + /* flags */ + GST_WRITE_UINT32_LE (*buf + 68, 0x0); /* TODO check if seekable */ + + GST_WRITE_UINT16_LE (*buf + 72, asfpad->stream_number); + GST_WRITE_UINT16_LE (*buf + 74, 0); /* language index */ + GST_WRITE_UINT64_LE (*buf + 76, 0); /* avg time per frame */ + GST_WRITE_UINT16_LE (*buf + 84, 0); /* stream name count */ + GST_WRITE_UINT16_LE (*buf + 86, 0); /* payload extension count */ + + *buf += ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE; +} + +/** + * gst_asf_mux_write_string_with_size: + * @asfmux: + * @size_buf: pointer to the memory position to write the size of the string + * @str_buf: pointer to the memory position to write the string + * @str: the string to be writen (in UTF-8) + * @use32: if the string size should be writen with 32 bits (if true) + * or with 16 (if false) + * + * Writes a string with its size as it is needed in many asf objects. + * The size is writen to size_buf as a WORD field if use32 is false, and + * as a DWORD if use32 is true. The string is writen to str_buf in UTF16-LE. + * The string should be passed in UTF-8. + * + * The string size in UTF16-LE is returned. + */ +static guint64 +gst_asf_mux_write_string_with_size (GstAsfMux * asfmux, + guint8 * size_buf, guint8 * str_buf, const gchar * str, gboolean use32) +{ + GError *error = NULL; + gsize str_size = 0; + gchar *str_utf16 = NULL; + + GST_LOG_OBJECT (asfmux, "Writing extended content description string: " + "%s", str); + + /* + * Covert the string to utf16 + * Also force the last bytes to null terminated, + * tags were with extra weird characters without it. + */ + str_utf16 = g_convert (str, -1, "UTF-16LE", "UTF-8", NULL, &str_size, &error); + str_utf16[str_size + 1] = '\0'; + str_utf16[str_size + 2] = '\0'; + + /* sum up the null terminating char */ + str_size += 2; + + if (use32) + GST_WRITE_UINT32_LE (size_buf, str_size); + else + GST_WRITE_UINT16_LE (size_buf, str_size); + if (error) { + GST_WARNING_OBJECT (asfmux, "Error converting string " + "to UTF-16: %s - %s", str, error->message); + g_free (error); + memset (str_buf, 0, str_size); + } else { + memcpy (str_buf, str_utf16, str_size); + } + g_free (str_utf16); + return str_size; +} + +/** + * gst_asf_mux_write_content_description_entry: + * @asfmux: + * @tags: + * @tagname: + * @size_buf: + * @data_buf: + * + * Checks if a string tag with tagname exists in the taglist. If it + * exists it is writen as an UTF-16LE to data_buf and its size in bytes + * is writen to size_buf. It is used for writing content description + * object fields. + * + * Returns: the size of the string + */ +static guint16 +gst_asf_mux_write_content_description_entry (GstAsfMux * asfmux, + const GstTagList * tags, const gchar * tagname, + guint8 * size_buf, guint8 * data_buf) +{ + gchar *text = NULL; + guint16 text_size = 0; + if (gst_tag_list_get_string (tags, tagname, &text)) { + text_size = gst_asf_mux_write_string_with_size (asfmux, size_buf, + data_buf, text, FALSE); + g_free (text); + } else { + GST_WRITE_UINT16_LE (size_buf, 0); + } + return text_size; +} + +static guint64 +gst_asf_mux_write_ext_content_description_dword_entry (GstAsfMux * asfmux, + guint8 * buf, const gchar * asf_tag, const guint32 value) +{ + guint64 tag_size; + GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: " + "%s (%u)", asf_tag, value); + + tag_size = gst_asf_mux_write_string_with_size (asfmux, buf, buf + 2, + asf_tag, FALSE); + buf += tag_size + 2; + GST_WRITE_UINT16_LE (buf, ASF_TAG_TYPE_DWORD); + GST_WRITE_UINT16_LE (buf + 2, 4); + GST_WRITE_UINT32_LE (buf + 4, value); + + /* tagsize -> string size + * 2 -> string size field size + * 4 -> dword entry + * 4 -> type of entry + entry size + */ + return tag_size + 2 + 4 + 4; +} + +static guint64 +gst_asf_mux_write_ext_content_description_string_entry (GstAsfMux * asfmux, + guint8 * buf, const gchar * asf_tag, const gchar * text) +{ + guint64 tag_size = 0; + guint64 text_size = 0; + + GST_DEBUG_OBJECT (asfmux, "Writing extended content description tag: " + "%s (%s)", asf_tag, text); + + tag_size = gst_asf_mux_write_string_with_size (asfmux, + buf, buf + 2, asf_tag, FALSE); + GST_WRITE_UINT16_LE (buf + tag_size + 2, ASF_TAG_TYPE_UNICODE_STR); + buf += tag_size + 2 + 2; + text_size = gst_asf_mux_write_string_with_size (asfmux, + buf, buf + 2, text, FALSE); + + /* the size of the strings in utf16-le plus the 3 WORD fields */ + return tag_size + text_size + 6; +} + +static void +gst_asf_mux_write_content_description (GstAsfMux * asfmux, guint8 ** buf, + const GstTagList * tags) +{ + guint8 *values = (*buf) + ASF_CONTENT_DESCRIPTION_OBJECT_SIZE; + guint64 size = 0; + + GST_DEBUG_OBJECT (asfmux, "Writing content description object"); + + gst_asf_put_guid (*buf, guids[ASF_CONTENT_DESCRIPTION_INDEX]); + + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_TITLE, *buf + 24, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_ARTIST, *buf + 26, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_COPYRIGHT, *buf + 28, values); + values += gst_asf_mux_write_content_description_entry (asfmux, tags, + GST_TAG_DESCRIPTION, *buf + 30, values); + + /* rating is currently not present in gstreamer tags, so we put 0 */ + GST_WRITE_UINT16_LE (*buf + 32, 0); + + size += values - *buf; + GST_WRITE_UINT64_LE (*buf + 16, size); + *buf += size; +} + +static void +write_ext_content_description_tag (const GstTagList * taglist, + const gchar * tag, gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfExtContDescData *data = (GstAsfExtContDescData *) user_data; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + text = g_value_get_string (&value); + data->size += + gst_asf_mux_write_ext_content_description_string_entry (data->asfmux, + data->buf + data->size, asftag, text); + } + break; + case ASF_TAG_TYPE_DWORD: + { + guint num = g_value_get_uint (&value); + data->size += + gst_asf_mux_write_ext_content_description_dword_entry (data->asfmux, + data->buf + data->size, asftag, num); + } + break; + default: + GST_WARNING_OBJECT (data->asfmux, + "Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + data->count++; + g_value_reset (&value); +} + +static void +gst_asf_mux_write_ext_content_description (GstAsfMux * asfmux, guint8 ** buf, + GstTagList * tags) +{ + GstAsfExtContDescData extContDesc; + extContDesc.asfmux = asfmux; + extContDesc.buf = *buf; + extContDesc.count = 0; + extContDesc.size = ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE; + + GST_DEBUG_OBJECT (asfmux, "Writing extended content description object"); + gst_asf_put_guid (*buf, guids[ASF_EXT_CONTENT_DESCRIPTION_INDEX]); + + gst_tag_list_foreach (tags, write_ext_content_description_tag, &extContDesc); + + GST_WRITE_UINT64_LE (*buf + 16, extContDesc.size); + GST_WRITE_UINT16_LE (*buf + 24, extContDesc.count); + + *buf += extContDesc.size; +} + +static void +write_metadata_tag (const GstTagList * taglist, const gchar * tag, + gpointer user_data) +{ + const gchar *asftag = gst_asf_get_asf_tag (tag); + GValue value = { 0 }; + guint type; + GstAsfMetadataObjData *data = (GstAsfMetadataObjData *) user_data; + guint16 tag_size; + guint32 content_size; + + if (asftag == NULL) + return; + + if (!gst_tag_list_copy_value (&value, taglist, tag)) { + return; + } + + type = gst_asf_get_tag_field_type (&value); + switch (type) { + case ASF_TAG_TYPE_UNICODE_STR: + { + const gchar *text; + text = g_value_get_string (&value); + GST_WRITE_UINT16_LE (data->buf + data->size, 0); + GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num); + data->size += 4; + + tag_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + 8, asftag, FALSE); + data->size += 2; + + GST_WRITE_UINT16_LE (data->buf + data->size, type); + data->size += 2; + + content_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + tag_size + 4, text, + TRUE); + data->size += tag_size + content_size + 4; + } + break; + case ASF_TAG_TYPE_DWORD: + { + guint num = g_value_get_uint (&value); + GST_WRITE_UINT16_LE (data->buf + data->size, 0); + GST_WRITE_UINT16_LE (data->buf + data->size + 2, data->stream_num); + data->size += 4; + + tag_size = gst_asf_mux_write_string_with_size (data->asfmux, + data->buf + data->size, data->buf + data->size + 8, asftag, FALSE); + data->size += 2; + + GST_WRITE_UINT16_LE (data->buf + data->size, type); + data->size += 2; + /* dword length */ + GST_WRITE_UINT32_LE (data->buf + data->size, 4); + data->size += 4 + tag_size; + + GST_WRITE_UINT32_LE (data->buf + data->size, num); + data->size += 4; + } + break; + default: + GST_WARNING_OBJECT (data->asfmux, + "Unhandled asf tag field type %u for tag %s", type, tag); + g_value_reset (&value); + return; + } + + data->count++; + g_value_reset (&value); +} + +static void +gst_asf_mux_write_metadata_object (GstAsfMux * asfmux, guint8 ** buf, + GstAsfPad * asfpad) +{ + GstAsfMetadataObjData metaObjData; + metaObjData.asfmux = asfmux; + metaObjData.buf = *buf; + metaObjData.count = 0; + metaObjData.size = ASF_METADATA_OBJECT_SIZE; + metaObjData.stream_num = asfpad->stream_number; + + if (asfpad->taglist == NULL || gst_tag_list_is_empty (asfpad->taglist)) + return; + + GST_DEBUG_OBJECT (asfmux, "Writing metadata object"); + gst_asf_put_guid (*buf, guids[ASF_METADATA_OBJECT_INDEX]); + + gst_tag_list_foreach (asfpad->taglist, write_metadata_tag, &metaObjData); + + GST_WRITE_UINT64_LE (*buf + 16, metaObjData.size); + GST_WRITE_UINT16_LE (*buf + 24, metaObjData.count); + + *buf += metaObjData.size; +} + +static void +gst_asf_mux_write_padding_object (GstAsfMux * asfmux, guint8 ** buf, + guint64 padding) +{ + if (padding < ASF_PADDING_OBJECT_SIZE) { + return; + } + + GST_DEBUG_OBJECT (asfmux, "Writing padding object of size %" G_GUINT64_FORMAT, + padding); + gst_asf_put_guid (*buf, guids[ASF_PADDING_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, padding); + memset (*buf + 24, 0, padding - ASF_PADDING_OBJECT_SIZE); + *buf += padding; +} + +static void +gst_asf_mux_write_data_object (GstAsfMux * asfmux, guint8 ** buf) +{ + gst_asf_put_guid (*buf, guids[ASF_DATA_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (*buf + 16, 0); /* object size - needs updating */ + gst_asf_put_guid (*buf + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (*buf + 40, 0); /* total data packets */ + GST_WRITE_UINT16_LE (*buf + 48, 0x0101); /* reserved */ + *buf += ASF_DATA_OBJECT_SIZE; +} + +/** + * gst_asf_mux_start_file: + * @asfmux: #GstAsfMux + * + * Starts the asf file/stream by creating and pushing + * the headers downstream. + */ +static GstFlowReturn +gst_asf_mux_start_file (GstAsfMux * asfmux) +{ + GstBuffer *buf = NULL; + guint8 *bufdata = NULL; + GSList *walk; + guint stream_num = g_slist_length (asfmux->collect->data); + guint metadata_obj_size = 0; + GstAsfTags *asftags; + guint64 padding = asfmux->prop_padding; + if (padding < ASF_PADDING_OBJECT_SIZE) + padding = 0; + + /* from this point we started writing the headers */ + GST_INFO_OBJECT (asfmux, "Writing headers"); + asfmux->state = GST_ASF_MUX_STATE_HEADERS; + + /* let downstream know we think in BYTES and expect to do seeking later */ + gst_pad_push_event (asfmux->srcpad, + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); + + asfmux->file_id = gst_asf_generate_file_id (); + + /* Get the metadata for content description object. + * We store our own taglist because it might get changed from now + * to the time we actually add its contents to the file, changing + * the size of the data we already calculated here. + */ + asftags = g_new0 (GstAsfTags, 1); + gst_asf_mux_get_content_description_tags (asfmux, asftags); + + /* get the total metadata objects size */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + metadata_obj_size += gst_asf_mux_get_metadata_object_size (asfmux, + (GstAsfPad *) walk->data); + } + + /* alloc a buffer for all header objects */ + buf = gst_buffer_new_and_alloc (gst_asf_mux_get_headers_size (asfmux) + + asftags->cont_desc_size + + asftags->ext_cont_desc_size + + metadata_obj_size + padding + ASF_DATA_OBJECT_SIZE); + bufdata = GST_BUFFER_DATA (buf); + + gst_asf_mux_write_header_object (asfmux, &bufdata, GST_BUFFER_SIZE (buf) - + ASF_DATA_OBJECT_SIZE, 2 + stream_num); + + /* get the position of the file properties object for + * updating it in gst_asf_mux_stop_file */ + asfmux->file_properties_object_position = bufdata - GST_BUFFER_DATA (buf); + gst_asf_mux_write_file_properties (asfmux, &bufdata); + + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_stream_properties (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + + if (asftags->cont_desc_size) { + gst_asf_mux_write_content_description (asfmux, &bufdata, asftags->tags); + } + if (asftags->ext_cont_desc_size) { + gst_asf_mux_write_ext_content_description (asfmux, &bufdata, asftags->tags); + } + + if (asftags) { + if (asftags->tags) + gst_tag_list_free (asftags->tags); + g_free (asftags); + } + + /* writing header extension objects */ + gst_asf_mux_write_header_extension (asfmux, &bufdata, stream_num * + ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE + metadata_obj_size); + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_extended_stream_properties (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + gst_asf_mux_write_metadata_object (asfmux, &bufdata, + (GstAsfPad *) walk->data); + } + + gst_asf_mux_write_padding_object (asfmux, &bufdata, padding); + + /* store data object position for later updating some fields */ + asfmux->data_object_position = bufdata - GST_BUFFER_DATA (buf); + gst_asf_mux_write_data_object (asfmux, &bufdata); + + g_assert (bufdata - GST_BUFFER_DATA (buf) == GST_BUFFER_SIZE (buf)); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +/** + * gst_asf_mux_add_simple_index_entry: + * @asfmux: + * @videopad: + * + * Adds a new entry to the simple index of the stream handler by videopad. + * This functions doesn't check if the time ellapsed + * is larger than the established time interval between entries. The caller + * is responsible for verifying this. + */ +static void +gst_asf_mux_add_simple_index_entry (GstAsfMux * asfmux, + GstAsfVideoPad * videopad) +{ + SimpleIndexEntry *entry = NULL; + GST_DEBUG_OBJECT (asfmux, "Adding new simple index entry " + "packet number:%" G_GUINT32_FORMAT ", " + "packet count:%" G_GUINT16_FORMAT, + videopad->last_keyframe_packet, videopad->last_keyframe_packet_count); + entry = g_malloc0 (sizeof (SimpleIndexEntry)); + entry->packet_number = videopad->last_keyframe_packet; + entry->packet_count = videopad->last_keyframe_packet_count; + if (entry->packet_count > videopad->max_keyframe_packet_count) + videopad->max_keyframe_packet_count = entry->packet_count; + videopad->simple_index = g_slist_append (videopad->simple_index, entry); +} + +/** + * gst_asf_mux_send_packet: + * @asfmux: + * @buf: The asf data packet + * + * Pushes an asf data packet downstream. The total number + * of packets and bytes of the stream are incremented. + * + * Returns: the result of pushing the buffer downstream + */ +static GstFlowReturn +gst_asf_mux_send_packet (GstAsfMux * asfmux, GstBuffer * buf) +{ + g_assert (GST_BUFFER_SIZE (buf) == asfmux->packet_size); + asfmux->total_data_packets++; + GST_LOG_OBJECT (asfmux, + "Pushing a packet of size %u and timestamp %" G_GUINT64_FORMAT, + GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf)); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +/** + * gst_asf_mux_flush_payloads: + * @asfmux: #GstAsfMux to flush the payloads from + * + * Fills an asf packet with asfmux queued payloads and + * pushes it downstream. + * + * Returns: The result of pushing the packet + */ +static GstFlowReturn +gst_asf_mux_flush_payloads (GstAsfMux * asfmux) +{ + GstBuffer *buf; + guint8 payloads_count = 0; /* we only use 6 bits, max is 63 */ + guint i; + GstClockTime send_ts = GST_CLOCK_TIME_NONE; + guint64 size_left; + guint8 *data; + GSList *walk; + GstAsfPad *pad; + gboolean has_keyframe; + AsfPayload *payload; + guint32 payload_size; + + if (asfmux->payloads == NULL) + return GST_FLOW_OK; /* nothing to send is ok */ + + GST_DEBUG_OBJECT (asfmux, "Flushing payloads"); + + buf = gst_buffer_new_and_alloc (asfmux->packet_size); + memset (GST_BUFFER_DATA (buf), 0, asfmux->packet_size); + + /* 1 for the multiple payload flags */ + data = GST_BUFFER_DATA (buf) + ASF_PAYLOAD_PARSING_INFO_SIZE + 1; + size_left = asfmux->packet_size - ASF_PAYLOAD_PARSING_INFO_SIZE - 1; + + has_keyframe = FALSE; + walk = asfmux->payloads; + while (walk && payloads_count < MAX_PAYLOADS_IN_A_PACKET) { + payload = (AsfPayload *) walk->data; + pad = (GstAsfPad *) payload->pad; + payload_size = gst_asf_payload_get_size (payload); + if (size_left < payload_size) { + break; /* next payload doesn't fit fully */ + } + + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) { + send_ts = GST_BUFFER_TIMESTAMP (payload->data); + } + + /* adding new simple index entry (if needed) */ + if (!pad->is_audio + && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (payload->data))) { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + if (videopad->has_keyframe) { + for (; videopad->next_index_time <= + ASF_MILI_TO_100NANO (payload->presentation_time); + videopad->next_index_time += videopad->time_interval) { + gst_asf_mux_add_simple_index_entry (asfmux, videopad); + } + } + } + + /* serialize our payload */ + GST_DEBUG_OBJECT (asfmux, "Serializing a payload into the packet. " + "Stream number:%" G_GUINT16_FORMAT + ", media object number:%" G_GUINT16_FORMAT + ", offset into media object:%" G_GUINT32_FORMAT + ", replicated data length:%" G_GUINT16_FORMAT + ", media object size:%" G_GUINT32_FORMAT + ", presentation time:%" G_GUINT32_FORMAT + ", payload size:%" G_GUINT16_FORMAT, + payload->stream_number & 0x7F, + (guint16) payload->media_obj_num, payload->offset_in_media_obj, + (guint16) payload->replicated_data_length, + payload->media_object_size, + payload->presentation_time, (guint16) GST_BUFFER_SIZE (payload->data)); + gst_asf_put_payload (data, payload); + if (!payload->has_packet_info) { + payload->has_packet_info = TRUE; + payload->packet_number = asfmux->total_data_packets; + } + + if (ASF_PAYLOAD_IS_KEYFRAME (payload)) { + has_keyframe = TRUE; + if (!pad->is_audio) { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + videopad->last_keyframe_packet = payload->packet_number; + videopad->last_keyframe_packet_count = payload->packet_count; + videopad->has_keyframe = TRUE; + } + } + + /* update our variables */ + data += payload_size; + size_left -= payload_size; + payloads_count++; + walk = g_slist_next (walk); + } + + /* remove flushed payloads */ + GST_LOG_OBJECT (asfmux, "Freeing already used payloads"); + for (i = 0; i < payloads_count; i++) { + GSList *aux = g_slist_nth (asfmux->payloads, 0); + AsfPayload *payload; + g_assert (aux); + payload = (AsfPayload *) aux->data; + asfmux->payloads = g_slist_remove (asfmux->payloads, payload); + asfmux->payload_data_size -= + (GST_BUFFER_SIZE (payload->data) + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE); + gst_asf_payload_free (payload); + } + + /* check if we can add part of the next payload */ + if (asfmux->payloads && size_left > ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) { + AsfPayload *payload = + (AsfPayload *) g_slist_nth (asfmux->payloads, 0)->data; + guint16 bytes_writen; + GST_DEBUG_OBJECT (asfmux, "Adding part of a payload to a packet"); + + if (ASF_PAYLOAD_IS_KEYFRAME (payload)) + has_keyframe = TRUE; + + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (send_ts))) { + send_ts = GST_BUFFER_TIMESTAMP (payload->data); + } + + bytes_writen = gst_asf_put_subpayload (data, payload, size_left); + if (!payload->has_packet_info) { + payload->has_packet_info = TRUE; + payload->packet_number = asfmux->total_data_packets; + } + asfmux->payload_data_size -= bytes_writen; + size_left -= (bytes_writen + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE); + payloads_count++; + } + + GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT, + asfmux->payload_data_size); + + /* fill payload parsing info */ + data = GST_BUFFER_DATA (buf); + /* flags */ + GST_WRITE_UINT8 (data, (0x0 << 7) | /* no error correction */ + (ASF_FIELD_TYPE_DWORD << 5) | /* packet length type */ + (ASF_FIELD_TYPE_DWORD << 3) | /* padding length type */ + (ASF_FIELD_TYPE_NONE << 1) | /* sequence type type */ + 0x1); /* multiple payloads */ + + /* property flags - according to the spec, this should not change */ + GST_WRITE_UINT8 (data + 1, (ASF_FIELD_TYPE_BYTE << 6) | /* stream number length type */ + (ASF_FIELD_TYPE_BYTE << 4) | /* media obj number length type */ + (ASF_FIELD_TYPE_DWORD << 2) | /* offset info media object length type */ + (ASF_FIELD_TYPE_BYTE)); /* replicated data length type */ + + GST_WRITE_UINT32_LE (data + 2, asfmux->packet_size); + GST_WRITE_UINT32_LE (data + 6, size_left); /* padding size */ + + /* packet send time */ + if (GST_CLOCK_TIME_IS_VALID (send_ts)) { + GST_WRITE_UINT32_LE (data + 10, (send_ts / GST_MSECOND)); + GST_BUFFER_TIMESTAMP (buf) = send_ts; + } + + /* packet duration */ + GST_WRITE_UINT16_LE (data + 14, 0); /* FIXME send duration needs to be estimated */ + + /* multiple payloads flags */ + GST_WRITE_UINT8 (data + 16, 0x2 << 6 | payloads_count); + + if (payloads_count == 0) { + GST_WARNING_OBJECT (asfmux, "Sending packet without any payload"); + } + asfmux->data_object_size += GST_BUFFER_SIZE (buf); + if (!has_keyframe) + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + return gst_asf_mux_send_packet (asfmux, buf); +} + +/** + * stream_number_compare: + * @a: a #GstAsfPad + * @b: another #GstAsfPad + * + * Utility function to compare #GstAsfPad by their stream numbers + * + * Returns: The difference between their stream numbers + */ +static gint +stream_number_compare (gconstpointer a, gconstpointer b) +{ + GstAsfPad *pad_a = (GstAsfPad *) a; + GstAsfPad *pad_b = (GstAsfPad *) b; + return pad_b->stream_number - pad_a->stream_number; +} + +static GstFlowReturn +gst_asf_mux_push_simple_index (GstAsfMux * asfmux, GstAsfVideoPad * pad) +{ + guint64 object_size = ASF_SIMPLE_INDEX_OBJECT_SIZE + + g_slist_length (pad->simple_index) * ASF_SIMPLE_INDEX_ENTRY_SIZE; + GstBuffer *buf = gst_buffer_new_and_alloc (object_size); + GSList *walk; + guint8 *data = GST_BUFFER_DATA (buf); + guint32 entries_count = g_slist_length (pad->simple_index); + + gst_asf_put_guid (data, guids[ASF_SIMPLE_INDEX_OBJECT_INDEX]); + GST_WRITE_UINT64_LE (data + 16, object_size); + gst_asf_put_guid (data + 24, asfmux->file_id); + GST_WRITE_UINT64_LE (data + 40, pad->time_interval); + GST_WRITE_UINT32_LE (data + 48, pad->max_keyframe_packet_count); + GST_WRITE_UINT32_LE (data + 52, entries_count); + data += ASF_SIMPLE_INDEX_OBJECT_SIZE; + + GST_DEBUG_OBJECT (asfmux, + "Simple index object values - size:%" G_GUINT64_FORMAT ", time interval:%" + G_GUINT64_FORMAT ", max packet count:%" G_GUINT32_FORMAT ", entries:%" + G_GUINT16_FORMAT, object_size, pad->time_interval, + pad->max_keyframe_packet_count, entries_count); + + for (walk = pad->simple_index; walk; walk = g_slist_next (walk)) { + SimpleIndexEntry *entry = (SimpleIndexEntry *) walk->data; + GST_DEBUG_OBJECT (asfmux, "Simple index entry: packet_number:%" + G_GUINT32_FORMAT " packet_count:%" G_GUINT16_FORMAT, + entry->packet_number, entry->packet_count); + GST_WRITE_UINT32_LE (data, entry->packet_number); + GST_WRITE_UINT16_LE (data + 4, entry->packet_count); + data += ASF_SIMPLE_INDEX_ENTRY_SIZE; + } + + GST_DEBUG_OBJECT (asfmux, "Pushing the simple index"); + g_assert (data - GST_BUFFER_DATA (buf) == object_size); + return gst_asf_mux_push_buffer (asfmux, buf); +} + +static GstFlowReturn +gst_asf_mux_write_indexes (GstAsfMux * asfmux) +{ + GSList *ordered_pads; + GSList *walker; + GstFlowReturn ret = GST_FLOW_OK; + + /* write simple indexes for video medias */ + ordered_pads = + g_slist_sort (asfmux->collect->data, + (GCompareFunc) stream_number_compare); + for (walker = ordered_pads; walker; walker = g_slist_next (walker)) { + GstAsfPad *pad = (GstAsfPad *) walker->data; + if (!pad->is_audio) { + ret = gst_asf_mux_push_simple_index (asfmux, (GstAsfVideoPad *) pad); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to write simple index for stream %" + G_GUINT16_FORMAT, (guint16) pad->stream_number); + return ret; + } + } + } + return ret; +} + +/** + * gst_asf_mux_stop_file: + * @asfmux: #GstAsfMux + * + * Finalizes the asf stream by pushing the indexes after + * the data object. Also seeks back to the header positions + * to rewrite some fields such as the total number of bytes + * of the file, or any other that couldn't be predicted/known + * back on the header generation. + * + * Returns: GST_FLOW_OK on success + */ +static GstFlowReturn +gst_asf_mux_stop_file (GstAsfMux * asfmux) +{ + GstEvent *event; + GstBuffer *buf; + GstFlowReturn ret = GST_FLOW_OK; + GSList *walk; + GstClockTime play_duration = 0; + guint32 bitrate = 0; + + /* write indexes */ + ret = gst_asf_mux_write_indexes (asfmux); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to write indexes"); + return ret; + } + + /* find max stream duration and bitrate */ + for (walk = asfmux->collect->data; walk; walk = g_slist_next (walk)) { + GstAsfPad *pad = (GstAsfPad *) walk->data; + bitrate += pad->bitrate; + if (pad->play_duration > play_duration) + play_duration = pad->play_duration; + } + + /* going back to file properties object to fill in + * values we didn't know back then */ + GST_DEBUG_OBJECT (asfmux, + "Sending new segment to file properties object position"); + event = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + asfmux->file_properties_object_position + 40, GST_CLOCK_TIME_NONE, 0); + if (!gst_pad_push_event (asfmux->srcpad, event)) { + GST_ERROR_OBJECT (asfmux, "Failed to update file properties object"); + return GST_FLOW_ERROR; + } + /* All file properties fields except the first 40 bytes */ + buf = gst_buffer_new_and_alloc (ASF_FILE_PROPERTIES_OBJECT_SIZE - 40); + + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf), asfmux->file_size); + gst_asf_put_time (GST_BUFFER_DATA (buf) + 8, gst_asf_get_current_time ()); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 16, asfmux->total_data_packets); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 24, (play_duration / 100) + + ASF_MILI_TO_100NANO (asfmux->preroll)); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 32, (play_duration / 100)); /* TODO send duration */ + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 40, asfmux->preroll); + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 48, 0x2); /* flags - seekable */ + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 52, asfmux->packet_size); + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 56, asfmux->packet_size); + /* FIXME - we want the max instantaneous bitrate, for vbr streams, we can't + * get it this way, this would be the average, right? */ + GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf) + 60, bitrate); /* max bitrate */ + + /* we don't use gst_asf_mux_push_buffer because we are overwriting + * already sent data */ + ret = gst_pad_push (asfmux->srcpad, buf); + if (ret != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfmux, "Failed to update file properties object"); + return ret; + } + + GST_DEBUG_OBJECT (asfmux, "Seeking back to data object"); + + /* seek back to the data object */ + event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, + asfmux->data_object_position + 16, GST_CLOCK_TIME_NONE, 0); + + if (!gst_pad_push_event (asfmux->srcpad, event)) { + GST_ERROR_OBJECT (asfmux, "Seek to update data object failed"); + return GST_FLOW_ERROR; + } + + buf = gst_buffer_new_and_alloc (32); /* qword+guid+qword */ + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf), asfmux->data_object_size + + ASF_DATA_OBJECT_SIZE); + gst_asf_put_guid (GST_BUFFER_DATA (buf) + 8, asfmux->file_id); + GST_WRITE_UINT64_LE (GST_BUFFER_DATA (buf) + 24, asfmux->total_data_packets); + + return gst_pad_push (asfmux->srcpad, buf); +} + +/** + * gst_asf_mux_process_buffer: + * @asfmux: + * @pad: stream of the buffer + * @buf: The buffer to be processed + * + * Processes the buffer by parsing it and + * queueing it up as an asf payload for later + * being added and pushed inside an asf packet. + * + * Returns: a #GstFlowReturn + */ +static GstFlowReturn +gst_asf_mux_process_buffer (GstAsfMux * asfmux, GstAsfPad * pad, + GstBuffer * buf) +{ + guint8 keyframe; + AsfPayload *payload; + + payload = g_malloc0 (sizeof (AsfPayload)); + payload->pad = (GstCollectData *) pad; + payload->data = buf; + + GST_LOG_OBJECT (asfmux, + "Processing payload data for stream number %" G_GUINT16_FORMAT, + pad->stream_number); + + /* stream number */ + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) { + keyframe = 0; + } else { + keyframe = 0x1 << 7; + } + payload->stream_number = keyframe | pad->stream_number; + + payload->media_obj_num = pad->media_object_number; + payload->offset_in_media_obj = 0; + payload->replicated_data_length = 8; + + /* replicated data - 1) media object size */ + payload->media_object_size = GST_BUFFER_SIZE (buf); + /* replicated data - 2) presentation time */ + if (!GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) { + GST_ERROR_OBJECT (asfmux, "Received buffer without timestamp"); + gst_asf_payload_free (payload); + return GST_FLOW_ERROR; + } + payload->presentation_time = asfmux->preroll + + (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND); + + /* update counting values */ + pad->media_object_number = (pad->media_object_number + 1) % 256; + if (GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) { + pad->play_duration += GST_BUFFER_DURATION (buf); + } else { + GST_WARNING_OBJECT (asfmux, "Received buffer without duration, it will not " + "be accounted in the total file time"); + } + + asfmux->payloads = g_slist_append (asfmux->payloads, payload); + asfmux->payload_data_size += + GST_BUFFER_SIZE (buf) + ASF_MULTIPLE_PAYLOAD_HEADER_SIZE; + GST_LOG_OBJECT (asfmux, "Payload data size: %" G_GUINT32_FORMAT, + asfmux->payload_data_size); + + while (asfmux->payload_data_size + ASF_PAYLOAD_PARSING_INFO_SIZE >= + asfmux->packet_size) { + GstFlowReturn ret = gst_asf_mux_flush_payloads (asfmux); + if (ret != GST_FLOW_OK) + return ret; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_asf_mux_collected (GstCollectPads * collect, gpointer data) +{ + GstAsfMux *asfmux = GST_ASF_MUX_CAST (data); + GstFlowReturn ret = GST_FLOW_OK; + GstAsfPad *best_pad = NULL; + GstClockTime best_time = GST_CLOCK_TIME_NONE; + GstBuffer *buf = NULL; + GSList *walk; + + if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_STARTED)) { + ret = gst_asf_mux_start_file (asfmux); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (asfmux, "Failed to send headers"); + return ret; + } else { + asfmux->state = GST_ASF_MUX_STATE_DATA; + } + } + + if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_EOS)) + return GST_FLOW_UNEXPECTED; + + /* select the earliest buffer */ + walk = asfmux->collect->data; + while (walk) { + GstAsfPad *pad; + GstCollectData *data; + GstClockTime time; + + data = (GstCollectData *) walk->data; + pad = (GstAsfPad *) data; + + walk = g_slist_next (walk); + + buf = gst_collect_pads_peek (collect, data); + if (buf == NULL) { + GST_LOG_OBJECT (asfmux, "Pad %s has no buffers", + GST_PAD_NAME (pad->collect.pad)); + continue; + } + time = GST_BUFFER_TIMESTAMP (buf); + gst_buffer_unref (buf); + + if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) || + (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) { + best_pad = pad; + best_time = time; + } + } + + if (best_pad != NULL) { + /* we have data */ + GST_LOG_OBJECT (asfmux, "selected pad %s with time %" GST_TIME_FORMAT, + GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time)); + buf = gst_collect_pads_pop (collect, &best_pad->collect); + ret = gst_asf_mux_process_buffer (asfmux, best_pad, buf); + } else { + /* no data, let's finish it up */ + while (asfmux->payloads) { + ret = gst_asf_mux_flush_payloads (asfmux); + if (ret != GST_FLOW_OK) { + return ret; + } + } + g_assert (asfmux->payloads == NULL); + g_assert (asfmux->payload_data_size == 0); + /* in 'is-live' mode we don't need to push indexes + * or updating headers */ + if (!asfmux->prop_is_live) { + ret = gst_asf_mux_stop_file (asfmux); + } + if (ret == GST_FLOW_OK) { + gst_pad_push_event (asfmux->srcpad, gst_event_new_eos ()); + ret = GST_FLOW_UNEXPECTED; + } + asfmux->state = GST_ASF_MUX_STATE_EOS; + } + + return ret; +} + +static void +gst_asf_mux_pad_reset (GstAsfPad * pad) +{ + pad->stream_number = 0; + pad->media_object_number = 0; + pad->play_duration = (GstClockTime) 0; + pad->bitrate = 0; + if (pad->codec_data) + gst_buffer_unref (pad->codec_data); + pad->codec_data = NULL; + if (pad->taglist) + gst_tag_list_free (pad->taglist); + pad->taglist = NULL; + + if (pad->is_audio) { + GstAsfAudioPad *audiopad = (GstAsfAudioPad *) pad; + audiopad->audioinfo.rate = 0; + audiopad->audioinfo.channels = 0; + audiopad->audioinfo.format = 0; + audiopad->audioinfo.av_bps = 0; + audiopad->audioinfo.blockalign = 0; + audiopad->audioinfo.size = 0; + } else { + GstAsfVideoPad *videopad = (GstAsfVideoPad *) pad; + videopad->vidinfo.size = 0; + videopad->vidinfo.width = 0; + videopad->vidinfo.height = 0; + videopad->vidinfo.planes = 1; + videopad->vidinfo.bit_cnt = 0; + videopad->vidinfo.compression = 0; + videopad->vidinfo.image_size = 0; + videopad->vidinfo.xpels_meter = 0; + videopad->vidinfo.ypels_meter = 0; + videopad->vidinfo.num_colors = 0; + videopad->vidinfo.imp_colors = 0; + + videopad->last_keyframe_packet = 0; + videopad->has_keyframe = FALSE; + videopad->last_keyframe_packet_count = 0; + videopad->max_keyframe_packet_count = 0; + videopad->next_index_time = 0; + videopad->time_interval = DEFAULT_SIMPLE_INDEX_TIME_INTERVAL; + if (videopad->simple_index) { + GSList *walk; + for (walk = videopad->simple_index; walk; walk = g_slist_next (walk)) { + g_free (walk->data); + walk->data = NULL; + } + g_slist_free (videopad->simple_index); + } + videopad->simple_index = NULL; + } +} + +static gboolean +gst_asf_mux_audio_set_caps (GstPad * pad, GstCaps * caps) +{ + GstAsfMux *asfmux; + GstAsfAudioPad *audiopad; + GstStructure *structure; + const gchar *caps_name; + gint channels, rate; + gchar *aux; + const GValue *codec_data; + + asfmux = GST_ASF_MUX (gst_pad_get_parent (pad)); + + audiopad = (GstAsfAudioPad *) gst_pad_get_element_private (pad); + g_assert (audiopad); + + aux = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux); + g_free (aux); + + structure = gst_caps_get_structure (caps, 0); + caps_name = gst_structure_get_name (structure); + + if (!gst_structure_get_int (structure, "channels", &channels) || + !gst_structure_get_int (structure, "rate", &rate)) + goto refuse_caps; + + audiopad->audioinfo.channels = (guint16) channels; + audiopad->audioinfo.rate = (guint32) rate; + + /* taken from avimux + * codec initialization data, if any + */ + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + audiopad->pad.codec_data = gst_value_get_buffer (codec_data); + gst_buffer_ref (audiopad->pad.codec_data); + } + + if (strcmp (caps_name, "audio/x-wma") == 0) { + gint version; + gint block_align = 0; + gint bitrate = 0; + + if (!gst_structure_get_int (structure, "wmaversion", &version)) { + goto refuse_caps; + } + + if (gst_structure_get_int (structure, "block_align", &block_align)) { + audiopad->audioinfo.blockalign = (guint16) block_align; + } + if (gst_structure_get_int (structure, "bitrate", &bitrate)) { + audiopad->pad.bitrate = (guint32) bitrate; + audiopad->audioinfo.av_bps = bitrate / 8; + } + + if (version == 1) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV1; + } else if (version == 2) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV2; + } else if (version == 3) { + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_WMAV3; + } else { + goto refuse_caps; + } + } else if (strcmp (caps_name, "audio/mpeg") == 0) { + gint version; + gint layer; + + if (!gst_structure_get_int (structure, "mpegversion", &version) || + !gst_structure_get_int (structure, "layer", &layer)) { + goto refuse_caps; + } + if (version != 1 || layer != 3) { + goto refuse_caps; + } + + audiopad->audioinfo.format = GST_RIFF_WAVE_FORMAT_MPEGL3; + } else { + goto refuse_caps; + } + + gst_object_unref (asfmux); + return TRUE; + +refuse_caps: + GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (asfmux); + return FALSE; +} + +/* TODO Read pixel aspect ratio */ +static gboolean +gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps) +{ + GstAsfMux *asfmux; + GstAsfVideoPad *videopad; + GstStructure *structure; + const gchar *caps_name; + gint width, height; + gchar *aux; + const GValue *codec_data; + + asfmux = GST_ASF_MUX (gst_pad_get_parent (pad)); + + videopad = (GstAsfVideoPad *) gst_pad_get_element_private (pad); + g_assert (videopad); + + aux = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (asfmux, "%s:%s, caps=%s", GST_DEBUG_PAD_NAME (pad), aux); + g_free (aux); + + structure = gst_caps_get_structure (caps, 0); + caps_name = gst_structure_get_name (structure); + + if (!gst_structure_get_int (structure, "width", &width) || + !gst_structure_get_int (structure, "height", &height)) + goto refuse_caps; + + videopad->vidinfo.width = (gint32) width; + videopad->vidinfo.height = (gint32) height; + + /* taken from avimux + * codec initialization data, if any + */ + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + videopad->pad.codec_data = gst_value_get_buffer (codec_data); + gst_buffer_ref (videopad->pad.codec_data); + } + + if (strcmp (caps_name, "video/x-wmv") == 0) { + gint version; + + if (!gst_structure_get_int (structure, "wmvversion", &version)) + goto refuse_caps; + + videopad->vidinfo.bit_cnt = 24; + if (version == 2) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2'); + } else if (version == 1) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1'); + } else if (version == 3) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3'); + } else { + goto refuse_caps; + } + } else { + goto refuse_caps; + } + + gst_object_unref (asfmux); + return TRUE; + +refuse_caps: + GST_WARNING_OBJECT (asfmux, "pad %s refused caps %" GST_PTR_FORMAT, + GST_PAD_NAME (pad), caps); + gst_object_unref (asfmux); + return FALSE; +} + +static GstPad * +gst_asf_mux_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name) +{ + GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); + GstAsfMux *asfmux = GST_ASF_MUX_CAST (element); + GstPad *newpad; + GstAsfPad *collect_pad; + gboolean is_audio; + guint collect_size = 0; + + GST_DEBUG_OBJECT (asfmux, "Requested pad: %s", GST_STR_NULL (name)); + + if (asfmux->state != GST_ASF_MUX_STATE_NONE) { + GST_WARNING_OBJECT (asfmux, "Not providing request pad after element is at " + "paused/playing state."); + return NULL; + } + + if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { + is_audio = TRUE; + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_setcaps_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_audio_set_caps)); + } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { + is_audio = FALSE; + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_setcaps_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_video_set_caps)); + } else { + GST_WARNING_OBJECT (asfmux, "This is not our template!"); + return NULL; + } + + /* add pad to collections */ + if (is_audio) { + collect_size = sizeof (GstAsfAudioPad); + } else { + collect_size = sizeof (GstAsfVideoPad); + } + collect_pad = (GstAsfPad *) + gst_collect_pads_add_pad_full (asfmux->collect, newpad, collect_size, + (GstCollectDataDestroyNotify) (gst_asf_mux_pad_reset)); + + /* set up pad */ + collect_pad->is_audio = is_audio; + if (!is_audio) + ((GstAsfVideoPad *) collect_pad)->simple_index = NULL; + collect_pad->taglist = NULL; + gst_asf_mux_pad_reset (collect_pad); + + /* set pad stream number */ + asfmux->stream_number += 1; + collect_pad->stream_number = asfmux->stream_number; + + /* FIXME: hacked way to override/extend the event function of + * GstCollectPads; because it sets its own event function giving + * the element no access to events. + */ + asfmux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); + gst_pad_set_event_function (newpad, + GST_DEBUG_FUNCPTR (gst_asf_mux_sink_event)); + + gst_pad_set_active (newpad, TRUE); + gst_element_add_pad (element, newpad); + + return newpad; +} + +static void +gst_asf_mux_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + switch (prop_id) { + case PROP_PACKET_SIZE: + g_value_set_uint (value, asfmux->prop_packet_size); + break; + case PROP_PREROLL: + g_value_set_uint64 (value, asfmux->prop_preroll); + break; + case PROP_MERGE_STREAM_TAGS: + g_value_set_boolean (value, asfmux->prop_merge_stream_tags); + break; + case PROP_PADDING: + g_value_set_uint64 (value, asfmux->prop_padding); + break; + case PROP_IS_LIVE: + g_value_set_boolean (value, asfmux->prop_is_live); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_asf_mux_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstAsfMux *asfmux; + + asfmux = GST_ASF_MUX (object); + switch (prop_id) { + case PROP_PACKET_SIZE: + asfmux->prop_packet_size = g_value_get_uint (value); + break; + case PROP_PREROLL: + asfmux->prop_preroll = g_value_get_uint64 (value); + break; + case PROP_MERGE_STREAM_TAGS: + asfmux->prop_merge_stream_tags = g_value_get_boolean (value); + break; + case PROP_PADDING: + asfmux->prop_padding = g_value_get_uint64 (value); + break; + case PROP_IS_LIVE: + asfmux->prop_is_live = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_asf_mux_change_state (GstElement * element, GstStateChange transition) +{ + GstAsfMux *asfmux; + GstStateChangeReturn ret; + + asfmux = GST_ASF_MUX (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + /* TODO - check if it is possible to mux 2 files without going + * through here */ + asfmux->packet_size = asfmux->prop_packet_size; + asfmux->preroll = asfmux->prop_preroll; + asfmux->merge_stream_tags = asfmux->prop_merge_stream_tags; + gst_collect_pads_start (asfmux->collect); + asfmux->state = GST_ASF_MUX_STATE_STARTED; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_collect_pads_stop (asfmux->collect); + asfmux->state = GST_ASF_MUX_STATE_NONE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + +done: + return ret; +} + +gboolean +gst_asf_mux_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "asfmux", + GST_RANK_NONE, GST_TYPE_ASF_MUX); +} diff --git a/gst/asfmux/gstasfmux.h b/gst/asfmux/gstasfmux.h new file mode 100644 index 00000000..280b6753 --- /dev/null +++ b/gst/asfmux/gstasfmux.h @@ -0,0 +1,159 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_ASF_MUX_H__ +#define __GST_ASF_MUX_H__ + + +#include +#include +#include + +#include "gstasfobjects.h" + +G_BEGIN_DECLS +#define GST_TYPE_ASF_MUX \ + (gst_asf_mux_get_type()) +#define GST_ASF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_MUX,GstAsfMux)) +#define GST_ASF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_MUX,GstAsfMuxClass)) +#define GST_IS_ASF_MUX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_MUX)) +#define GST_IS_ASF_MUX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_MUX)) +#define GST_ASF_MUX_CAST(obj) ((GstAsfMux*)(obj)) +typedef struct _GstAsfMux GstAsfMux; +typedef struct _GstAsfMuxClass GstAsfMuxClass; +typedef struct _GstAsfPad GstAsfPad; +typedef struct _GstAsfAudioPad GstAsfAudioPad; +typedef struct _GstAsfVideoPad GstAsfVideoPad; +typedef enum _GstAsfMuxState GstAsfMuxState; + +enum _GstAsfMuxState +{ + GST_ASF_MUX_STATE_NONE, + GST_ASF_MUX_STATE_STARTED, + GST_ASF_MUX_STATE_HEADERS, + GST_ASF_MUX_STATE_DATA, + GST_ASF_MUX_STATE_EOS +}; + +struct _GstAsfPad +{ + GstCollectData collect; + + gboolean is_audio; + guint8 stream_number; + guint8 media_object_number; + guint32 bitrate; + + GstClockTime play_duration; + + GstBuffer *codec_data; + + /* stream only metadata */ + GstTagList *taglist; +}; + +struct _GstAsfAudioPad +{ + GstAsfPad pad; + + gst_riff_strf_auds audioinfo; +}; + +struct _GstAsfVideoPad +{ + GstAsfPad pad; + + gst_riff_strf_vids vidinfo; + + /* Simple Index Entries */ + GSList *simple_index; + gboolean has_keyframe; /* if we have received one at least */ + guint32 last_keyframe_packet; + guint16 last_keyframe_packet_count; + guint16 max_keyframe_packet_count; + GstClockTime next_index_time; + guint64 time_interval; +}; + +struct _GstAsfMux +{ + GstElement element; + + /* output stream state */ + GstAsfMuxState state; + + /* counter to assign stream numbers */ + guint8 stream_number; + + /* counting variables */ + guint64 file_size; + guint64 data_object_size; + guint64 total_data_packets; + + /* + * data object size field position + * needed for updating when finishing the file + */ + guint64 data_object_position; + guint64 file_properties_object_position; + + /* payloads still to be sent in a packet */ + guint32 payload_data_size; + GSList *payloads; + + Guid file_id; + + /* properties */ + guint32 prop_packet_size; + guint64 prop_preroll; + gboolean prop_merge_stream_tags; + guint64 prop_padding; + gboolean prop_is_live; + + /* same as properties, but those are stored here to be + * used without modification while muxing a single file */ + guint32 packet_size; + guint64 preroll; /* milisecs */ + gboolean merge_stream_tags; + + /* pads */ + GstPad *srcpad; + + /* sinkpads, video first */ + GSList *sinkpads; + + GstCollectPads *collect; + GstPadEventFunction collect_event; +}; + +struct _GstAsfMuxClass +{ + GstElementClass parent_class; +}; + +GType gst_asf_mux_get_type (void); +gboolean gst_asf_mux_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_ASF_MUX_H__ */ diff --git a/gst/asfmux/gstasfobjects.c b/gst/asfmux/gstasfobjects.c new file mode 100644 index 00000000..27c71f5e --- /dev/null +++ b/gst/asfmux/gstasfobjects.c @@ -0,0 +1,803 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gstasfobjects.h" +#include + +/* Guids */ +const Guid guids[] = { + /* asf header object */ + {0x75B22630, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf file properties object */ + {0x8CABDCA1, 0xA947, 0x11CF, G_GUINT64_CONSTANT (0x8EE400C00C205365)}, + /* asf stream properties object */ + {0xB7DC0791, 0xA9B7, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)}, + /* asf audio media */ + {0xF8699E40, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf no error correction */ + {0x20FB5700, 0x5B55, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf audio spread */ + {0xBFC3CD50, 0x618F, 0x11CF, G_GUINT64_CONSTANT (0x8BB200AA00B4E220)}, + /* asf header extension object */ + {0x5FBF03B5, 0xA92E, 0x11CF, G_GUINT64_CONSTANT (0x8EE300C00C205365)}, + /* asf reserved 1 */ + {0xABD3D211, 0xA9BA, 0x11CF, G_GUINT64_CONSTANT (0x8EE600C00C205365)}, + /* asf data object */ + {0x75B22636, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf extended stream properties object */ + {0x14E6A5CB, 0xC672, 0x4332, G_GUINT64_CONSTANT (0x8399A96952065B5A)}, + /* asf video media */ + {0xBC19EFC0, 0x5B4D, 0x11CF, G_GUINT64_CONSTANT (0xA8FD00805F5C442B)}, + /* asf simple index object */ + {0x33000890, 0xE5B1, 0x11CF, G_GUINT64_CONSTANT (0x89F400A0C90349CB)}, + /* asf content description */ + {0x75B22633, 0x668E, 0x11CF, G_GUINT64_CONSTANT (0xA6D900AA0062CE6C)}, + /* asf extended content description */ + {0xD2D0A440, 0xE307, 0x11D2, G_GUINT64_CONSTANT (0x97F000A0C95EA850)}, + /* asf metadata object */ + {0xC5F8CBEA, 0x5BAF, 0x4877, G_GUINT64_CONSTANT (0x8467AA8C44FA4CCA)}, + /* asf padding object */ + {0x1806D474, 0xCADF, 0x4509, G_GUINT64_CONSTANT (0xA4BA9AABCB96AAE8)} +}; + +/** + * gst_asf_generate_file_id: + * + * Generates a random GUID + * + * Returns: The generated GUID + */ +Guid +gst_asf_generate_file_id () +{ + guint32 aux; + Guid guid; + guid.v1 = g_random_int (); + aux = g_random_int (); + guid.v2 = (guint16) (aux & 0x0000FFFF); + guid.v3 = (guint16) (aux >> 16); + guid.v4 = (((guint64) g_random_int ()) << 32) | (guint64) g_random_int (); + + return guid; +} + +/** + * gst_byte_reader_get_asf_var_size_field: + * @reader: A #GstByteReader + * @field_type: an asf field type + * @var: pointer to store the result + * + * Reads the proper data from the #GstByteReader according to the + * asf field type and stores it in var + * + * Returns: True on success, false otherwise + */ +gboolean +gst_byte_reader_get_asf_var_size_field (GstByteReader * reader, + guint8 field_type, guint32 * var) +{ + guint8 aux8 = 0; + guint16 aux16 = 0; + guint32 aux32 = 0; + gboolean ret; + + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + ret = gst_byte_reader_get_uint32_le (reader, &aux32); + *var = aux32; + break; + case ASF_FIELD_TYPE_WORD: + ret = gst_byte_reader_get_uint16_le (reader, &aux16); + *var = aux16; + break; + case ASF_FIELD_TYPE_BYTE: + ret = gst_byte_reader_get_uint8 (reader, &aux8); + *var = aux8; + break; + case ASF_FIELD_TYPE_NONE: + ret = TRUE; + *var = 0; + break; + default: + return FALSE; + } + return ret; +} + +/** + * gst_asf_read_var_size_field: + * @data: pointer to the data to be read + * @field_type: the asf field type pointed by data + * + * Reads and returns the value read from the data, according to the + * field type given + * + * Returns: The value read + */ +guint32 +gst_asf_read_var_size_field (guint8 * data, guint8 field_type) +{ + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + return GST_READ_UINT32_LE (data); + case ASF_FIELD_TYPE_WORD: + return GST_READ_UINT16_LE (data); + case ASF_FIELD_TYPE_BYTE: + return data[0]; + default: + return 0; + } +} + +/** + * gst_asf_get_var_size_field_len: + * @field_type: the asf field type + * + * Returns: the size in bytes of a variable of field_type type + */ +guint +gst_asf_get_var_size_field_len (guint8 field_type) +{ + switch (field_type) { + case ASF_FIELD_TYPE_DWORD: + return 4; + case ASF_FIELD_TYPE_WORD: + return 2; + case ASF_FIELD_TYPE_BYTE: + return 1; + default: + return 0; + } +} + +/** + * gst_asf_file_info_new: + * Creates a new #GstAsfFileInfo + * Returns: the created struct + */ +GstAsfFileInfo * +gst_asf_file_info_new () +{ + return g_new0 (GstAsfFileInfo, 1); +} + +/** + * gst_asf_file_info_reset: + * @info: the #GstAsfFileInfo to be reset + * resets the data of a #GstFileInfo + */ +void +gst_asf_file_info_reset (GstAsfFileInfo * info) +{ + info->packet_size = 0; + info->packets_count = 0; + info->broadcast = FALSE; +} + +/** + * gst_asf_file_info_free: + * @info: the #GstAsfFileInfo to be freed + * + * Releases memory associated with this #GstAsfFileInfo + */ +void +gst_asf_file_info_free (GstAsfFileInfo * info) +{ + g_free (info); +} + +/** + * gst_asf_payload_get_size: + * @payload: the payload to get the size from + * + * Returns: the size of an asf payload of the data represented by this + * #AsfPayload + */ +guint32 +gst_asf_payload_get_size (AsfPayload * payload) +{ + return ASF_MULTIPLE_PAYLOAD_HEADER_SIZE + GST_BUFFER_SIZE (payload->data); +} + +/** + * gst_asf_payload_free: + * @payload: the #AsfPayload to be freed + * + * Releases teh memory associated with this payload + */ +void +gst_asf_payload_free (AsfPayload * payload) +{ + gst_buffer_unref (payload->data); + g_free (payload); +} + +/** + * gst_asf_get_current_time: + * + * Gets system current time in ASF time unit + * (100-nanoseconds since Jan, 1st 1601) + * + * Returns: + */ +guint64 +gst_asf_get_current_time () +{ + GTimeVal timeval; + guint64 secs; + guint64 usecs; + + g_get_current_time (&timeval); + + secs = (guint64) timeval.tv_sec; + usecs = (guint64) timeval.tv_usec; + return secs * G_GUINT64_CONSTANT (10000000) + usecs * 10 + + G_GUINT64_CONSTANT (116444628000000000); +} + +/** + * gst_asf_match_guid: + * @data: pointer to the guid to be tested + * @guid: guid to match against data + * + * Checks if the guid pointed by data is the same + * as the guid parameter + * + * Returns: True if they are the same, false otherwise + */ +gboolean +gst_asf_match_guid (const guint8 * data, const Guid * guid) +{ + Guid g; + g.v1 = GST_READ_UINT32_LE (data); + g.v2 = GST_READ_UINT16_LE (data + 4); + g.v3 = GST_READ_UINT16_LE (data + 6); + g.v4 = GST_READ_UINT64_BE (data + 8); + + return g.v1 == guid->v1 && + g.v2 == guid->v2 && g.v3 == guid->v3 && g.v4 == guid->v4; +} + +/** + * gst_asf_put_i32: + * @buf: the memory to write data to + * @data: the value to be writen + * + * Writes a 32 bit signed integer to memory + */ +void +gst_asf_put_i32 (guint8 * buf, gint32 data) +{ + *(gint32 *) buf = data; +} + +/** + * gst_asf_put_time: + * @buf: pointer to the buffer to write the value to + * @time: value to be writen + * + * Writes an asf time value to the buffer + */ +void +gst_asf_put_time (guint8 * buf, guint64 time) +{ + GST_WRITE_UINT64_LE (buf, time); +} + +/** + * gst_asf_put_guid: + * @buf: the buffer to write the guid to + * @guid: the guid to be writen + * + * Writes a GUID to the buffer + */ +void +gst_asf_put_guid (guint8 * buf, Guid guid) +{ + guint32 *aux32 = (guint32 *) buf; + guint16 *aux16 = (guint16 *) & (buf[4]); + guint64 *aux64 = (guint64 *) & (buf[8]); + *aux32 = GUINT32_TO_LE (guid.v1); + *aux16 = GUINT16_TO_LE (guid.v2); + aux16 = (guint16 *) & (buf[6]); + *aux16 = GUINT16_TO_LE (guid.v3); + *aux64 = GUINT64_TO_BE (guid.v4); +} + +/** + * gst_asf_put_payload: + * @buf: memory to write the payload to + * @payload: #AsfPayload to be writen + * + * Writes the asf payload to the buffer. The #AsfPayload + * packet count is incremented. + */ +void +gst_asf_put_payload (guint8 * buf, AsfPayload * payload) +{ + GST_WRITE_UINT8 (buf, payload->stream_number); + GST_WRITE_UINT8 (buf + 1, payload->media_obj_num); + GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj); + GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length); + GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size); + GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time); + GST_WRITE_UINT16_LE (buf + 15, (guint16) GST_BUFFER_SIZE (payload->data)); + memcpy (buf + 17, GST_BUFFER_DATA (payload->data), + GST_BUFFER_SIZE (payload->data)); + + payload->packet_count++; +} + +/** + * gst_asf_put_subpayload: + * @buf: buffer to write the payload to + * @payload: the payload to be writen + * @size: maximum size in bytes to write + * + * Serializes part of a payload to a buffer. + * The maximum size is checked against the payload length, + * the minimum of this size and the payload length is writen + * to the buffer and the writen size is returned. + * + * It also updates the values of the payload to match the remaining + * data. + * In case there is not enough space to write the headers, nothing is done. + * + * Returns: The writen size in bytes. + */ +guint16 +gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, guint16 size) +{ + guint16 payload_size; + GstBuffer *newbuf; + if (size <= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE) { + return 0; /* do nothing if there is not enough space */ + } + GST_WRITE_UINT8 (buf, payload->stream_number); + GST_WRITE_UINT8 (buf + 1, payload->media_obj_num); + GST_WRITE_UINT32_LE (buf + 2, payload->offset_in_media_obj); + GST_WRITE_UINT8 (buf + 6, payload->replicated_data_length); + GST_WRITE_UINT32_LE (buf + 7, payload->media_object_size); + GST_WRITE_UINT32_LE (buf + 11, payload->presentation_time); + size -= ASF_MULTIPLE_PAYLOAD_HEADER_SIZE; + payload_size = size < GST_BUFFER_SIZE (payload->data) ? + size : GST_BUFFER_SIZE (payload->data); + GST_WRITE_UINT16_LE (buf + 15, payload_size); + memcpy (buf + 17, GST_BUFFER_DATA (payload->data), payload_size); + + /* updates the payload to the remaining data */ + payload->offset_in_media_obj += payload_size; + newbuf = gst_buffer_create_sub (payload->data, payload_size, + GST_BUFFER_SIZE (payload->data) - payload_size); + gst_buffer_copy_metadata (payload->data, newbuf, GST_BUFFER_COPY_FLAGS | + GST_BUFFER_COPY_CAPS); + GST_BUFFER_TIMESTAMP (newbuf) = GST_BUFFER_TIMESTAMP (payload->data); + gst_buffer_unref (payload->data); + payload->data = newbuf; + + payload->packet_count++; + + return payload_size; +} + +/** + * gst_asf_match_and_peek_obj_size: + * @data: data to be peeked at + * @guid: pointer to a guid + * + * Compares the first bytes of data against the guid parameter and + * if they match gets the object size (that are right after the guid in + * asf objects). + * + * In case the guids don't match, 0 is returned. + * If the guid is NULL the match is assumed to be true. + * + * Returns: The size of the object in case the guid matches, 0 otherwise + */ +guint64 +gst_asf_match_and_peek_obj_size (const guint8 * data, const Guid * guid) +{ + g_assert (data); + if (guid && !gst_asf_match_guid (data, guid)) { + /* this is not the expected object */ + return 0; + } + /* return the object size */ + return GST_READ_UINT64_LE (data + ASF_GUID_SIZE); +} + +/** + * gst_asf_parse_mult_payload: + * @reader: a #GstByteReader ready to read the multiple payload data + * @has_keyframe: pointer to return the result + * + * Parses a multiple payload section of an asf data packet + * to see if any of the paylaods has a a keyframe + * + * Notice that the #GstByteReader might not be positioned after + * this section on this function return. Because this section + * is the last one in an asf packet and the remaining data + * is probably uninteresting to the application. + * + * Returns: true on success, false if some error occurrs + */ +static gboolean +gst_asf_parse_mult_payload (GstByteReader * reader, gboolean * has_keyframe) +{ + guint payloads; + guint8 payload_len_type; + guint8 rep_data_len; + guint32 payload_len; + guint8 stream_num; + guint8 aux; + guint i; + + if (!gst_byte_reader_get_uint8 (reader, &aux)) + return FALSE; + + payloads = (aux & 0x3F); + payload_len_type = (aux & 0xC0) >> 6; + + *has_keyframe = FALSE; + for (i = 0; i < payloads; i++) { + GST_LOG ("Parsing payload %u/%u", i + 1, payloads); + if (!gst_byte_reader_get_uint8 (reader, &stream_num)) + goto error; + if ((stream_num & 0x80) != 0) { + GST_LOG ("Keyframe found, stoping parse of payloads"); + *has_keyframe = TRUE; + return TRUE; + } + /* skip to replicated data length */ + if (!gst_byte_reader_skip (reader, 5)) + goto error; + if (!gst_byte_reader_get_uint8 (reader, &rep_data_len)) + goto error; + if (!gst_byte_reader_skip (reader, rep_data_len)) + goto error; + if (!gst_byte_reader_get_asf_var_size_field (reader, payload_len_type, + &payload_len)) + goto error; + if (!gst_byte_reader_skip (reader, payload_len)) + goto error; + } + + /* we do not skip the rest of the payload bytes as + this is the last data to be parsed on the buffer */ + return TRUE; +error: + GST_WARNING ("Error while parsing payloads"); + return FALSE; +} + +/** + * gst_asf_parse_single_payload: + * @reader: a #GstByteReader ready to read the multiple payload data + * @has_keyframe: pointer to return the result + * + * Parses a single payload section of an asf data packet + * to see if any of the paylaods has a a keyframe + * + * Notice that the #GstByteReader might not be positioned after + * this section on this function return. Because this section + * is the last one in an asf packet and the remaining data + * is probably uninteresting to the application. + * + * Returns: true on success, false if some error occurrs + */ +static gboolean +gst_asf_parse_single_payload (GstByteReader * reader, gboolean * has_keyframe) +{ + guint8 stream_num; + if (!gst_byte_reader_get_uint8 (reader, &stream_num)) + return GST_FLOW_ERROR; + *has_keyframe = (stream_num & 0x80) != 0; + + /* we do not skip the rest of the payload bytes as + this is the last data to be parsed on the buffer */ + return TRUE; +} + +gboolean +gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, + gboolean trust_delta_flag) +{ + GstByteReader *reader; + gboolean ret = TRUE; + guint8 first; + guint8 aux; + guint8 packet_len_type; + guint8 padding_len_type; + guint8 seq_len_type; + guint8 rep_data_len_type; + guint8 mo_number_len_type; + guint8 mo_offset_type; + gboolean mult_payloads; + guint32 packet_len; + guint32 padd_len; + guint32 send_time; + guint16 duration; + gboolean has_keyframe; + + reader = gst_byte_reader_new_from_buffer (buffer); + + GST_LOG ("Starting packet parsing, size: %u", GST_BUFFER_SIZE (buffer)); + if (!gst_byte_reader_get_uint8 (reader, &first)) + goto error; + + if (first & 0x80) { /* error correction present */ + guint8 err_cor_len; + GST_DEBUG ("Packet contains error correction"); + if (first & 0x60) { + GST_ERROR ("Error correction data length should be " + "set to 0 and is reserved for future use."); + return FALSE; + } + err_cor_len = (first & 0x0F); + GST_DEBUG ("Error correction data length: %d", (gint) err_cor_len); + if (!gst_byte_reader_skip (reader, err_cor_len)) + goto error; + if (!gst_byte_reader_get_uint8 (reader, &aux)) + goto error; + } else { + aux = first; + } + mult_payloads = (aux & 0x1) != 0; + + packet_len_type = (aux >> 5) & 0x3; + padding_len_type = (aux >> 3) & 0x3; + seq_len_type = (aux >> 1) & 0x3; + GST_LOG ("Field sizes: packet length type: %u " + ", padding length type: %u, sequence length type: %u", + gst_asf_get_var_size_field_len (packet_len_type), + gst_asf_get_var_size_field_len (padding_len_type), + gst_asf_get_var_size_field_len (seq_len_type)); + + if (mult_payloads) { + GST_DEBUG ("Packet contains multiple payloads"); + } + + if (!gst_byte_reader_get_uint8 (reader, &aux)) + goto error; + rep_data_len_type = aux & 0x3; + mo_offset_type = (aux >> 2) & 0x3; + mo_number_len_type = (aux >> 4) & 0x3; + + /* gets the fields lengths */ + GST_LOG ("Getting packet and padding length"); + if (!gst_byte_reader_get_asf_var_size_field (reader, + packet_len_type, &packet_len)) + goto error; + if (!gst_byte_reader_skip (reader, + gst_asf_get_var_size_field_len (seq_len_type))) + goto error; + if (!gst_byte_reader_get_asf_var_size_field (reader, + padding_len_type, &padd_len)) + goto error; + + if (packet_len_type != ASF_FIELD_TYPE_NONE && + packet_len != GST_BUFFER_SIZE (buffer)) { + GST_WARNING ("ASF packets should be aligned with buffers"); + ret = FALSE; + goto end; + } + + GST_LOG ("Getting send time and duration"); + if (!gst_byte_reader_get_uint32_le (reader, &send_time)) + goto error; + if (!gst_byte_reader_get_uint16_le (reader, &duration)) + goto error; + + has_keyframe = FALSE; + GST_LOG ("Checking for keyframes"); + if (trust_delta_flag) { + has_keyframe = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + if (mult_payloads) { + ret = gst_asf_parse_mult_payload (reader, &has_keyframe); + } else { + ret = gst_asf_parse_single_payload (reader, &has_keyframe); + } + } + + if (!ret) { + GST_WARNING ("Failed to parse payloads"); + goto end; + } + GST_DEBUG ("Received packet of length %" G_GUINT32_FORMAT + ", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT + ", duration %" G_GUINT16_FORMAT " and %s keyframe(s)", + packet_len, padd_len, send_time, duration, + (has_keyframe) ? "with" : "without"); + + packet->packet_size = packet_len; + packet->padding = padd_len; + packet->send_time = send_time; + packet->duration = duration; + packet->has_keyframe = has_keyframe; + + gst_byte_reader_free (reader); + return ret; + +error: + ret = FALSE; + GST_WARNING ("Error while parsing data packet"); +end: + gst_byte_reader_free (reader); + return ret; +} + +static gboolean +gst_asf_parse_file_properties_obj (GstByteReader * reader, + GstAsfFileInfo * asfinfo) +{ + guint32 min_ps; + guint32 max_ps; + guint64 packets; + guint32 flags; + GST_DEBUG ("ASF: Parsing file properties object"); + + /* skip until data packets count */ + if (!gst_byte_reader_skip (reader, 32)) + return FALSE; + if (!gst_byte_reader_get_uint64_le (reader, &packets)) + return FALSE; + asfinfo->packets_count = packets; + GST_DEBUG ("ASF: packets count %" G_GUINT64_FORMAT, packets); + + /* skip until flags */ + if (!gst_byte_reader_skip (reader, 24)) + return FALSE; + + if (!gst_byte_reader_get_uint32_le (reader, &flags)) + return GST_FLOW_ERROR; + asfinfo->broadcast = (flags & 0x1) == 1; + GST_DEBUG ("ASF: broadcast flag: %s", asfinfo->broadcast ? "true" : "false"); + if (!gst_byte_reader_get_uint32_le (reader, &min_ps)) + return GST_FLOW_ERROR; + if (!gst_byte_reader_get_uint32_le (reader, &max_ps)) + return GST_FLOW_ERROR; + + if (min_ps != max_ps) { + GST_WARNING ("Mininum and maximum packet size differ " + "%" G_GUINT32_FORMAT " and %" G_GUINT32_FORMAT ", " + "ASF spec states they should be the same", min_ps, max_ps); + return FALSE; + } + + GST_DEBUG ("ASF: Packet size: %" G_GUINT32_FORMAT, min_ps); + asfinfo->packet_size = min_ps; + if (!gst_byte_reader_skip (reader, 4)) + return FALSE; + + return TRUE; +} + +gboolean +gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info) +{ + gboolean ret = TRUE; + guint32 header_objects; + guint32 i; + GstByteReader *reader; + guint64 object_size; + + object_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer), + &(guids[ASF_HEADER_OBJECT_INDEX])); + if (object_size == 0) { + GST_WARNING ("ASF: Cannot parse, header guid not found at the beginning " + " of data"); + return FALSE; + } + + reader = gst_byte_reader_new_from_buffer (buffer); + + if (!gst_byte_reader_skip (reader, ASF_GUID_OBJSIZE_SIZE)) + goto error; + if (!gst_byte_reader_get_uint32_le (reader, &header_objects)) + goto error; + GST_DEBUG ("ASF: Header has %" G_GUINT32_FORMAT " child" + " objects", header_objects); + /* skip reserved bytes */ + if (!gst_byte_reader_skip (reader, 2)) + goto error; + + /* iterate through childs of header object */ + for (i = 0; i < header_objects; i++) { + const guint8 *guid = NULL; + guint64 obj_size; + if (!gst_byte_reader_get_data (reader, ASF_GUID_SIZE, &guid)) + goto error; + if (!gst_byte_reader_get_uint64_le (reader, &obj_size)) + goto error; + + if (gst_asf_match_guid (guid, &guids[ASF_FILE_PROPERTIES_OBJECT_INDEX])) { + ret = gst_asf_parse_file_properties_obj (reader, file_info); + } else { + /* we don't know/care about this object */ + if (!gst_byte_reader_skip (reader, obj_size - ASF_GUID_OBJSIZE_SIZE)) + goto error; + } + + if (!ret) + goto end; + } + goto end; + +error: + ret = FALSE; + GST_WARNING ("ASF: Error while parsing headers"); +end: + gst_byte_reader_free (reader); + return ret; +} + +#define MAP_GST_TO_ASF_TAG(tag, gst, asf) \ + if (strcmp (tag, gst) == 0) \ + return asf + +/** + * gst_asf_get_asf_tag: + * @gsttag: a gstreamer tag + * + * Maps gstreamer tags to asf tags + * + * Returns: The tag corresponding name in asf files or NULL if it is not mapped + */ +const gchar * +gst_asf_get_asf_tag (const gchar * gsttag) +{ + g_return_val_if_fail (gsttag != NULL, NULL); + + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE, ASF_TAG_TITLE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TITLE_SORTNAME, ASF_TAG_TITLE_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST, ASF_TAG_ARTIST); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ARTIST_SORTNAME, ASF_TAG_ARTIST_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM, ASF_TAG_ALBUM_TITLE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_ALBUM_SORTNAME, + ASF_TAG_ALBUM_TITLE_SORTNAME); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_GENRE, ASF_TAG_GENRE); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COPYRIGHT, ASF_TAG_COPYRIGHT); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMPOSER, ASF_TAG_COMPOSER); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_COMMENT, ASF_TAG_COMMENT); + MAP_GST_TO_ASF_TAG (gsttag, GST_TAG_TRACK_NUMBER, ASF_TAG_TRACK_NUMBER); + + return NULL; +} + +guint +gst_asf_get_tag_field_type (GValue * value) +{ + if (G_VALUE_HOLDS_STRING (value)) + return ASF_TAG_TYPE_UNICODE_STR; + if (G_VALUE_HOLDS_UINT (value)) + return ASF_TAG_TYPE_DWORD; + + return -1; +} + +gboolean +gst_asf_tag_present_in_content_description (const gchar * tag) +{ + return strcmp (tag, GST_TAG_TITLE) == 0 || + strcmp (tag, GST_TAG_ARTIST) == 0 || + strcmp (tag, GST_TAG_COPYRIGHT) == 0 || + strcmp (tag, GST_TAG_DESCRIPTION) == 0; + /* FIXME we have no tag for rating */ +} diff --git a/gst/asfmux/gstasfobjects.h b/gst/asfmux/gstasfobjects.h new file mode 100644 index 00000000..f91e1e11 --- /dev/null +++ b/gst/asfmux/gstasfobjects.h @@ -0,0 +1,190 @@ +/* ASF muxer plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GST_ASF_OBJECTS_H__ +#define __GST_ASF_OBJECTS_H__ + +#include +#include +#include +#include + +#define ASF_PAYLOAD_IS_KEYFRAME(pay) ((pay->stream_number & 0x80) != 0) +#define ASF_MILI_TO_100NANO(v) (v * 10000) +#define ASF_GUID_SIZE 16 +#define ASF_GUID_OBJSIZE_SIZE 24 + +typedef struct _Guid +{ + guint32 v1; + guint16 v2; + guint16 v3; + guint64 v4; +} Guid; + +typedef struct _GstAsfFileInfo +{ + guint64 packets_count; + guint32 packet_size; + gboolean broadcast; +} GstAsfFileInfo; + +typedef struct _GstAsfPacketInfo +{ + guint32 packet_size; + guint32 padding; + guint32 send_time; + guint16 duration; + gboolean has_keyframe; +} GstAsfPacketInfo; + +typedef struct _SimpleIndexEntry +{ + guint32 packet_number; + guint16 packet_count; +} SimpleIndexEntry; + +typedef struct _AsfPayload +{ + guint8 stream_number; + guint8 media_obj_num; + guint32 offset_in_media_obj; + guint8 replicated_data_length; + guint32 media_object_size; + guint32 presentation_time; + GstBuffer *data; + + GstCollectData *pad; + + /* simple index info */ + gboolean has_packet_info; + guint32 packet_number; + guint16 packet_count; +} AsfPayload; + +Guid gst_asf_generate_file_id (); + +gboolean gst_byte_reader_get_asf_var_size_field (GstByteReader * reader, + guint8 field_type, guint32 * var); +guint32 gst_asf_read_var_size_field (guint8 * data, guint8 field_type); +guint gst_asf_get_var_size_field_len (guint8 field_type); + +GstAsfFileInfo *gst_asf_file_info_new (); +void gst_asf_file_info_reset (GstAsfFileInfo * info); +void gst_asf_file_info_free (GstAsfFileInfo * info); + +guint32 gst_asf_payload_get_size (AsfPayload * payload); +void gst_asf_payload_free (AsfPayload * payload); + +guint64 gst_asf_get_current_time (); + +gboolean gst_asf_match_guid (const guint8 * data, const Guid * g); + +void gst_asf_put_i32 (guint8 * buf, gint32 data); +void gst_asf_put_time (guint8 * buf, guint64 time); +void gst_asf_put_guid (guint8 * buf, Guid guid); +void gst_asf_put_payload (guint8 * buf, AsfPayload * payload); +guint16 gst_asf_put_subpayload (guint8 * buf, AsfPayload * payload, + guint16 size); + +gboolean gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, + gboolean trust_delta_flag); +guint64 gst_asf_match_and_peek_obj_size (const guint8 * data, + const Guid * guid); +gboolean gst_asf_parse_headers (GstBuffer * buffer, GstAsfFileInfo * file_info); + +/* ASF tags + * found at http://msdn.microsoft.com/en-us/library/dd562330(VS.85).aspx + */ + +#define ASF_TAG_TITLE "Title\0" +#define ASF_TAG_TITLE_SORTNAME "TitleSortOrder\0" + +/* FIXME asf has no artist tag other than AlbumArtist, but it has Author + * What to use here? */ +#define ASF_TAG_ARTIST "WM/AlbumArtist\0" +#define ASF_TAG_ARTIST_SORTNAME "AlbumArtistSortOrder\0" + +#define ASF_TAG_ALBUM_TITLE "WM/AlbumTitle\0" +#define ASF_TAG_ALBUM_TITLE_SORTNAME "AlbumTitleSortOrder\0" + +#define ASF_TAG_GENRE "WM/Genre\0" +#define ASF_TAG_COMMENT "Comment\0" +#define ASF_TAG_TRACK_NUMBER "WM/TrackNumber\0" +#define ASF_TAG_COPYRIGHT "Copyright\0" +#define ASF_TAG_COMPOSER "WM/Composer\0" + +const gchar *gst_asf_get_asf_tag (const gchar * gsttag); +guint gst_asf_get_tag_field_type (GValue * value); +gboolean gst_asf_tag_present_in_content_description (const gchar * tag); + +/* ASF Objects Sizes */ +#define ASF_HEADER_OBJECT_SIZE 30 +#define ASF_FILE_PROPERTIES_OBJECT_SIZE 104 +#define ASF_STREAM_PROPERTIES_OBJECT_SIZE 78 +#define ASF_HEADER_EXTENSION_OBJECT_SIZE 46 +#define ASF_AUDIO_SPECIFIC_DATA_SIZE 18 +#define ASF_VIDEO_SPECIFIC_DATA_SIZE 51 +#define ASF_DATA_OBJECT_SIZE 50 +#define ASF_PAYLOAD_PARSING_INFO_SIZE 16 +#define ASF_SINGLE_PAYLOAD_HEADER_SIZE 15 +#define ASF_MULTIPLE_PAYLOAD_HEADER_SIZE 17 +#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_SIZE 88 +#define ASF_CONTENT_DESCRIPTION_OBJECT_SIZE 34 +#define ASF_EXT_CONTENT_DESCRIPTION_OBJECT_SIZE 26 +#define ASF_SIMPLE_INDEX_OBJECT_SIZE 56 +#define ASF_SIMPLE_INDEX_ENTRY_SIZE 6 +#define ASF_METADATA_OBJECT_SIZE 26 +#define ASF_PADDING_OBJECT_SIZE 24 + +/* Field types for data object payload description */ +#define ASF_FIELD_TYPE_NONE 0 +#define ASF_FIELD_TYPE_BYTE 1 +#define ASF_FIELD_TYPE_WORD 2 +#define ASF_FIELD_TYPE_DWORD 3 + +/* tag types */ +#define ASF_TAG_TYPE_UNICODE_STR 0 +#define ASF_TAG_TYPE_BYTES 1 +#define ASF_TAG_TYPE_BOOL 2 +#define ASF_TAG_TYPE_DWORD 3 +#define ASF_TAG_TYPE_QWORD 4 +#define ASF_TAG_TYPE_WORD 5 + +/* GUID objects */ + +#define ASF_HEADER_OBJECT_INDEX 0 +#define ASF_FILE_PROPERTIES_OBJECT_INDEX 1 +#define ASF_STREAM_PROPERTIES_OBJECT_INDEX 2 +#define ASF_AUDIO_MEDIA_INDEX 3 +#define ASF_NO_ERROR_CORRECTION_INDEX 4 +#define ASF_AUDIO_SPREAD_INDEX 5 +#define ASF_HEADER_EXTENSION_OBJECT_INDEX 6 +#define ASF_RESERVED_1_INDEX 7 +#define ASF_DATA_OBJECT_INDEX 8 +#define ASF_EXTENDED_STREAM_PROPERTIES_OBJECT_INDEX 9 +#define ASF_VIDEO_MEDIA_INDEX 10 +#define ASF_SIMPLE_INDEX_OBJECT_INDEX 11 +#define ASF_CONTENT_DESCRIPTION_INDEX 12 +#define ASF_EXT_CONTENT_DESCRIPTION_INDEX 13 +#define ASF_METADATA_OBJECT_INDEX 14 +#define ASF_PADDING_OBJECT_INDEX 15 + +extern const Guid guids[]; + +#endif diff --git a/gst/asfmux/gstasfparse.c b/gst/asfmux/gstasfparse.c new file mode 100644 index 00000000..17db7293 --- /dev/null +++ b/gst/asfmux/gstasfparse.c @@ -0,0 +1,623 @@ +/* ASF parser plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "gstasfparse.h" + +/* FIXME add this include + * #include */ + +GST_DEBUG_CATEGORY_STATIC (asfparse_debug); +#define GST_CAT_DEFAULT asfparse_debug + +enum +{ + PROP_0, +}; + +static const GstElementDetails gst_asf_parse_details = +GST_ELEMENT_DETAILS ("ASF parser", + "Parser", + "Parses ASF", + "Thiago Santos "); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) true") + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, parsed = (boolean) false") + ); + +static void gst_asf_parse_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_asf_parse_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_asf_parse_change_state (GstElement * element, + GstStateChange transition); +static void gst_asf_parse_loop (GstPad * pad); + +GST_BOILERPLATE (GstAsfParse, gst_asf_parse, GstElement, GST_TYPE_ELEMENT); + +static void +gst_asf_parse_reset (GstAsfParse * asfparse) +{ + gst_adapter_clear (asfparse->adapter); + gst_asf_file_info_reset (asfparse->asfinfo); + asfparse->parse_state = ASF_PARSING_HEADERS; + asfparse->headers_size = 0; + asfparse->data_size = 0; + asfparse->parsed_packets = 0; + asfparse->offset = 0; +} + +static gboolean +gst_asf_parse_sink_activate (GstPad * pad) +{ + if (gst_pad_check_pull_range (pad)) { + return gst_pad_activate_pull (pad, TRUE); + } else { + return gst_pad_activate_push (pad, TRUE); + } +} + +static gboolean +gst_asf_parse_sink_activate_pull (GstPad * pad, gboolean active) +{ + if (active) { + return gst_pad_start_task (pad, (GstTaskFunction) gst_asf_parse_loop, pad); + } else { + return gst_pad_stop_task (pad); + } +} + +static GstFlowReturn +gst_asf_parse_push (GstAsfParse * asfparse, GstBuffer * buf) +{ + gst_buffer_set_caps (buf, asfparse->outcaps); + return gst_pad_push (asfparse->srcpad, buf); +} + +static GstFlowReturn +gst_asf_parse_parse_data_object (GstAsfParse * asfparse, GstBuffer * buffer) +{ + GstByteReader *reader; + GstFlowReturn ret = GST_FLOW_OK; + guint64 packet_count; + + GST_DEBUG_OBJECT (asfparse, "Parsing data object"); + + reader = gst_byte_reader_new_from_buffer (buffer); + /* skip to packet count */ + if (!gst_byte_reader_skip (reader, 40)) + goto error; + if (!gst_byte_reader_get_uint64_le (reader, &packet_count)) + goto error; + + if (asfparse->asfinfo->packets_count != packet_count) { + GST_WARNING_OBJECT (asfparse, "File properties object and data object have " + "different packets count, %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, + asfparse->asfinfo->packets_count, packet_count); + } else { + GST_DEBUG_OBJECT (asfparse, "Total packets: %" G_GUINT64_FORMAT, + packet_count); + } + + gst_byte_reader_free (reader); + return gst_asf_parse_push (asfparse, buffer); + +error: + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (asfparse, "Error while parsing data object headers"); + gst_byte_reader_free (reader); + return ret; +} + +static GstFlowReturn +gst_asf_parse_parse_packet (GstAsfParse * asfparse, GstBuffer * buffer) +{ + GstAsfPacketInfo *packetinfo = asfparse->packetinfo; + + if (!gst_asf_parse_packet (buffer, packetinfo, FALSE)) + goto error; + + GST_DEBUG_OBJECT (asfparse, "Received packet of length %" G_GUINT32_FORMAT + ", padding %" G_GUINT32_FORMAT ", send time %" G_GUINT32_FORMAT + ", duration %" G_GUINT16_FORMAT " and %s keyframe(s)", + packetinfo->packet_size, packetinfo->padding, + packetinfo->send_time, packetinfo->duration, + (packetinfo->has_keyframe) ? "with" : "without"); + + /* set gstbuffer fields */ + if (!packetinfo->has_keyframe) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + GST_BUFFER_TIMESTAMP (buffer) = ((GstClockTime) packetinfo->send_time) + * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = ((GstClockTime) packetinfo->duration) + * GST_MSECOND; + + return gst_asf_parse_push (asfparse, buffer); + +error: + GST_ERROR_OBJECT (asfparse, "Error while parsing data packet"); + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_asf_parse_pull_headers (GstAsfParse * asfparse) +{ + GstBuffer *guid_and_size = NULL; + GstBuffer *headers = NULL; + guint64 size; + GstFlowReturn ret; + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_GUID_OBJSIZE_SIZE, &guid_and_size)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers"); + goto leave; + } + asfparse->offset += ASF_GUID_OBJSIZE_SIZE; + size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size), + &(guids[ASF_HEADER_OBJECT_INDEX])); + + if (size == 0) { + GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing"); + goto leave; + } + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + size - ASF_GUID_OBJSIZE_SIZE, &headers)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data from headers"); + goto leave; + } + headers = gst_buffer_join (guid_and_size, headers); + guid_and_size = NULL; + asfparse->offset += size - ASF_GUID_OBJSIZE_SIZE; + if (!gst_asf_parse_headers (headers, asfparse->asfinfo)) { + goto leave; + } + return gst_asf_parse_push (asfparse, headers); + +leave: + if (headers) + gst_buffer_unref (headers); + if (guid_and_size) + gst_buffer_unref (guid_and_size); + return ret; +} + +static GstFlowReturn +gst_asf_parse_pull_data_header (GstAsfParse * asfparse) +{ + GstBuffer *buf = NULL; + GstFlowReturn ret; + + if ((ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_DATA_OBJECT_SIZE, &buf)) != GST_FLOW_OK) { + GST_ERROR_OBJECT (asfparse, "Failed to pull data header"); + return ret; + } + asfparse->offset += ASF_DATA_OBJECT_SIZE; + asfparse->data_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buf), + &(guids[ASF_DATA_OBJECT_INDEX])); + if (asfparse->data_size == 0) { + GST_ERROR_OBJECT (asfparse, "Unexpected object, was expecting data object"); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } + + return gst_asf_parse_parse_data_object (asfparse, buf); +} + +static GstFlowReturn +gst_asf_parse_pull_packets (GstAsfParse * asfparse) +{ + GstFlowReturn ret; + while (asfparse->asfinfo->broadcast || + asfparse->parsed_packets < asfparse->asfinfo->packets_count) { + GstBuffer *packet = NULL; + + GST_DEBUG_OBJECT (asfparse, "Parsing packet %" G_GUINT64_FORMAT, + asfparse->parsed_packets); + + /* get the packet */ + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + asfparse->asfinfo->packet_size, &packet); + if (ret != GST_FLOW_OK) + return ret; + asfparse->parsed_packets++; + asfparse->offset += asfparse->asfinfo->packet_size; + + /* parse the packet */ + ret = gst_asf_parse_parse_packet (asfparse, packet); + if (ret != GST_FLOW_OK) + return ret; + } + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_asf_parse_pull_indexes (GstAsfParse * asfparse) +{ + GstBuffer *guid_and_size = NULL; + GstBuffer *buf = NULL; + guint64 obj_size; + GstFlowReturn ret = GST_FLOW_OK; + while (1) { + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, + ASF_GUID_OBJSIZE_SIZE, &guid_and_size); + if (ret != GST_FLOW_OK) + break; + /* we can peek at the object size */ + obj_size = + gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (guid_and_size), NULL); + if (obj_size == 0) { + GST_ERROR_OBJECT (asfparse, "Incomplete object found"); + gst_buffer_unref (guid_and_size); + ret = GST_FLOW_ERROR; + break; + } + asfparse->offset += ASF_GUID_OBJSIZE_SIZE; + + /* pull the rest of the object */ + ret = gst_pad_pull_range (asfparse->sinkpad, asfparse->offset, obj_size, + &buf); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (guid_and_size); + break; + } + asfparse->offset += obj_size - ASF_GUID_OBJSIZE_SIZE; + + buf = gst_buffer_join (guid_and_size, buf); + ret = gst_asf_parse_push (asfparse, buf); + if (ret != GST_FLOW_OK) + break; + } + return ret; +} + +static void +gst_asf_parse_loop (GstPad * pad) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstAsfParse *asfparse = GST_ASF_PARSE_CAST (GST_OBJECT_PARENT (pad)); + + GST_LOG_OBJECT (asfparse, "Processing data in loop function"); + switch (asfparse->parse_state) { + case ASF_PARSING_HEADERS: + GST_INFO_OBJECT (asfparse, "Starting to parse headers"); + ret = gst_asf_parse_pull_headers (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + asfparse->parse_state = ASF_PARSING_DATA; + + case ASF_PARSING_DATA: + GST_INFO_OBJECT (asfparse, "Parsing data object headers"); + ret = gst_asf_parse_pull_data_header (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + asfparse->parse_state = ASF_PARSING_PACKETS; + + case ASF_PARSING_PACKETS: + GST_INFO_OBJECT (asfparse, "Starting packet parsing"); + GST_INFO_OBJECT (asfparse, "Broadcast mode %s", + asfparse->asfinfo->broadcast ? "on" : "off"); + ret = gst_asf_parse_pull_packets (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + + /* test if all packets have been processed */ + if (!asfparse->asfinfo->broadcast && + asfparse->parsed_packets == asfparse->asfinfo->packets_count) { + GST_INFO_OBJECT (asfparse, + "All %" G_GUINT64_FORMAT " packets processed", + asfparse->parsed_packets); + asfparse->parse_state = ASF_PARSING_INDEXES; + } + + case ASF_PARSING_INDEXES: + /* we currently don't care about indexes, so just push them forward */ + GST_INFO_OBJECT (asfparse, "Starting indexes parsing"); + ret = gst_asf_parse_pull_indexes (asfparse); + if (ret != GST_FLOW_OK) + goto pause; + default: + break; + } + +pause: + { + const gchar *reason = gst_flow_get_name (ret); + + GST_INFO_OBJECT (asfparse, "Pausing sinkpad task"); + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) { + if (ret == GST_FLOW_UNEXPECTED) { + } else { + GST_ELEMENT_ERROR (asfparse, STREAM, FAILED, + (NULL), ("streaming task paused, reason %s (%d)", reason, ret)); + } + gst_pad_push_event (asfparse->srcpad, gst_event_new_eos ()); + } + } +} + +static GstFlowReturn +gst_asf_parse_chain (GstPad * pad, GstBuffer * buffer) +{ + GstAsfParse *asfparse; + GstFlowReturn ret = GST_FLOW_OK; + + asfparse = GST_ASF_PARSE (GST_PAD_PARENT (pad)); + gst_adapter_push (asfparse->adapter, buffer); + + switch (asfparse->parse_state) { + case ASF_PARSING_HEADERS: + if (asfparse->headers_size == 0 && + gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + + /* we can peek at the object size */ + asfparse->headers_size = + gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), + &(guids[ASF_HEADER_OBJECT_INDEX])); + + if (asfparse->headers_size == 0) { + /* something is wrong, this probably ain't an ASF stream */ + GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing"); + ret = GST_FLOW_ERROR; + goto end; + } + } + if (gst_adapter_available (asfparse->adapter) >= asfparse->headers_size) { + GstBuffer *headers = gst_adapter_take_buffer (asfparse->adapter, + asfparse->headers_size); + if (gst_asf_parse_headers (headers, asfparse->asfinfo)) { + ret = gst_asf_parse_push (asfparse, headers); + asfparse->parse_state = ASF_PARSING_DATA; + } else { + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (asfparse, "Failed to parse headers"); + } + } + break; + case ASF_PARSING_DATA: + if (asfparse->data_size == 0 && + gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + + /* we can peek at the object size */ + asfparse->data_size = + gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), + &(guids[ASF_DATA_OBJECT_INDEX])); + + if (asfparse->data_size == 0) { + /* something is wrong */ + GST_ERROR_OBJECT (asfparse, "Unexpected object after headers, was " + "expecting a data object"); + ret = GST_FLOW_ERROR; + goto end; + } + } + /* if we have received the full data object headers */ + if (gst_adapter_available (asfparse->adapter) >= ASF_DATA_OBJECT_SIZE) { + ret = gst_asf_parse_parse_data_object (asfparse, + gst_adapter_take_buffer (asfparse->adapter, ASF_DATA_OBJECT_SIZE)); + if (ret != GST_FLOW_OK) { + goto end; + } + asfparse->parse_state = ASF_PARSING_PACKETS; + } + break; + case ASF_PARSING_PACKETS: + g_assert (asfparse->asfinfo->packet_size); + while ((asfparse->asfinfo->broadcast || + asfparse->parsed_packets < asfparse->asfinfo->packets_count) && + gst_adapter_available (asfparse->adapter) >= + asfparse->asfinfo->packet_size) { + GstBuffer *packet = gst_adapter_take_buffer (asfparse->adapter, + asfparse->asfinfo->packet_size); + asfparse->parsed_packets++; + ret = gst_asf_parse_parse_packet (asfparse, packet); + if (ret != GST_FLOW_OK) + goto end; + } + if (!asfparse->asfinfo->broadcast && + asfparse->parsed_packets >= asfparse->asfinfo->packets_count) { + GST_INFO_OBJECT (asfparse, "Finished parsing packets"); + asfparse->parse_state = ASF_PARSING_INDEXES; + } + break; + case ASF_PARSING_INDEXES: + /* we currently don't care about any of those objects */ + if (gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) { + guint64 obj_size; + /* we can peek at the object size */ + obj_size = gst_asf_match_and_peek_obj_size (gst_adapter_peek + (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), NULL); + if (gst_adapter_available (asfparse->adapter) >= obj_size) { + GST_DEBUG_OBJECT (asfparse, "Skiping object"); + ret = gst_asf_parse_push (asfparse, + gst_adapter_take_buffer (asfparse->adapter, obj_size)); + if (ret != GST_FLOW_OK) { + goto end; + } + } + } + break; + default: + break; + } + +end: + return ret; +} + +static void +gst_asf_parse_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + + gst_element_class_set_details (element_class, &gst_asf_parse_details); + + GST_DEBUG_CATEGORY_INIT (asfparse_debug, "asfparse", 0, + "Parser for ASF streams"); +} + +static void +gst_asf_parse_finalize (GObject * object) +{ + GstAsfParse *asfparse = GST_ASF_PARSE (object); + gst_adapter_clear (asfparse->adapter); + g_object_unref (G_OBJECT (asfparse->adapter)); + gst_caps_unref (asfparse->outcaps); + gst_asf_file_info_free (asfparse->asfinfo); + g_free (asfparse->packetinfo); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_asf_parse_class_init (GstAsfParseClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gst_asf_parse_get_property; + gobject_class->set_property = gst_asf_parse_set_property; + gobject_class->finalize = gst_asf_parse_finalize; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_asf_parse_change_state); +} + +static void +gst_asf_parse_init (GstAsfParse * asfparse, GstAsfParseClass * klass) +{ + asfparse->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_pad_set_chain_function (asfparse->sinkpad, gst_asf_parse_chain); + gst_pad_set_activate_function (asfparse->sinkpad, + gst_asf_parse_sink_activate); + gst_pad_set_activatepull_function (asfparse->sinkpad, + gst_asf_parse_sink_activate_pull); + gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->sinkpad); + + asfparse->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_use_fixed_caps (asfparse->srcpad); + gst_element_add_pad (GST_ELEMENT (asfparse), asfparse->srcpad); + + asfparse->adapter = gst_adapter_new (); + asfparse->outcaps = gst_caps_new_simple ("video/x-ms-asf", NULL); + asfparse->asfinfo = gst_asf_file_info_new (); + asfparse->packetinfo = g_new0 (GstAsfPacketInfo, 1); + gst_asf_parse_reset (asfparse); +} + +static void +gst_asf_parse_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstAsfParse *asfparse; + + asfparse = GST_ASF_PARSE (object); + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_asf_parse_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstAsfParse *asfparse; + + asfparse = GST_ASF_PARSE (object); + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_asf_parse_change_state (GstElement * element, GstStateChange transition) +{ + GstAsfParse *asfparse; + GstStateChangeReturn ret; + + asfparse = GST_ASF_PARSE (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_asf_parse_reset (asfparse); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto done; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + +done: + return ret; +} + +gboolean +gst_asf_parse_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "asfparse", + GST_RANK_NONE, GST_TYPE_ASF_PARSE); +} diff --git a/gst/asfmux/gstasfparse.h b/gst/asfmux/gstasfparse.h new file mode 100644 index 00000000..760e81f4 --- /dev/null +++ b/gst/asfmux/gstasfparse.h @@ -0,0 +1,88 @@ +/* ASF Parser plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_ASF_PARSE_H__ +#define __GST_ASF_PARSE_H__ + + +#include +#include +#include + +#include "gstasfobjects.h" + +G_BEGIN_DECLS + +#define GST_TYPE_ASF_PARSE \ + (gst_asf_parse_get_type()) +#define GST_ASF_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASF_PARSE,GstAsfParse)) +#define GST_ASF_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASF_PARSE,GstAsfParseClass)) +#define GST_IS_ASF_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASF_PARSE)) +#define GST_IS_ASF_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASF_PARSE)) + +#define GST_ASF_PARSE_CAST(obj) ((GstAsfParse*)(obj)) + +enum GstAsfParsingState { + ASF_PARSING_HEADERS, + ASF_PARSING_DATA, + ASF_PARSING_PACKETS, + ASF_PARSING_INDEXES +}; + +typedef struct _GstAsfParse GstAsfParse; +typedef struct _GstAsfParseClass GstAsfParseClass; + +struct _GstAsfParse { + GstElement element; + + enum GstAsfParsingState parse_state; + + GstAdapter *adapter; + + GstPad *srcpad; + GstPad *sinkpad; + GstCaps *outcaps; + + guint64 parsed_packets; + + guint64 offset; /* used in pull mode */ + + /* parsed info */ + GstAsfFileInfo *asfinfo; + GstAsfPacketInfo *packetinfo; /* we keep it here to avoid allocs */ + guint64 headers_size; + guint64 data_size; +}; + +struct _GstAsfParseClass { + GstElementClass parent_class; +}; + +GType gst_asf_parse_get_type(void); +gboolean gst_asf_parse_plugin_init (GstPlugin * plugin); + +G_END_DECLS + + +#endif /* __GST_ASF_PARSE_H__ */ diff --git a/gst/asfmux/gstrtpasfpay.c b/gst/asfmux/gstrtpasfpay.c new file mode 100644 index 00000000..7c57984f --- /dev/null +++ b/gst/asfmux/gstrtpasfpay.c @@ -0,0 +1,446 @@ +/* ASF RTP Payloader plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* FIXME + * - this element doesn't follow (max/min) time properties, + * is it possible to do it with a container format? + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gstrtpasfpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpasfpay_debug); +#define GST_CAT_DEFAULT (rtpasfpay_debug) + +/* elementfactory information */ +static const GstElementDetails gst_rtp_asf_pay_details = +GST_ELEMENT_DETAILS ("RTP ASF payloader", + "Codec/Payloader/Network", + "Payload-encodes ASF into RTP packets (MS_RTSP)", + "Thiago Santos "); + +static GstStaticPadTemplate gst_rtp_asf_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-ms-asf, " "parsed = (boolean) true") + ); + +static GstStaticPadTemplate gst_rtp_asf_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) {\"audio\", \"video\", \"application\"}, " + "clock-rate = (int) 1000, " "encoding-name = (string) \"X-ASF-PF\"") + ); + +static GstFlowReturn +gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer); +static gboolean +gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps); + +GST_BOILERPLATE (GstRtpAsfPay, gst_rtp_asf_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD); + +static void +gst_rtp_asf_pay_init (GstRtpAsfPay * rtpasfpay, GstRtpAsfPayClass * klass) +{ + rtpasfpay->first_ts = 0; + rtpasfpay->config = NULL; + rtpasfpay->packets_count = 0; + rtpasfpay->state = ASF_NOT_STARTED; + rtpasfpay->headers = NULL; + rtpasfpay->current = NULL; +} + +static void +gst_rtp_asf_pay_finalize (GObject * object) +{ + GstRtpAsfPay *rtpasfpay; + rtpasfpay = GST_RTP_ASF_PAY (object); + g_free (rtpasfpay->config); + if (rtpasfpay->headers) + gst_buffer_unref (rtpasfpay->headers); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_asf_pay_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_asf_pay_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_asf_pay_src_template)); + gst_element_class_set_details (element_class, &gst_rtp_asf_pay_details); +} + +static void +gst_rtp_asf_pay_class_init (GstRtpAsfPayClass * klass) +{ + GObjectClass *gobject_class; + GstBaseRTPPayloadClass *gstbasertppayload_class; + + gobject_class = (GObjectClass *) klass; + gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; + + gobject_class->finalize = gst_rtp_asf_pay_finalize; + + gstbasertppayload_class->handle_buffer = gst_rtp_asf_pay_handle_buffer; + gstbasertppayload_class->set_caps = gst_rtp_asf_pay_set_caps; + + GST_DEBUG_CATEGORY_INIT (rtpasfpay_debug, "rtpasfpay", 0, + "ASF RTP Payloader"); +} + +static gboolean +gst_rtp_asf_pay_set_caps (GstBaseRTPPayload * rtppay, GstCaps * caps) +{ + /* FIXME change application for the actual content */ + gst_basertppayload_set_options (rtppay, "application", TRUE, "X-ASF-PF", + 1000); + return TRUE; +} + +static GstFlowReturn +gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer) +{ + GstBaseRTPPayload *rtppay; + GstAsfPacketInfo *packetinfo; + guint8 flags; + guint8 *data; + guint32 packet_util_size; + guint32 packet_offset; + guint32 size_left; + GstFlowReturn ret = GST_FLOW_OK; + + rtppay = GST_BASE_RTP_PAYLOAD (rtpasfpay); + packetinfo = &rtpasfpay->packetinfo; + + if (!gst_asf_parse_packet (buffer, packetinfo, TRUE)) { + GST_ERROR_OBJECT (rtpasfpay, "Error while parsing asf packet"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + if (packetinfo->packet_size == 0) + packetinfo->packet_size = rtpasfpay->asfinfo.packet_size; + + GST_LOG_OBJECT (rtpasfpay, "Packet size: %" G_GUINT32_FORMAT + ", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size, + packetinfo->padding); + + /* FIXME - should update the padding field to 0 */ + + packet_util_size = packetinfo->packet_size - packetinfo->padding; + packet_offset = 0; + while (packet_util_size > 0) { + /* Even if we don't fill completely an output buffer we + * push it when we add an fragment. Because it seems that + * it is not possible to determine where a asf packet + * fragment ends inside a rtp packet payload. + * This flag tells us to push the packet. + */ + gboolean force_push = FALSE; + + /* we have no output buffer pending, create one */ + if (rtpasfpay->current == NULL) { + GST_LOG_OBJECT (rtpasfpay, "Creating new output buffer"); + rtpasfpay->current = + gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU (rtpasfpay), + 0, 0); + rtpasfpay->cur_off = gst_rtp_buffer_get_header_len (rtpasfpay->current); + rtpasfpay->has_ts = FALSE; + rtpasfpay->marker = FALSE; + } + data = GST_BUFFER_DATA (rtpasfpay->current) + rtpasfpay->cur_off; + size_left = GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off; + + GST_DEBUG_OBJECT (rtpasfpay, "Input buffer bytes consumed: %" + G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT, packet_offset, + GST_BUFFER_SIZE (buffer)); + + GST_DEBUG_OBJECT (rtpasfpay, "Output rtpbuffer status"); + GST_DEBUG_OBJECT (rtpasfpay, "Current offset: %" G_GUINT32_FORMAT, + rtpasfpay->cur_off); + GST_DEBUG_OBJECT (rtpasfpay, "Size left: %" G_GUINT32_FORMAT, size_left); + GST_DEBUG_OBJECT (rtpasfpay, "Has ts: %s", + rtpasfpay->has_ts ? "yes" : "no"); + if (rtpasfpay->has_ts) { + GST_DEBUG_OBJECT (rtpasfpay, "Ts: %" G_GUINT32_FORMAT, rtpasfpay->ts); + } + + flags = 0; + if (packetinfo->has_keyframe) { + flags = flags | 0x80; + } + flags = flags | 0x20; /* Relative timestamp is present */ + + if (!rtpasfpay->has_ts) { + /* this is the first asf packet, its send time is the + * rtp packet timestamp */ + rtpasfpay->has_ts = TRUE; + rtpasfpay->ts = packetinfo->send_time; + } + + if (GST_BUFFER_SIZE (rtpasfpay->current) - rtpasfpay->cur_off >= + packet_util_size + 8) { + /* enough space for the rest of the packet */ + if (packet_offset == 0) { + flags = flags | 0x40; + GST_WRITE_UINT24_BE (data + 1, packet_util_size); + } else { + GST_WRITE_UINT24_BE (data + 1, packet_offset); + force_push = TRUE; + } + data[0] = flags; + GST_WRITE_UINT32_BE (data + 4, + (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); + memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset, + packet_util_size); + + /* updating status variables */ + rtpasfpay->cur_off += 8 + packet_util_size; + size_left -= packet_util_size + 8; + packet_offset += packet_util_size; + packet_util_size = 0; + rtpasfpay->marker = TRUE; + } else { + /* fragment packet */ + data[0] = flags; + GST_WRITE_UINT24_BE (data + 1, packet_offset); + GST_WRITE_UINT32_BE (data + 4, + (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); + memcpy (data + 8, GST_BUFFER_DATA (buffer) + packet_offset, + size_left - 8); + + /* updating status variables */ + rtpasfpay->cur_off += size_left; + packet_offset += size_left - 8; + packet_util_size -= size_left - 8; + size_left = 0; + force_push = TRUE; + } + + /* there is not enough room for any more buffers */ + if (force_push || size_left <= 8) { + + if (size_left != 0) { + /* trim remaining bytes not used */ + GstBuffer *aux = gst_buffer_create_sub (rtpasfpay->current, 0, + GST_BUFFER_SIZE (rtpasfpay->current) - size_left); + gst_buffer_unref (rtpasfpay->current); + rtpasfpay->current = aux; + } + gst_rtp_buffer_set_ssrc (rtpasfpay->current, rtppay->current_ssrc); + gst_rtp_buffer_set_marker (rtpasfpay->current, rtpasfpay->marker); + gst_rtp_buffer_set_payload_type (rtpasfpay->current, + GST_BASE_RTP_PAYLOAD_PT (rtppay)); + gst_rtp_buffer_set_seq (rtpasfpay->current, rtppay->seqnum + 1); + gst_rtp_buffer_set_timestamp (rtpasfpay->current, packetinfo->send_time); + + GST_BUFFER_TIMESTAMP (rtpasfpay->current) = GST_BUFFER_TIMESTAMP (buffer); + + gst_buffer_set_caps (rtpasfpay->current, + GST_PAD_CAPS (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay))); + + rtppay->seqnum++; + rtppay->timestamp = packetinfo->send_time; + + GST_DEBUG_OBJECT (rtpasfpay, "Pushing rtp buffer"); + ret = + gst_pad_push (GST_BASE_RTP_PAYLOAD_SRCPAD (rtppay), + rtpasfpay->current); + rtpasfpay->current = NULL; + if (ret != GST_FLOW_OK) { + gst_buffer_unref (buffer); + return ret; + } + } + } + gst_buffer_unref (buffer); + return ret; +} + +static GstFlowReturn +gst_rtp_asf_pay_parse_headers (GstRtpAsfPay * rtpasfpay) +{ + GstFlowReturn ret = GST_FLOW_OK; + gchar *maxps; + g_return_val_if_fail (rtpasfpay->headers, GST_FLOW_ERROR); + + if (!gst_asf_parse_headers (rtpasfpay->headers, &rtpasfpay->asfinfo)) + goto error; + + GST_DEBUG_OBJECT (rtpasfpay, "Packets number: %" G_GUINT64_FORMAT, + rtpasfpay->asfinfo.packets_count); + GST_DEBUG_OBJECT (rtpasfpay, "Packets size: %" G_GUINT32_FORMAT, + rtpasfpay->asfinfo.packet_size); + GST_DEBUG_OBJECT (rtpasfpay, "Broadcast mode: %s", + rtpasfpay->asfinfo.broadcast ? "true" : "false"); + + /* get the config for caps */ + g_free (rtpasfpay->config); + rtpasfpay->config = g_base64_encode (GST_BUFFER_DATA (rtpasfpay->headers), + GST_BUFFER_SIZE (rtpasfpay->headers)); + GST_DEBUG_OBJECT (rtpasfpay, "Serialized headers to base64 string %s", + rtpasfpay->config); + + g_assert (rtpasfpay->config != NULL); + GST_DEBUG_OBJECT (rtpasfpay, "Setting optional caps values: maxps=%" + G_GUINT32_FORMAT " and config=%s", rtpasfpay->asfinfo.packet_size, + rtpasfpay->config); + maxps = + g_strdup_printf ("%" G_GUINT32_FORMAT, rtpasfpay->asfinfo.packet_size); + gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpasfpay), "maxps", + G_TYPE_STRING, maxps, "config", G_TYPE_STRING, rtpasfpay->config, NULL); + g_free (maxps); + + return GST_FLOW_OK; + +error: + ret = GST_FLOW_ERROR; + GST_ERROR_OBJECT (rtpasfpay, "Error while parsing headers"); + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_rtp_asf_pay_handle_buffer (GstBaseRTPPayload * rtppay, GstBuffer * buffer) +{ + GstRtpAsfPay *rtpasfpay = GST_RTP_ASF_PAY_CAST (rtppay); + + if (G_UNLIKELY (rtpasfpay->state == ASF_END)) { + GST_LOG_OBJECT (rtpasfpay, + "Dropping buffer as we already pushed all packets"); + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; /* we already finished our job */ + } + + /* receive headers + * we only accept if they are in a single buffer */ + if (G_UNLIKELY (rtpasfpay->state == ASF_NOT_STARTED)) { + guint64 header_size; + + if (GST_BUFFER_SIZE (buffer) < 24) { /* guid+object size size */ + GST_ERROR_OBJECT (rtpasfpay, + "Buffer too small, smaller than a Guid and object size"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + header_size = gst_asf_match_and_peek_obj_size (GST_BUFFER_DATA (buffer), + &(guids[ASF_HEADER_OBJECT_INDEX])); + if (header_size > 0) { + GST_DEBUG_OBJECT (rtpasfpay, "ASF header guid received, size %" + G_GUINT64_FORMAT, header_size); + + if (GST_BUFFER_SIZE (buffer) < header_size) { + GST_ERROR_OBJECT (rtpasfpay, "Headers should be contained in a single" + " buffer"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } else { + rtpasfpay->state = ASF_DATA_OBJECT; + + /* clear previous headers, if any */ + if (rtpasfpay->headers) { + gst_buffer_unref (rtpasfpay->headers); + } + + GST_DEBUG_OBJECT (rtpasfpay, "Storing headers"); + if (GST_BUFFER_SIZE (buffer) == header_size) { + rtpasfpay->headers = buffer; + return GST_FLOW_OK; + } else { + /* headers are a subbuffer of thie buffer */ + GstBuffer *aux = gst_buffer_create_sub (buffer, header_size, + GST_BUFFER_SIZE (buffer) - header_size); + rtpasfpay->headers = gst_buffer_create_sub (buffer, 0, header_size); + gst_buffer_replace (&buffer, aux); + } + } + } else { + GST_ERROR_OBJECT (rtpasfpay, "Missing ASF header start"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + } + + if (G_UNLIKELY (rtpasfpay->state == ASF_DATA_OBJECT)) { + if (GST_BUFFER_SIZE (buffer) != ASF_DATA_OBJECT_SIZE) { + GST_ERROR_OBJECT (rtpasfpay, "Received buffer of different size of " + "the data object header"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + + if (gst_asf_match_guid (GST_BUFFER_DATA (buffer), + &(guids[ASF_DATA_OBJECT_INDEX]))) { + GST_DEBUG_OBJECT (rtpasfpay, "Received data object header"); + rtpasfpay->headers = gst_buffer_join (rtpasfpay->headers, buffer); + rtpasfpay->state = ASF_PACKETS; + + return gst_rtp_asf_pay_parse_headers (rtpasfpay); + } else { + GST_ERROR_OBJECT (rtpasfpay, "Unexpected object received (was expecting " + "data object)"); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + } + + if (G_LIKELY (rtpasfpay->state == ASF_PACKETS)) { + /* in broadcast mode we can't trust the packets count information + * from the headers + * We assume that if this is on broadcast mode it is a live stream + * and we are going to keep receiving packets indefinitely + */ + if (rtpasfpay->asfinfo.broadcast || + rtpasfpay->packets_count < rtpasfpay->asfinfo.packets_count) { + GST_DEBUG_OBJECT (rtpasfpay, "Received packet %" + G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, + rtpasfpay->packets_count, rtpasfpay->asfinfo.packets_count); + rtpasfpay->packets_count++; + return gst_rtp_asf_pay_handle_packet (rtpasfpay, buffer); + } else { + GST_INFO_OBJECT (rtpasfpay, "Packets ended"); + rtpasfpay->state = ASF_END; + gst_buffer_unref (buffer); + return GST_FLOW_UNEXPECTED; + } + } + + gst_buffer_unref (buffer); + return GST_FLOW_OK; +} + +gboolean +gst_rtp_asf_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpasfpay", + GST_RANK_NONE, GST_TYPE_RTP_ASF_PAY); +} diff --git a/gst/asfmux/gstrtpasfpay.h b/gst/asfmux/gstrtpasfpay.h new file mode 100644 index 00000000..8c7505a5 --- /dev/null +++ b/gst/asfmux/gstrtpasfpay.h @@ -0,0 +1,87 @@ +/* ASF RTP Payloader plugin for GStreamer + * Copyright (C) 2009 Thiago Santos + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_RTP_ASF_PAY_H__ +#define __GST_RTP_ASF_PAY_H__ + +#include +#include +#include +#include + +#include "gstasfobjects.h" + +G_BEGIN_DECLS +#define GST_TYPE_RTP_ASF_PAY \ + (gst_rtp_asf_pay_get_type()) +#define GST_RTP_ASF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPay)) +#define GST_RTP_ASF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ASF_PAY,GstRtpAsfPayClass)) +#define GST_IS_RTP_ASF_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ASF_PAY)) +#define GST_IS_RTP_ASF_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ASF_PAY)) +#define GST_RTP_ASF_PAY_CAST(obj) ((GstRtpAsfPay*)(obj)) + enum GstRtpAsfPayState +{ + ASF_NOT_STARTED, + ASF_DATA_OBJECT, + ASF_PACKETS, + ASF_END +}; + +typedef struct _GstRtpAsfPay GstRtpAsfPay; +typedef struct _GstRtpAsfPayClass GstRtpAsfPayClass; + +struct _GstRtpAsfPay +{ + GstBaseRTPPayload rtppay; + + enum GstRtpAsfPayState state; + + guint32 first_ts; + gchar *config; + guint64 packets_count; + GstAsfFileInfo asfinfo; + + /* current output buffer */ + GstBuffer *current; + guint32 cur_off; + guint32 ts; + gboolean has_ts; + gboolean marker; + + /* keeping it here to avoid allocs/frees */ + GstAsfPacketInfo packetinfo; + + GstBuffer *headers; +}; + +struct _GstRtpAsfPayClass +{ + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_asf_pay_get_type (void); +gboolean gst_rtp_asf_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_ASF_PAY_H__ */ -- cgit v1.2.1 From 7bd35cb8c77523000a7b4acdb58fe6fe6d88bbac Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Jul 2009 12:43:02 +0200 Subject: rtpbin: only reconsider once for BYE When iterating the sources of a BYE packet, don't signal a reconsideration for each of them but signal after we handled all sources. --- gst/rtpmanager/rtpsession.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index d63d9d0e..fe38c36a 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1711,6 +1711,7 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, { guint count, i; gchar *reason; + gboolean reconsider = FALSE; reason = gst_rtcp_packet_bye_get_reason (packet); GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason)); @@ -1769,11 +1770,9 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, sess->next_rtcp_check_time += arrival->time; - RTP_SESSION_UNLOCK (sess); - /* notify app of reconsideration */ - if (sess->callbacks.reconsider) - sess->callbacks.reconsider (sess, sess->reconsider_user_data); - RTP_SESSION_LOCK (sess); + /* mark pending reconsider. We only want to signal the reconsideration + * once after we handled all the source in the bye packet */ + reconsider = TRUE; } } @@ -1784,6 +1783,13 @@ rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet, g_object_unref (source); } + if (reconsider) { + RTP_SESSION_UNLOCK (sess); + /* notify app of reconsideration */ + if (sess->callbacks.reconsider) + sess->callbacks.reconsider (sess, sess->reconsider_user_data); + RTP_SESSION_LOCK (sess); + } g_free (reason); } -- cgit v1.2.1 From 9b1bdac9cc60e274ddc10b2948d9f7d70f2b6e49 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Jul 2009 13:17:20 +0200 Subject: rtpbin: don't do lip-sync after a BYE After a BYE packet from a source, stop forwarding the SR packets for lip-sync to rtpbin. Some senders don't update their SR packets correctly after sending a BYE and then we break lip-sync. We prefer to let the jitterbuffers drain with the current lip-sync instead. --- gst/rtpmanager/rtpsession.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index fe38c36a..9d73d51d 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1577,7 +1577,7 @@ rtp_session_process_rb (RTPSession * sess, RTPSource * source, */ static void rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet, - RTPArrivalStats * arrival) + RTPArrivalStats * arrival, gboolean * do_sync) { guint32 senderssrc, rtptime, packet_count, octet_count; guint64 ntptime; @@ -1594,6 +1594,12 @@ rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet, if (!source) return; + /* don't try to do lip-sync for sources that sent a BYE */ + if (rtp_source_received_bye (source)) + *do_sync = FALSE; + else + *do_sync = TRUE; + prevsender = RTP_SOURCE_IS_SENDER (source); /* first update the source */ @@ -1816,7 +1822,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, GstClockTime current_time) { GstRTCPPacket packet; - gboolean more, is_bye = FALSE, is_sr = FALSE; + gboolean more, is_bye = FALSE, do_sync = FALSE; RTPArrivalStats arrival; GstFlowReturn result = GST_FLOW_OK; @@ -1853,8 +1859,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, switch (type) { case GST_RTCP_TYPE_SR: - rtp_session_process_sr (sess, &packet, &arrival); - is_sr = TRUE; + rtp_session_process_sr (sess, &packet, &arrival, &do_sync); break; case GST_RTCP_TYPE_RR: rtp_session_process_rr (sess, &packet, &arrival); @@ -1891,7 +1896,7 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, RTP_SESSION_UNLOCK (sess); /* notify caller of sr packets in the callback */ - if (is_sr && sess->callbacks.sync_rtcp) + if (do_sync && sess->callbacks.sync_rtcp) result = sess->callbacks.sync_rtcp (sess, sess->source, buffer, sess->sync_rtcp_user_data); else -- cgit v1.2.1 From ffe90f81a4d1f9e123c269895bb97446b9d0f3c2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 27 Jul 2009 15:46:23 +0200 Subject: rtpsession: avoid doing lip-sync in BYE When we get a BYE packet, don't do lip-sync with the SR inside because some senders have trouble constructing valid SR packets after BYE. --- gst/rtpmanager/rtpsession.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 9d73d51d..3e17ec12 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1869,6 +1869,8 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer, break; case GST_RTCP_TYPE_BYE: is_bye = TRUE; + /* don't try to attempt lip-sync anymore for streams with a BYE */ + do_sync = FALSE; rtp_session_process_bye (sess, &packet, &arrival); break; case GST_RTCP_TYPE_APP: -- cgit v1.2.1 From 01a75329002031199f24609754ce5eef03b3a061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommi=20My=C3=B6h=C3=A4nen?= Date: Mon, 27 Jul 2009 08:25:37 +0300 Subject: camerabin: do not put video pipeline into READY when start recording camerabin: remember probe IDs and disconnect them when destroying pipelines --- gst/camerabin/camerabinvideo.c | 48 ++++++++++++++++++++++++++++++++++++++---- gst/camerabin/camerabinvideo.h | 6 ++++++ gst/camerabin/gstcamerabin.c | 4 +--- 3 files changed, 51 insertions(+), 7 deletions(-) (limited to 'gst') diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c index bc915243..f9c9d875 100644 --- a/gst/camerabin/camerabinvideo.c +++ b/gst/camerabin/camerabinvideo.c @@ -186,6 +186,11 @@ gst_camerabin_video_init (GstCameraBinVideo * vid, vid->mute = ARG_DEFAULT_MUTE; + vid->aud_src_probe_id = 0; + vid->vid_src_probe_id = 0; + vid->vid_tee_probe_id = 0; + vid->vid_sink_probe_id = 0; + /* Create src and sink ghost pads */ vid->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); gst_element_add_pad (GST_ELEMENT (vid), vid->sinkpad); @@ -194,7 +199,7 @@ gst_camerabin_video_init (GstCameraBinVideo * vid, gst_element_add_pad (GST_ELEMENT (vid), vid->srcpad); /* Add probe for handling eos when stopping recording */ - gst_pad_add_event_probe (vid->sinkpad, + vid->vid_sink_probe_id = gst_pad_add_event_probe (vid->sinkpad, G_CALLBACK (camerabin_video_sink_have_event), vid); } @@ -206,6 +211,11 @@ gst_camerabin_video_dispose (GstCameraBinVideo * vid) g_string_free (vid->filename, TRUE); vid->filename = NULL; + if (vid->vid_sink_probe_id) { + gst_pad_remove_event_probe (vid->sinkpad, vid->vid_sink_probe_id); + vid->vid_sink_probe_id = 0; + } + if (vid->user_post) { gst_object_unref (vid->user_post); vid->user_post = NULL; @@ -439,6 +449,10 @@ camerabin_video_pad_aud_src_have_buffer (GstPad * pad, GstBuffer * buffer, { GstCameraBinVideo *vid = (GstCameraBinVideo *) u_data; + GST_LOG ("buffer in with size %d duration %" G_GINT64_FORMAT " ts %" + GST_TIME_FORMAT, GST_BUFFER_SIZE (buffer), GST_BUFFER_DURATION (buffer), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + if (vid->calculate_adjust_ts_aud) { GstEvent *event; GstPad *peerpad = NULL; @@ -561,7 +575,7 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid) } /* Add probe for rewriting video timestamps */ - gst_pad_add_buffer_probe (vid->tee_video_srcpad, + vid->vid_tee_probe_id = gst_pad_add_buffer_probe (vid->tee_video_srcpad, G_CALLBACK (camerabin_video_pad_tee_src0_have_buffer), vid); #ifdef USE_TIMEOVERLAY @@ -666,12 +680,12 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid) vid_srcpad = gst_element_get_static_pad (queue, "src"); gst_ghost_pad_set_target (GST_GHOST_PAD (vid->srcpad), vid_srcpad); /* Never let video bin eos events reach view finder */ - gst_pad_add_event_probe (vid_srcpad, + vid->vid_src_probe_id = gst_pad_add_event_probe (vid_srcpad, G_CALLBACK (gst_camerabin_drop_eos_probe), vid); gst_object_unref (vid_srcpad); pad = gst_element_get_static_pad (vid->aud_src, "src"); - gst_pad_add_buffer_probe (pad, + vid->aud_src_probe_id = gst_pad_add_buffer_probe (pad, G_CALLBACK (camerabin_video_pad_aud_src_have_buffer), vid); gst_object_unref (pad); @@ -700,6 +714,32 @@ gst_camerabin_video_destroy_elements (GstCameraBinVideo * vid) { GST_DEBUG ("destroying video elements"); + /* Remove buffer probe from audio src pad */ + if (vid->aud_src_probe_id) { + GstPad *pad = gst_element_get_static_pad (vid->aud_src, "src"); + if (pad) { + gst_pad_remove_buffer_probe (pad, vid->aud_src_probe_id); + gst_object_unref (pad); + } + vid->aud_src_probe_id = 0; + } + + /* Remove EOS event probe from videobin srcpad (queue's srcpad) */ + if (vid->vid_src_probe_id) { + GstPad *pad = gst_ghost_pad_get_target (GST_GHOST_PAD (vid->srcpad)); + if (pad) { + gst_pad_remove_event_probe (pad, vid->vid_src_probe_id); + gst_object_unref (pad); + } + vid->vid_src_probe_id = 0; + } + + /* Remove buffer probe from video tee srcpad */ + if (vid->vid_tee_probe_id) { + gst_pad_remove_buffer_probe (vid->tee_video_srcpad, vid->vid_tee_probe_id); + vid->vid_tee_probe_id = 0; + } + /* Release tee request pads */ if (vid->tee_video_srcpad) { gst_element_release_request_pad (vid->tee, vid->tee_video_srcpad); diff --git a/gst/camerabin/camerabinvideo.h b/gst/camerabin/camerabinvideo.h index dd094d52..c318e9c3 100644 --- a/gst/camerabin/camerabinvideo.h +++ b/gst/camerabin/camerabinvideo.h @@ -82,6 +82,12 @@ struct _GstCameraBinVideo GstEvent *pending_eos; + /* Probe IDs */ + gulong aud_src_probe_id; + gulong vid_src_probe_id; + gulong vid_tee_probe_id; + gulong vid_sink_probe_id; + gboolean mute; }; diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index 22a33477..c64db542 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1597,13 +1597,11 @@ gst_camerabin_start_video_recording (GstCameraBin * camera) gst_camerabin_rewrite_tags (camera); /* Pause the pipeline in order to distribute new clock in paused_to_playing */ - /* audio src timestamps will be 0 without state change to READY. ??? */ - gst_element_set_state (GST_ELEMENT (camera), GST_STATE_READY); - gst_element_set_locked_state (camera->vidbin, FALSE); state_ret = gst_element_set_state (GST_ELEMENT (camera), GST_STATE_PAUSED); if (state_ret != GST_STATE_CHANGE_FAILURE) { g_mutex_lock (camera->capture_mutex); + gst_element_set_locked_state (camera->vidbin, FALSE); g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE, "active-pad", camera->pad_src_vid, NULL); -- cgit v1.2.1 From 2d9bdb933d40c0f32dd353b792ac0e70374e5192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommi=20My=C3=B6h=C3=A4nen?= Date: Mon, 27 Jul 2009 14:03:59 +0300 Subject: camerabin: photo iface setter functions return TRUE when used in NULL state We cache the values and apply then when possible. --- gst/camerabin/gstcamerabinphotography.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'gst') diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c index cb17abca..c5689149 100644 --- a/gst/camerabin/gstcamerabinphotography.c +++ b/gst/camerabin/gstcamerabinphotography.c @@ -42,7 +42,7 @@ gst_camerabin_set_ev_compensation (GstPhotography * photo, gfloat ev_compensation) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -82,7 +82,7 @@ static gboolean gst_camerabin_set_iso_speed (GstPhotography * photo, guint iso_speed) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -120,7 +120,7 @@ gst_camerabin_set_white_balance_mode (GstPhotography * photo, GstWhiteBalanceMode white_balance_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -161,7 +161,7 @@ gst_camerabin_set_colour_tone_mode (GstPhotography * photo, GstColourToneMode colour_tone_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -201,7 +201,7 @@ static gboolean gst_camerabin_set_flash_mode (GstPhotography * photo, GstFlashMode flash_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -269,7 +269,7 @@ static gboolean gst_camerabin_set_scene_mode (GstPhotography * photo, GstSceneMode scene_mode) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); @@ -345,7 +345,7 @@ static gboolean gst_camerabin_set_config (GstPhotography * photo, GstPhotoSettings * config) { GstCameraBin *camera; - gboolean ret = FALSE; + gboolean ret = TRUE; g_return_val_if_fail (photo != NULL, FALSE); camera = GST_CAMERABIN (photo); -- cgit v1.2.1 From 03348629cea29e878c9acd2f5aa344fa71b009b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 28 Jul 2009 11:59:56 -0400 Subject: rtpsource: Incoming buffers do not always have caps --- gst/rtpmanager/rtpsource.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 40cdd238..77d56c22 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -981,7 +981,8 @@ rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer, seqnr = gst_rtp_buffer_get_seq (buffer); - rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); + if (GST_BUFFER_CAPS (buffer)) + rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); if (stats->cycles == -1) { GST_DEBUG ("received first buffer"); -- cgit v1.2.1 From 9232e2278c229a0d238c4a22b0169b3024cb7914 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 28 Jul 2009 18:18:20 +0200 Subject: rtpsource: allow for NULL caps on buffers Add the NULL caps check where it matters and also cover another case of potential NULL caps. Fixes #590030 --- gst/rtpmanager/rtpsource.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 77d56c22..1e0b6c0d 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -628,7 +628,7 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps) gint ival; /* nothing changed, return */ - if (src->caps == caps) + if (caps == NULL || src->caps == caps) return; s = gst_caps_get_structure (caps, 0); @@ -981,8 +981,7 @@ rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer, seqnr = gst_rtp_buffer_get_seq (buffer); - if (GST_BUFFER_CAPS (buffer)) - rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); + rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer)); if (stats->cycles == -1) { GST_DEBUG ("received first buffer"); -- cgit v1.2.1 From fb624bd416ab6efc5628d032ea0610731bf95ae3 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 28 Jul 2009 21:37:04 -0300 Subject: asfmux: Forcing pad names to be video_%02d and audio_%02d like --- gst/asfmux/gstasfmux.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index 9e8218df..0d640f04 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -25,7 +25,11 @@ /** * SECTION:element-asfmux * - * Muxes window media content into an ASF file/stream. + * Muxes media into an ASF file/stream. + * + * Pad names are either video_xx or audio_xx, where 'xx' is the + * stream number of the stream that goes through that pad. Stream numbers + * are assigned sequentially, starting from 1. * * * Example launch lines @@ -2079,7 +2083,7 @@ refuse_caps: static GstPad * gst_asf_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name) + GstPadTemplate * templ, const gchar * req_name) { GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); GstAsfMux *asfmux = GST_ASF_MUX_CAST (element); @@ -2087,8 +2091,9 @@ gst_asf_mux_request_new_pad (GstElement * element, GstAsfPad *collect_pad; gboolean is_audio; guint collect_size = 0; + gchar *name; - GST_DEBUG_OBJECT (asfmux, "Requested pad: %s", GST_STR_NULL (name)); + GST_DEBUG_OBJECT (asfmux, "Requested pad: %s", GST_STR_NULL (req_name)); if (asfmux->state != GST_ASF_MUX_STATE_NONE) { GST_WARNING_OBJECT (asfmux, "Not providing request pad after element is at " @@ -2097,13 +2102,19 @@ gst_asf_mux_request_new_pad (GstElement * element, } if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { - is_audio = TRUE; + name = g_strdup_printf ("audio_%02d", asfmux->stream_number + 1); + GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name); newpad = gst_pad_new_from_template (templ, name); + g_free (name); + is_audio = TRUE; gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_asf_mux_audio_set_caps)); } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { - is_audio = FALSE; + name = g_strdup_printf ("video_%02d", asfmux->stream_number + 1); + GST_DEBUG_OBJECT (asfmux, "Adding new pad %s", name); newpad = gst_pad_new_from_template (templ, name); + g_free (name); + is_audio = FALSE; gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_asf_mux_video_set_caps)); } else { -- cgit v1.2.1 From 0672f2d9b2e8511dfdd9a975e139a306d891ed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Wed, 29 Jul 2009 17:23:31 -0400 Subject: rtpmux: Remove empty finalize --- gst/rtpmux/gstrtpmux.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'gst') diff --git a/gst/rtpmux/gstrtpmux.c b/gst/rtpmux/gstrtpmux.c index 4c2bea09..23a6c7e7 100644 --- a/gst/rtpmux/gstrtpmux.c +++ b/gst/rtpmux/gstrtpmux.c @@ -83,8 +83,6 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", GST_STATIC_CAPS ("application/x-rtp") ); -static void gst_rtp_mux_finalize (GObject * object); - static GstPad *gst_rtp_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name); static void gst_rtp_mux_release_pad (GstElement * element, GstPad * pad); @@ -125,7 +123,6 @@ gst_rtp_mux_class_init (GstRTPMuxClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; - gobject_class->finalize = gst_rtp_mux_finalize; gobject_class->get_property = gst_rtp_mux_get_property; gobject_class->set_property = gst_rtp_mux_set_property; @@ -211,16 +208,6 @@ gst_rtp_mux_init (GstRTPMux * object, GstRTPMuxClass * g_class) object->seqnum_offset = DEFAULT_SEQNUM_OFFSET; } -static void -gst_rtp_mux_finalize (GObject * object) -{ - GstRTPMux *rtp_mux; - - rtp_mux = GST_RTP_MUX (object); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - static GstPad * gst_rtp_mux_create_sinkpad (GstRTPMux * rtp_mux, GstPadTemplate * templ) { -- cgit v1.2.1 From 7dd441f8f506145d5f66efd670731f9d9d70844e Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 29 Jul 2009 18:44:11 -0300 Subject: asfmux: adds support to vc-1 streams --- gst/asfmux/gstasfmux.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index 0d640f04..5d42d4a7 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -2052,20 +2052,26 @@ gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps) } if (strcmp (caps_name, "video/x-wmv") == 0) { - gint version; - - if (!gst_structure_get_int (structure, "wmvversion", &version)) - goto refuse_caps; + guint32 fourcc; - videopad->vidinfo.bit_cnt = 24; - if (version == 2) { - videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2'); - } else if (version == 1) { - videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1'); - } else if (version == 3) { - videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3'); + /* in case we have a fourcc, we use it */ + if (gst_structure_get_fourcc (structure, "format", &fourcc)) { + videopad->vidinfo.compression = fourcc; } else { - goto refuse_caps; + gint version; + if (!gst_structure_get_int (structure, "wmvversion", &version)) + goto refuse_caps; + + videopad->vidinfo.bit_cnt = 24; + if (version == 2) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2'); + } else if (version == 1) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1'); + } else if (version == 3) { + videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3'); + } else { + goto refuse_caps; + } } } else { goto refuse_caps; -- cgit v1.2.1 From d86de48fa108583e9052ee6c3e75497b23b1bf16 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 29 Jul 2009 19:07:27 -0300 Subject: asfmux: accept pad requests a little longer asfmux now start rejecting pad requests after it pushes the first buffer, before this, it was rejecting after going to paused --- gst/asfmux/gstasfmux.c | 5 ++--- gst/asfmux/gstasfmux.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index 5d42d4a7..32581221 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -355,7 +355,7 @@ gst_asf_mux_sink_event (GstPad * pad, GstEvent * event) * writing the headers, because tags are to be in * the headers */ - if (asfmux->state == GST_ASF_MUX_STATE_STARTED) { + if (asfmux->state == GST_ASF_MUX_STATE_NONE) { GstTagList *list = NULL; gst_event_parse_tag (event, &list); if (asfmux->merge_stream_tags) { @@ -1793,7 +1793,7 @@ gst_asf_mux_collected (GstCollectPads * collect, gpointer data) GstBuffer *buf = NULL; GSList *walk; - if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_STARTED)) { + if (G_UNLIKELY (asfmux->state == GST_ASF_MUX_STATE_NONE)) { ret = gst_asf_mux_start_file (asfmux); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (asfmux, "Failed to send headers"); @@ -2237,7 +2237,6 @@ gst_asf_mux_change_state (GstElement * element, GstStateChange transition) asfmux->preroll = asfmux->prop_preroll; asfmux->merge_stream_tags = asfmux->prop_merge_stream_tags; gst_collect_pads_start (asfmux->collect); - asfmux->state = GST_ASF_MUX_STATE_STARTED; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; diff --git a/gst/asfmux/gstasfmux.h b/gst/asfmux/gstasfmux.h index 280b6753..2b71fa36 100644 --- a/gst/asfmux/gstasfmux.h +++ b/gst/asfmux/gstasfmux.h @@ -50,7 +50,6 @@ typedef enum _GstAsfMuxState GstAsfMuxState; enum _GstAsfMuxState { GST_ASF_MUX_STATE_NONE, - GST_ASF_MUX_STATE_STARTED, GST_ASF_MUX_STATE_HEADERS, GST_ASF_MUX_STATE_DATA, GST_ASF_MUX_STATE_EOS -- cgit v1.2.1 From 80196e1bb8892a9a72890078530099d6e016f2cd Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 30 Jul 2009 00:04:43 -0300 Subject: asfmux: Fixes video stream properties object Fixes stream properties object creation for video content --- gst/asfmux/gstasfmux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index 32581221..33f2c74d 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -786,8 +786,8 @@ gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf, videopad->vidinfo.height * videopad->vidinfo.bit_cnt); GST_WRITE_UINT32_LE (*buf + 35, videopad->vidinfo.xpels_meter); GST_WRITE_UINT32_LE (*buf + 39, videopad->vidinfo.ypels_meter); - GST_WRITE_UINT32_LE (*buf + 41, videopad->vidinfo.num_colors); - GST_WRITE_UINT32_LE (*buf + 45, videopad->vidinfo.num_colors); + GST_WRITE_UINT32_LE (*buf + 43, videopad->vidinfo.num_colors); + GST_WRITE_UINT32_LE (*buf + 47, videopad->vidinfo.imp_colors); *buf += ASF_VIDEO_SPECIFIC_DATA_SIZE; } @@ -2054,6 +2054,8 @@ gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps) if (strcmp (caps_name, "video/x-wmv") == 0) { guint32 fourcc; + videopad->vidinfo.bit_cnt = 24; + /* in case we have a fourcc, we use it */ if (gst_structure_get_fourcc (structure, "format", &fourcc)) { videopad->vidinfo.compression = fourcc; @@ -2061,8 +2063,6 @@ gst_asf_mux_video_set_caps (GstPad * pad, GstCaps * caps) gint version; if (!gst_structure_get_int (structure, "wmvversion", &version)) goto refuse_caps; - - videopad->vidinfo.bit_cnt = 24; if (version == 2) { videopad->vidinfo.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2'); } else if (version == 1) { -- cgit v1.2.1 From c271548aabb978eded82b18f5caf5e5966b8718c Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 30 Jul 2009 00:12:45 -0300 Subject: asfmux: Removing useless TODOs After testing in windows and linux, this audio correction field made no difference so far. All example files I got only had zeroes in this field --- gst/asfmux/gstasfmux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index 33f2c74d..edc4bb71 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -727,7 +727,7 @@ gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf, gst_asf_put_guid (*buf + 24, guids[ASF_VIDEO_MEDIA_INDEX]); /* error correction */ if (asfpad->is_audio) { - gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); /* TODO - use audio spread */ + gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); } else { gst_asf_put_guid (*buf + 40, guids[ASF_NO_ERROR_CORRECTION_INDEX]); } @@ -796,7 +796,6 @@ gst_asf_mux_write_stream_properties (GstAsfMux * asfmux, guint8 ** buf, memcpy (*buf, GST_BUFFER_DATA (asfpad->codec_data), codec_data_length); *buf += codec_data_length; - /* TODO - error correction for audio */ } /** -- cgit v1.2.1 From 7583bf6813501a071700522c8bb227afdb4471af Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 30 Jul 2009 00:19:18 -0300 Subject: asfmux: Set video stream seekable property to true Video stream always have indexes, so they are all seekable --- gst/asfmux/gstasfmux.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfmux.c b/gst/asfmux/gstasfmux.c index edc4bb71..83f52701 100644 --- a/gst/asfmux/gstasfmux.c +++ b/gst/asfmux/gstasfmux.c @@ -846,7 +846,13 @@ gst_asf_mux_write_extended_stream_properties (GstAsfMux * asfmux, guint8 ** buf, GST_WRITE_UINT32_LE (*buf + 64, 0); /* maximum object size */ /* flags */ - GST_WRITE_UINT32_LE (*buf + 68, 0x0); /* TODO check if seekable */ + if (asfpad->is_audio) { + /* TODO check if audio is seekable */ + GST_WRITE_UINT32_LE (*buf + 68, 0x0); + } else { + /* video has indexes, so it is seekable */ + GST_WRITE_UINT32_LE (*buf + 68, 0x2); + } GST_WRITE_UINT16_LE (*buf + 72, asfpad->stream_number); GST_WRITE_UINT16_LE (*buf + 74, 0); /* language index */ -- cgit v1.2.1 From 88e399aac25eb982511ab0d30505ed89bcfde011 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Fri, 31 Jul 2009 00:19:19 -0300 Subject: rtpasfpay: set padding field to 0 on rtp asf packets The ASF RTP spec demands that packets have their padding removed and the padding size field set to 0 for packets when puting them inside the RTP packets --- gst/asfmux/gstasfobjects.c | 10 ++++++++++ gst/asfmux/gstasfobjects.h | 6 ++++++ gst/asfmux/gstrtpasfpay.c | 25 ++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) (limited to 'gst') diff --git a/gst/asfmux/gstasfobjects.c b/gst/asfmux/gstasfobjects.c index 27c71f5e..c0b63ae0 100644 --- a/gst/asfmux/gstasfobjects.c +++ b/gst/asfmux/gstasfobjects.c @@ -521,6 +521,7 @@ gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, GstByteReader *reader; gboolean ret = TRUE; guint8 first; + guint8 err_length = 0; /* length of the error fields */ guint8 aux; guint8 packet_len_type; guint8 padding_len_type; @@ -543,6 +544,7 @@ gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, if (first & 0x80) { /* error correction present */ guint8 err_cor_len; + err_length += 1; GST_DEBUG ("Packet contains error correction"); if (first & 0x60) { GST_ERROR ("Error correction data length should be " @@ -550,9 +552,12 @@ gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, return FALSE; } err_cor_len = (first & 0x0F); + err_length += err_cor_len; GST_DEBUG ("Error correction data length: %d", (gint) err_cor_len); if (!gst_byte_reader_skip (reader, err_cor_len)) goto error; + + /* put payload parsing info first byte in aux var */ if (!gst_byte_reader_get_uint8 (reader, &aux)) goto error; } else { @@ -631,6 +636,11 @@ gst_asf_parse_packet (GstBuffer * buffer, GstAsfPacketInfo * packet, packet->send_time = send_time; packet->duration = duration; packet->has_keyframe = has_keyframe; + packet->multiple_payloads = mult_payloads ? TRUE : FALSE; + packet->padd_field_type = padding_len_type; + packet->packet_field_type = packet_len_type; + packet->seq_field_type = seq_len_type; + packet->err_cor_len = err_length; gst_byte_reader_free (reader); return ret; diff --git a/gst/asfmux/gstasfobjects.h b/gst/asfmux/gstasfobjects.h index f91e1e11..302c42c6 100644 --- a/gst/asfmux/gstasfobjects.h +++ b/gst/asfmux/gstasfobjects.h @@ -46,6 +46,12 @@ typedef struct _GstAsfFileInfo typedef struct _GstAsfPacketInfo { + guint8 err_cor_len; + gboolean multiple_payloads; + guint8 padd_field_type; + guint8 packet_field_type; + guint8 seq_field_type; + guint32 packet_size; guint32 padding; guint32 send_time; diff --git a/gst/asfmux/gstrtpasfpay.c b/gst/asfmux/gstrtpasfpay.c index 7c57984f..ffcfd85a 100644 --- a/gst/asfmux/gstrtpasfpay.c +++ b/gst/asfmux/gstrtpasfpay.c @@ -154,7 +154,30 @@ gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer) ", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size, packetinfo->padding); - /* FIXME - should update the padding field to 0 */ + /* update padding field to 0 */ + if (packetinfo->padding > 0) { + GstAsfPacketInfo info; + /* find padding field offset */ + guint offset = packetinfo->err_cor_len + 2 + + gst_asf_get_var_size_field_len (packetinfo->packet_field_type) + + gst_asf_get_var_size_field_len (packetinfo->seq_field_type); + buffer = gst_buffer_make_writable (buffer); + switch (packetinfo->padd_field_type) { + case ASF_FIELD_TYPE_DWORD: + GST_WRITE_UINT32_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0); + break; + case ASF_FIELD_TYPE_WORD: + GST_WRITE_UINT16_LE (&(GST_BUFFER_DATA (buffer)[offset]), 0); + break; + case ASF_FIELD_TYPE_BYTE: + GST_BUFFER_DATA (buffer)[offset] = 0; + break; + case ASF_FIELD_TYPE_NONE: + default: + break; + } + gst_asf_parse_packet (buffer, &info, FALSE); + } packet_util_size = packetinfo->packet_size - packetinfo->padding; packet_offset = 0; -- cgit v1.2.1 From 89788ddc1f4caa951880ac0402499fb62690f980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommi=20My=C3=B6h=C3=A4nen?= Date: Thu, 30 Jul 2009 14:17:59 +0300 Subject: camerabin: Set camerasrc to READY state instead of PAUSED when acquiring allowed caps --- gst/camerabin/gstcamerabin.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'gst') diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c index c64db542..c3010ab8 100644 --- a/gst/camerabin/gstcamerabin.c +++ b/gst/camerabin/gstcamerabin.c @@ -1095,7 +1095,6 @@ gst_camerabin_get_allowed_input_caps (GstCameraBin * camera) GstCaps *caps = NULL; GstPad *pad = NULL, *peer_pad = NULL; GstState state; - gboolean temp_videosrc_pause = FALSE; GstElement *videosrc; g_return_val_if_fail (camera != NULL, NULL); @@ -1121,27 +1120,25 @@ gst_camerabin_get_allowed_input_caps (GstCameraBin * camera) state = GST_STATE (videosrc); - /* Make this function work also in READY and NULL state */ - if (state == GST_STATE_READY || state == GST_STATE_NULL) { - GST_DEBUG_OBJECT (camera, "setting videosrc to paused temporarily"); - temp_videosrc_pause = TRUE; + /* Make this function work also in NULL state */ + if (state == GST_STATE_NULL) { + GST_DEBUG_OBJECT (camera, "setting videosrc to ready temporarily"); peer_pad = gst_pad_get_peer (pad); if (peer_pad) { gst_pad_unlink (pad, peer_pad); } - /* Set videosrc to PAUSED to open video device */ + /* Set videosrc to READY to open video device */ gst_element_set_locked_state (videosrc, TRUE); - gst_element_set_state (videosrc, GST_STATE_PAUSED); + gst_element_set_state (videosrc, GST_STATE_READY); } camera->allowed_caps = gst_pad_get_caps (pad); /* Restore state and re-link if necessary */ - if (temp_videosrc_pause) { + if (state == GST_STATE_NULL) { GST_DEBUG_OBJECT (camera, "restoring videosrc state %d", state); /* Reset videosrc to NULL state, some drivers seem to need this */ gst_element_set_state (videosrc, GST_STATE_NULL); - gst_element_set_state (videosrc, state); if (peer_pad) { gst_pad_link (pad, peer_pad); gst_object_unref (peer_pad); -- cgit v1.2.1 From b6582939256d417ed5f2eae3c66d7b4cf0c668fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tommi=20My=C3=B6h=C3=A4nen?= Date: Fri, 31 Jul 2009 11:27:03 +0300 Subject: outputselector: check for pending srcpad in _get_property() If there is a pending srcpad, return it instead of active srcpad in gst_output_selector_get_property() function. --- gst/selector/gstoutputselector.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'gst') diff --git a/gst/selector/gstoutputselector.c b/gst/selector/gstoutputselector.c index 15c0a726..f5c89eef 100644 --- a/gst/selector/gstoutputselector.c +++ b/gst/selector/gstoutputselector.c @@ -238,7 +238,8 @@ gst_output_selector_get_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_ACTIVE_PAD: GST_OBJECT_LOCK (object); - g_value_set_object (value, sel->active_srcpad); + g_value_set_object (value, + sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad); GST_OBJECT_UNLOCK (object); break; case PROP_RESEND_LATEST:{ -- cgit v1.2.1 From 2e7b6e44db4fd03b4ca69891eac241e073e7cf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Fri, 12 Jun 2009 17:22:22 +0100 Subject: flacparse: remove internal bitreader and bytereader now that these are in libgstbase --- gst/flacparse/Makefile.am | 4 +- gst/flacparse/gstbitreader.c | 400 ------------- gst/flacparse/gstbitreader.h | 99 ---- gst/flacparse/gstbytereader.c | 1231 ----------------------------------------- gst/flacparse/gstbytereader.h | 134 ----- gst/flacparse/gstflacparse.c | 4 +- 6 files changed, 4 insertions(+), 1868 deletions(-) delete mode 100644 gst/flacparse/gstbitreader.c delete mode 100644 gst/flacparse/gstbitreader.h delete mode 100644 gst/flacparse/gstbytereader.c delete mode 100644 gst/flacparse/gstbytereader.h (limited to 'gst') diff --git a/gst/flacparse/Makefile.am b/gst/flacparse/Makefile.am index 00e6f93f..8278130b 100644 --- a/gst/flacparse/Makefile.am +++ b/gst/flacparse/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstflacparse.la -libgstflacparse_la_SOURCES = gstflac.c gstbaseparse.c gstflacparse.c gstbitreader.c gstbytereader.c +libgstflacparse_la_SOURCES = gstflac.c gstbaseparse.c gstflacparse.c libgstflacparse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstflacparse_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ @@ -9,4 +9,4 @@ libgstflacparse_la_LIBADD = \ libgstflacparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstflacparse_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstbitreader.h gstbytereader.h gstbaseparse.h gstflacparse.h +noinst_HEADERS = gstbaseparse.h gstflacparse.h diff --git a/gst/flacparse/gstbitreader.c b/gst/flacparse/gstbitreader.c deleted file mode 100644 index 7647ab94..00000000 --- a/gst/flacparse/gstbitreader.c +++ /dev/null @@ -1,400 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 Sebastian Dröge . - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstbitreader.h" - -#include - -/** - * SECTION:gstbitreader - * @short_description: Reads any number of bits from a memory buffer - * - * #GstBitReader provides a bit reader that can read any number of bits - * from a memory buffer. It provides functions for reading any number of bits - * into 8, 16, 32 and 64 bit variables. - */ - -/** - * gst_bit_reader_new: - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * Create a new #GstBitReader instance, which will read from @data. - * - * Returns: a new #GstBitReader instance - * - * Since: 0.10.22 - */ -GstBitReader * -gst_bit_reader_new (const guint8 * data, guint size) -{ - GstBitReader *ret = g_slice_new0 (GstBitReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_bit_reader_new_from_buffer: - * @buffer: Buffer from which the #GstBitReader should read - * - * Create a new #GstBitReader instance, which will read from the - * #GstBuffer @buffer. - * - * Returns: a new #GstBitReader instance - * - * Since: 0.10.22 - */ -GstBitReader * -gst_bit_reader_new_from_buffer (const GstBuffer * buffer) -{ - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - - return gst_bit_reader_new (GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_bit_reader_free: - * @reader: a #GstBitReader instance - * - * Frees a #GstBitReader instance, which was previously allocated by - * gst_bit_reader_new() or gst_bit_reader_new_from_buffer(). - * - * Since: 0.10.22 - */ -void -gst_bit_reader_free (GstBitReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstBitReader, reader); -} - -/** - * gst_bit_reader_init: - * @reader: a #GstBitReader instance - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * Initializes a #GstBitReader instance to read from @data. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_bit_reader_init (GstBitReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = reader->bit = 0; -} - -/** - * gst_bit_reader_init: - * @reader: a #GstBitReader instance - * @buffer: Buffer from which the #GstBitReader should read - * - * Initializes a #GstBitReader instance to read from @buffer. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_bit_reader_init_from_buffer (GstBitReader * reader, - const GstBuffer * buffer) -{ - g_return_if_fail (GST_IS_BUFFER (buffer)); - - gst_bit_reader_init (reader, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_bit_reader_set_pos: - * @reader: a #GstBitReader instance - * @pos: The new position in bits - * - * Sets the new position of a #GstBitReader instance to @pos in bits. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_set_pos (GstBitReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size * 8) - return FALSE; - - reader->byte = pos / 8; - reader->bit = pos % 8; - - return TRUE; -} - -/** - * gst_bit_reader_get_pos: - * @reader: a #GstBitReader instance - * - * Returns the current position of a #GstBitReader instance in bits. - * - * Returns: The current position of @reader in bits. - * - * Since: 0.10.22 - */ -guint -gst_bit_reader_get_pos (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->byte * 8 + reader->bit; -} - -/** - * gst_bit_reader_get_remaining: - * @reader: a #GstBitReader instance - * - * Returns the remaining number of bits of a #GstBitReader instance. - * - * Returns: The remaining number of bits of @reader instance. - * - * Since: 0.10.22 - */ -guint -gst_bit_reader_get_remaining (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->size * 8 - (reader->byte * 8 + reader->bit); -} - -/** - * gst_bit_reader_skip: - * @reader: a #GstBitReader instance - * @nbits: the number of bits to skip - * - * Skips @nbits bits of the #GstBitReader instance. - * - * Returns: %TRUE if @nbits bits could be skipped, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_skip (GstBitReader * reader, guint nbits) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (gst_bit_reader_get_remaining (reader) < nbits) - return FALSE; - - reader->bit += nbits; - reader->byte += reader->bit / 8; - reader->bit = reader->bit % 8; - - return TRUE; -} - -/** - * gst_bit_reader_skip_to_byte: - * @reader: a #GstBitReader instance - * - * Skips until the next byte. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_bit_reader_skip_to_byte (GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (reader->byte > reader->size) - return FALSE; - - if (reader->bit) { - reader->bit = 0; - reader->byte++; - } - - return TRUE; -} - -/** - * gst_bit_reader_get_bits_uint8: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint16: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint32: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_get_bits_uint64: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint8: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint16: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint32: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_bit_reader_peek_bits_uint64: - * @reader: a #GstBitReader instance - * @val: Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BIT_READER_READ_BITS(bits) \ -gboolean \ -gst_bit_reader_get_bits_uint##bits (GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - guint##bits ret = 0; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - g_return_val_if_fail (nbits <= bits, FALSE); \ - \ - if (reader->byte * 8 + reader->bit + nbits > reader->size * 8) \ - return FALSE; \ - \ - while (nbits > 0) { \ - guint toread = MIN (nbits, 8 - reader->bit); \ - \ - ret <<= toread; \ - ret |= (reader->data[reader->byte] & (0xff >> reader->bit)) >> (8 - toread - reader->bit); \ - \ - reader->bit += toread; \ - if (reader->bit >= 8) { \ - reader->byte++; \ - reader->bit = 0; \ - } \ - nbits -= toread; \ - } \ - \ - *val = ret; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_bit_reader_peek_bits_uint##bits (const GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - GstBitReader tmp; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - tmp = *reader; \ - return gst_bit_reader_get_bits_uint##bits (&tmp, val, nbits); \ -} - -GST_BIT_READER_READ_BITS (8); -GST_BIT_READER_READ_BITS (16); -GST_BIT_READER_READ_BITS (32); -GST_BIT_READER_READ_BITS (64); diff --git a/gst/flacparse/gstbitreader.h b/gst/flacparse/gstbitreader.h deleted file mode 100644 index 5dc16a30..00000000 --- a/gst/flacparse/gstbitreader.h +++ /dev/null @@ -1,99 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 Sebastian Dröge . - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_BIT_READER_H__ -#define __GST_BIT_READER_H__ - -#include - -G_BEGIN_DECLS - -/** - * GstBitReader: - * @data: Data from which the bit reader will read - * @size: Size of @data in bytes - * @byte: Current byte position - * @bit: Bit position in the current byte - * - * A bit reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ - guint bit; /* Bit position in the current byte */ -} GstBitReader; - -GstBitReader * gst_bit_reader_new (const guint8 *data, guint size); -GstBitReader * gst_bit_reader_new_from_buffer (const GstBuffer *buffer); -void gst_bit_reader_free (GstBitReader *reader); - -void gst_bit_reader_init (GstBitReader *reader, const guint8 *data, guint size); -void gst_bit_reader_init_from_buffer (GstBitReader *reader, const GstBuffer *buffer); - -gboolean gst_bit_reader_set_pos (GstBitReader *reader, guint pos); - -guint gst_bit_reader_get_pos (const GstBitReader *reader); -guint gst_bit_reader_get_remaining (const GstBitReader *reader); - -gboolean gst_bit_reader_skip (GstBitReader *reader, guint nbits); -gboolean gst_bit_reader_skip_to_byte (GstBitReader *reader); - -gboolean gst_bit_reader_get_bits_uint8 (GstBitReader *reader, guint8 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint16 (GstBitReader *reader, guint16 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint32 (GstBitReader *reader, guint32 *val, guint nbits); -gboolean gst_bit_reader_get_bits_uint64 (GstBitReader *reader, guint64 *val, guint nbits); - -gboolean gst_bit_reader_peek_bits_uint8 (const GstBitReader *reader, guint8 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint16 (const GstBitReader *reader, guint16 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint32 (const GstBitReader *reader, guint32 *val, guint nbits); -gboolean gst_bit_reader_peek_bits_uint64 (const GstBitReader *reader, guint64 *val, guint nbits); - -/** - * GST_BIT_READER_INIT: - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * A #GstBitReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_bit_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BIT_READER_INIT(data, size) {data, size, 0, 0} - -/** - * GST_BIT_READER_INIT_FROM_BUFFER: - * @buffer: Buffer from which the #GstBitReader should read - * - * A #GstBitReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_bit_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BIT_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0, 0} - -G_END_DECLS - -#endif /* __GST_BIT_READER_H__ */ diff --git a/gst/flacparse/gstbytereader.c b/gst/flacparse/gstbytereader.c deleted file mode 100644 index cb91ea79..00000000 --- a/gst/flacparse/gstbytereader.c +++ /dev/null @@ -1,1231 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 Sebastian Dröge . - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstbytereader.h" - -#include - -/** - * SECTION:gstbytereader - * @short_description: Reads different integer and floating point types from a memory buffer - * - * #GstByteReader provides a byte reader that can read different integer and - * floating point types from a memory buffer. It provides functions for reading - * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits - * and functions for reading little/big endian floating points numbers of - * 32 and 64 bits. - */ - - -/* Copied from gst/floatcast/floatcast.h as this is in gst-plugins-base */ - -inline static gdouble -GDOUBLE_SWAP_LE_BE (gdouble in) -{ - union - { - guint64 i; - gdouble d; - } u; - - u.d = in; - u.i = GUINT64_SWAP_LE_BE (u.i); - return u.d; -} - -inline static gfloat -GFLOAT_SWAP_LE_BE (gfloat in) -{ - union - { - guint32 i; - gfloat f; - } u; - - u.f = in; - u.i = GUINT32_SWAP_LE_BE (u.i); - return u.f; -} - -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define GFLOAT_TO_LE(val) ((gfloat) (val)) -#define GFLOAT_TO_BE(val) (GFLOAT_SWAP_LE_BE (val)) -#define GDOUBLE_TO_LE(val) ((gdouble) (val)) -#define GDOUBLE_TO_BE(val) (GDOUBLE_SWAP_LE_BE (val)) - -#elif G_BYTE_ORDER == G_BIG_ENDIAN -#define GFLOAT_TO_LE(val) (GFLOAT_SWAP_LE_BE (val)) -#define GFLOAT_TO_BE(val) ((gfloat) (val)) -#define GDOUBLE_TO_LE(val) (GDOUBLE_SWAP_LE_BE (val)) -#define GDOUBLE_TO_BE(val) ((gdouble) (val)) - -#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ -#error unknown ENDIAN type -#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ - -#define GFLOAT_FROM_LE(val) (GFLOAT_TO_LE (val)) -#define GFLOAT_FROM_BE(val) (GFLOAT_TO_BE (val)) -#define GDOUBLE_FROM_LE(val) (GDOUBLE_TO_LE (val)) -#define GDOUBLE_FROM_BE(val) (GDOUBLE_TO_BE (val)) - - -/** - * gst_byte_reader_new: - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * Create a new #GstByteReader instance, which will read from @data. - * - * Returns: a new #GstByteReader instance - * - * Since: 0.10.22 - */ -GstByteReader * -gst_byte_reader_new (const guint8 * data, guint size) -{ - GstByteReader *ret = g_slice_new0 (GstByteReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_byte_reader_new_from_buffer: - * @buffer: Buffer from which the #GstByteReader should read - * - * Create a new #GstByteReader instance, which will read from the - * #GstBuffer @buffer. - * - * Returns: a new #GstByteReader instance - * - * Since: 0.10.22 - */ -GstByteReader * -gst_byte_reader_new_from_buffer (const GstBuffer * buffer) -{ - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - - return gst_byte_reader_new (GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_byte_reader_free: - * @reader: a #GstByteReader instance - * - * Frees a #GstByteReader instance, which was previously allocated by - * gst_byte_reader_new() or gst_byte_reader_new_from_buffer(). - * - * Since: 0.10.22 - */ -void -gst_byte_reader_free (GstByteReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstByteReader, reader); -} - -/** - * gst_byte_reader_init: - * @reader: a #GstByteReader instance - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * Initializes a #GstByteReader instance to read from @data. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_byte_reader_init (GstByteReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = 0; -} - -/** - * gst_byte_reader_init: - * @reader: a #GstByteReader instance - * @buffer: Buffer from which the #GstByteReader should read - * - * Initializes a #GstByteReader instance to read from @buffer. This function - * can be called on already initialized instances. - * - * Since: 0.10.22 - */ -void -gst_byte_reader_init_from_buffer (GstByteReader * reader, - const GstBuffer * buffer) -{ - g_return_if_fail (GST_IS_BUFFER (buffer)); - - gst_byte_reader_init (reader, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); -} - -/** - * gst_byte_reader_set_pos: - * @reader: a #GstByteReader instance - * @pos: The new position in bytes - * - * Sets the new position of a #GstByteReader instance to @pos in bytes. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_byte_reader_set_pos (GstByteReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size) - return FALSE; - - reader->byte = pos; - - return TRUE; -} - -/** - * gst_byte_reader_get_pos: - * @reader: a #GstByteReader instance - * - * Returns the current position of a #GstByteReader instance in bytes. - * - * Returns: The current position of @reader in bytes. - * - * Since: 0.10.22 - */ -guint -gst_byte_reader_get_pos (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->byte; -} - -/** - * gst_byte_reader_get_remaining: - * @reader: a #GstByteReader instance - * - * Returns the remaining number of bytes of a #GstByteReader instance. - * - * Returns: The remaining number of bytes of @reader instance. - * - * Since: 0.10.22 - */ -guint -gst_byte_reader_get_remaining (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return reader->size - reader->byte; -} - -/** - * gst_byte_reader_skip: - * @reader: a #GstByteReader instance - * @nbytes: the number of bytes to skip - * - * Skips @nbytes bytes of the #GstByteReader instance. - * - * Returns: %TRUE if @nbytes bytes could be skipped, %FALSE otherwise. - * - * Since: 0.10.22 - */ -gboolean -gst_byte_reader_skip (GstByteReader * reader, guint nbytes) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (gst_byte_reader_get_remaining (reader) < nbytes) - return FALSE; - - reader->byte += nbytes; - - return TRUE; -} - -/** - * gst_byte_reader_get_uint8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint8 to store the result - * - * Read an unsigned 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint8 to store the result - * - * Read a signed 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int8: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int16_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int16_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int24_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int24_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - - -/** - * gst_byte_reader_get_uint32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_uint64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_int64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_uint64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #guint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_int64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BYTE_READER_READ_INTS(bits) \ -gboolean \ -gst_byte_reader_get_uint##bits##_le (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_uint##bits##_be (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_int##bits##_le (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_get_int##bits##_be (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_uint##bits##_le (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_uint##bits##_be (GstByteReader *reader, guint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_int##bits##_le (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_LE (&reader->data[reader->byte]); \ - return TRUE; \ -} \ -\ -gboolean \ -gst_byte_reader_peek_int##bits##_be (GstByteReader *reader, gint##bits *val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - *val = GST_READ_UINT##bits##_BE (&reader->data[reader->byte]); \ - return TRUE; \ -} - - -GST_BYTE_READER_READ_INTS (16); -GST_BYTE_READER_READ_INTS (32); -GST_BYTE_READER_READ_INTS (64); - -gboolean -gst_byte_reader_get_uint8 (GstByteReader * reader, guint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - reader->byte++; - return TRUE; -} - -gboolean -gst_byte_reader_get_int8 (GstByteReader * reader, gint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - reader->byte++; - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint8 (GstByteReader * reader, guint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - return TRUE; -} - -gboolean -gst_byte_reader_peek_int8 (GstByteReader * reader, gint8 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 1 > reader->size) - return FALSE; - - *val = GST_READ_UINT8 (&reader->data[reader->byte]); - return TRUE; -} - -gboolean -gst_byte_reader_get_uint24_le (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - reader->byte += 3; - return TRUE; -} - -gboolean -gst_byte_reader_get_uint24_be (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - reader->byte += 3; - return TRUE; -} - -gboolean -gst_byte_reader_get_int24_le (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - - if (ret & 0x00800000) - ret |= 0xff000000; - - reader->byte += 3; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_get_int24_be (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - if (ret & 0x00800000) - ret |= 0xff000000; - - reader->byte += 3; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint24_le (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - return TRUE; -} - -gboolean -gst_byte_reader_peek_uint24_be (GstByteReader * reader, guint32 * val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - *val = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - return TRUE; -} - -gboolean -gst_byte_reader_peek_int24_le (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte + 2] << 16)); - - if (ret & 0x00800000) - ret |= 0xff000000; - - *val = ret; - return TRUE; -} - -gboolean -gst_byte_reader_peek_int24_be (GstByteReader * reader, gint32 * val) -{ - guint32 ret; - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (reader->byte + 3 > reader->size) - return FALSE; - - ret = - (reader->data[reader->byte + 2] | (reader->data[reader->byte + - 1] << 8) | (reader->data[reader->byte] << 16)); - if (ret & 0x00800000) - ret |= 0xff000000; - - *val = ret; - return TRUE; -} - -/** - * gst_byte_reader_get_float32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float32_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float32_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float64_le: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_get_float64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -/** - * gst_byte_reader_peek_float64_be: - * @reader: a #GstByteReader instance - * @val: Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - * - * Since: 0.10.22 - */ - -#define GST_BYTE_READER_READ_FLOATS(bits, type, TYPE) \ -gboolean \ -gst_byte_reader_get_float##bits##_le (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_LE (ret); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_get_float##bits##_be (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_BE (ret); \ - reader->byte += bits / 8; \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_float##bits##_le (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_LE (ret); \ - return TRUE; \ -} \ -gboolean \ -gst_byte_reader_peek_float##bits##_be (GstByteReader *reader, g##type *val) \ -{ \ - g##type ret; \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (reader->byte + bits / 8 > reader->size) \ - return FALSE; \ - \ - memcpy (&ret, &reader->data[reader->byte], bits / 8); \ - *val = G##TYPE##_FROM_BE (ret); \ - return TRUE; \ -} - -GST_BYTE_READER_READ_FLOATS (32, float, FLOAT); -GST_BYTE_READER_READ_FLOATS (64, double, DOUBLE); diff --git a/gst/flacparse/gstbytereader.h b/gst/flacparse/gstbytereader.h deleted file mode 100644 index 28534c00..00000000 --- a/gst/flacparse/gstbytereader.h +++ /dev/null @@ -1,134 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 Sebastian Dröge . - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_BYTE_READER_H__ -#define __GST_BYTE_READER_H__ - -#include - -G_BEGIN_DECLS - -/** - * GstByteReader: - * @data: Data from which the bit reader will read - * @size: Size of @data in bytes - * @byte: Current byte position - * - * A byte reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ -} GstByteReader; - -GstByteReader * gst_byte_reader_new (const guint8 *data, guint size); -GstByteReader * gst_byte_reader_new_from_buffer (const GstBuffer *buffer); -void gst_byte_reader_free (GstByteReader *reader); - -void gst_byte_reader_init (GstByteReader *reader, const guint8 *data, guint size); -void gst_byte_reader_init_from_buffer (GstByteReader *reader, const GstBuffer *buffer); - -gboolean gst_byte_reader_set_pos (GstByteReader *reader, guint pos); - -guint gst_byte_reader_get_pos (const GstByteReader *reader); -guint gst_byte_reader_get_remaining (const GstByteReader *reader); - -gboolean gst_byte_reader_skip (GstByteReader *reader, guint nbytes); - -gboolean gst_byte_reader_get_uint8 (GstByteReader *reader, guint8 *val); -gboolean gst_byte_reader_get_int8 (GstByteReader *reader, gint8 *val); -gboolean gst_byte_reader_get_uint16_le (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_get_int16_le (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_get_uint16_be (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_get_int16_be (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_get_uint24_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int24_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint24_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int24_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint32_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int32_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint32_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_get_int32_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_get_uint64_le (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_get_int64_le (GstByteReader *reader, gint64 *val); -gboolean gst_byte_reader_get_uint64_be (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_get_int64_be (GstByteReader *reader, gint64 *val); - -gboolean gst_byte_reader_peek_uint8 (GstByteReader *reader, guint8 *val); -gboolean gst_byte_reader_peek_int8 (GstByteReader *reader, gint8 *val); -gboolean gst_byte_reader_peek_uint16_le (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_peek_int16_le (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_peek_uint16_be (GstByteReader *reader, guint16 *val); -gboolean gst_byte_reader_peek_int16_be (GstByteReader *reader, gint16 *val); -gboolean gst_byte_reader_peek_uint24_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int24_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint24_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int24_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint32_le (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int32_le (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint32_be (GstByteReader *reader, guint32 *val); -gboolean gst_byte_reader_peek_int32_be (GstByteReader *reader, gint32 *val); -gboolean gst_byte_reader_peek_uint64_le (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_peek_int64_le (GstByteReader *reader, gint64 *val); -gboolean gst_byte_reader_peek_uint64_be (GstByteReader *reader, guint64 *val); -gboolean gst_byte_reader_peek_int64_be (GstByteReader *reader, gint64 *val); - -gboolean gst_byte_reader_get_float32_le (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_get_float32_be (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_get_float64_le (GstByteReader *reader, gdouble *val); -gboolean gst_byte_reader_get_float64_be (GstByteReader *reader, gdouble *val); - -gboolean gst_byte_reader_peek_float32_le (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_peek_float32_be (GstByteReader *reader, gfloat *val); -gboolean gst_byte_reader_peek_float64_le (GstByteReader *reader, gdouble *val); -gboolean gst_byte_reader_peek_float64_be (GstByteReader *reader, gdouble *val); - -/** - * GST_BYTE_READER_INIT: - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * A #GstByteReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_byte_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BYTE_READER_INIT(data, size) {data, size, 0} - -/** - * GST_BYTE_READER_INIT_FROM_BUFFER: - * @buffer: Buffer from which the #GstByteReader should read - * - * A #GstByteReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_byte_reader_init(). - * - * Since: 0.10.22 - */ -#define GST_BYTE_READER_INIT_FROM_BUFFER(buffer) {GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), 0} - -G_END_DECLS - -#endif /* __GST_BYTE_READER_H__ */ diff --git a/gst/flacparse/gstflacparse.c b/gst/flacparse/gstflacparse.c index 871082b1..d65ba062 100644 --- a/gst/flacparse/gstflacparse.c +++ b/gst/flacparse/gstflacparse.c @@ -28,8 +28,8 @@ #include #include -#include "gstbitreader.h" -#include "gstbytereader.h" +#include +#include GST_DEBUG_CATEGORY_STATIC (flacparse_debug); #define GST_CAT_DEFAULT flacparse_debug -- cgit v1.2.1 From 5a596e67dafefcebc85d270364bf7508e7edbcd5 Mon Sep 17 00:00:00 2001 From: Jan Urbanski Date: Tue, 4 Aug 2009 12:58:35 +0200 Subject: qtmux: Don't require endianness field for 8 bit raw audio Fixes bug #590360. --- gst/qtmux/gstqtmux.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'gst') diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c index ba155f6a..f1001966 100644 --- a/gst/qtmux/gstqtmux.c +++ b/gst/qtmux/gstqtmux.c @@ -1553,10 +1553,16 @@ gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps) if (!gst_structure_get_int (structure, "width", &width) || !gst_structure_get_int (structure, "depth", &depth) || - !gst_structure_get_boolean (structure, "signed", &sign) || - !gst_structure_get_int (structure, "endianness", &endianness)) { - GST_DEBUG_OBJECT (qtmux, - "broken caps, width/depth/signed/endianness field missing"); + !gst_structure_get_boolean (structure, "signed", &sign)) { + GST_DEBUG_OBJECT (qtmux, "broken caps, width/depth/signed field missing"); + goto refuse_caps; + } + + if (depth <= 8) { + endianness = G_BYTE_ORDER; + } else if (!gst_structure_get_boolean (structure, + "endianness", &endianness)) { + GST_DEBUG_OBJECT (qtmux, "broken caps, endianness field missing"); goto refuse_caps; } -- cgit v1.2.1 From 3f6e84ec6176ed9b2df7d180729cdf06412e6992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 5 Aug 2009 09:36:00 +0200 Subject: mpeg[pt]sdemux: Fix SEEKING query Send the BYTES based query downstream, not the orignal one. --- gst/mpegdemux/gstmpegdemux.c | 2 +- gst/mpegdemux/gstmpegtsdemux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index f8cba31b..18ecef8d 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -1368,7 +1368,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) * have the SCR */ peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); - res = gst_pad_peer_query (demux->sinkpad, query); + res = gst_pad_peer_query (demux->sinkpad, peerquery); if (!res || demux->scr_rate_n == G_MAXUINT64 || demux->scr_rate_d == G_MAXUINT64) { gst_query_set_seeking (query, fmt, FALSE, -1, -1); diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index eb40b186..7873266a 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -2778,7 +2778,7 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) * have a bitrate */ peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); - res = gst_pad_peer_query (demux->sinkpad, query); + res = gst_pad_peer_query (demux->sinkpad, peerquery); if (!res || demux->bitrate == -1) { gst_query_set_seeking (query, fmt, FALSE, -1, -1); } else { -- cgit v1.2.1 From 15b29bb8347938aebe920d4ebd2ec211dd43052a Mon Sep 17 00:00:00 2001 From: ric Date: Thu, 6 Aug 2009 19:26:21 +0200 Subject: rtpsource: avoid buffer leak on bad seqnum Fixes #590797 --- gst/rtpmanager/rtpsource.c | 1 + 1 file changed, 1 insertion(+) (limited to 'gst') diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 1e0b6c0d..28fa23ef 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -1074,6 +1074,7 @@ done: bad_sequence: { GST_WARNING ("unacceptable seqnum received"); + gst_buffer_unref (buffer); return GST_FLOW_OK; } } -- cgit v1.2.1 From 1f9525d8218cf96018b109dc682f74746e11487e Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Aug 2009 13:04:18 +0200 Subject: flacparse: sync baseparse (some more) --- gst/flacparse/gstbaseparse.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'gst') diff --git a/gst/flacparse/gstbaseparse.c b/gst/flacparse/gstbaseparse.c index 85fb8d71..8fb2b2ae 100644 --- a/gst/flacparse/gstbaseparse.c +++ b/gst/flacparse/gstbaseparse.c @@ -1026,6 +1026,12 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) skip = -1; if (bclass->check_valid_frame (parse, tmpbuf, &fsize, &skip)) { + if (gst_adapter_available (parse->adapter) < fsize) { + GST_DEBUG_OBJECT (parse, + "found valid frame but not enough data available (only %d bytes)", + gst_adapter_available (parse->adapter)); + goto done; + } break; } if (skip > 0) { -- cgit v1.2.1 From 1f2a0d3b3daea4ef63a6d49208346a0561538f54 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Aug 2009 13:06:28 +0200 Subject: baseparse: fix minor memory leak --- gst/aacparse/gstbaseparse.c | 1 + gst/amrparse/gstbaseparse.c | 1 + gst/flacparse/gstbaseparse.c | 1 + 3 files changed, 3 insertions(+) (limited to 'gst') diff --git a/gst/aacparse/gstbaseparse.c b/gst/aacparse/gstbaseparse.c index b59319a5..ffb17f3f 100644 --- a/gst/aacparse/gstbaseparse.c +++ b/gst/aacparse/gstbaseparse.c @@ -950,6 +950,7 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG_OBJECT (parse, "found valid frame but not enough data available (only %d bytes)", gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); goto done; } break; diff --git a/gst/amrparse/gstbaseparse.c b/gst/amrparse/gstbaseparse.c index 870492a8..f4d08fc7 100644 --- a/gst/amrparse/gstbaseparse.c +++ b/gst/amrparse/gstbaseparse.c @@ -950,6 +950,7 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG_OBJECT (parse, "found valid frame but not enough data available (only %d bytes)", gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); goto done; } break; diff --git a/gst/flacparse/gstbaseparse.c b/gst/flacparse/gstbaseparse.c index 8fb2b2ae..24b3516f 100644 --- a/gst/flacparse/gstbaseparse.c +++ b/gst/flacparse/gstbaseparse.c @@ -1030,6 +1030,7 @@ gst_base_parse_chain (GstPad * pad, GstBuffer * buffer) GST_DEBUG_OBJECT (parse, "found valid frame but not enough data available (only %d bytes)", gst_adapter_available (parse->adapter)); + gst_buffer_unref (tmpbuf); goto done; } break; -- cgit v1.2.1 From 222322e7f372397c18a9bd092be1ba8581586799 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Aug 2009 13:07:17 +0200 Subject: baseparse: prevent infinite loop when draining --- gst/aacparse/gstbaseparse.c | 8 ++++++++ gst/amrparse/gstbaseparse.c | 8 ++++++++ gst/flacparse/gstbaseparse.c | 8 ++++++++ 3 files changed, 24 insertions(+) (limited to 'gst') diff --git a/gst/aacparse/gstbaseparse.c b/gst/aacparse/gstbaseparse.c index ffb17f3f..cdd8b182 100644 --- a/gst/aacparse/gstbaseparse.c +++ b/gst/aacparse/gstbaseparse.c @@ -872,6 +872,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->adapter); if (!avail) @@ -881,6 +883,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } diff --git a/gst/amrparse/gstbaseparse.c b/gst/amrparse/gstbaseparse.c index f4d08fc7..68a8aad6 100644 --- a/gst/amrparse/gstbaseparse.c +++ b/gst/amrparse/gstbaseparse.c @@ -872,6 +872,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->adapter); if (!avail) @@ -881,6 +883,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } diff --git a/gst/flacparse/gstbaseparse.c b/gst/flacparse/gstbaseparse.c index 24b3516f..ccc2f3eb 100644 --- a/gst/flacparse/gstbaseparse.c +++ b/gst/flacparse/gstbaseparse.c @@ -895,6 +895,8 @@ gst_base_parse_drain (GstBaseParse * parse) { guint avail; + GST_DEBUG_OBJECT (parse, "draining"); + for (;;) { avail = gst_adapter_available (parse->priv->adapter); if (!avail) @@ -904,6 +906,12 @@ gst_base_parse_drain (GstBaseParse * parse) if (gst_base_parse_chain (parse->sinkpad, NULL) != GST_FLOW_OK) { break; } + + /* nothing changed, maybe due to truncated frame; break infinite loop */ + if (avail == gst_adapter_available (parse->adapter)) { + GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); + gst_adapter_clear (parse->adapter); + } } } -- cgit v1.2.1 From 3c22fb611bdbc9bc3a768264ea3e8b726d2c231c Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Fri, 7 Aug 2009 18:17:28 +0200 Subject: mpegpsdemux: improved demuxer performance Increased performance doing pull_range in blocks of 32Kb instead of 4Kb. Caching the value of gst_adapter_available instead of calling it 3 times. Added some comments with the header descriptions. Peek enough data to avoid a corner case where could be readed data outside the buffer. Speed up some more inlining some functions and keeping another stream pointer list to be used as iterator. Sprinkle branch prediction macros accross the code. Handling the seeking with flush in pull mode in the proper way. --- gst/mpegdemux/gstmpegdemux.c | 183 +++++++++++++++++++++++++++++-------------- gst/mpegdemux/gstmpegdemux.h | 3 + 2 files changed, 127 insertions(+), 59 deletions(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 18ecef8d..88b7bae5 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -53,7 +53,7 @@ #define MAX_DVD_AUDIO_STREAMS 8 #define MAX_DVD_SUBPICTURE_STREAMS 32 -#define BLOCK_SZ 4096 +#define BLOCK_SZ 32768 #define SCAN_SCR_SZ 12 #define SCAN_PTS_SZ 80 @@ -185,9 +185,9 @@ static inline gboolean gst_flups_demux_scan_forward_ts (GstFluPSDemux * demux, static inline gboolean gst_flups_demux_scan_backward_ts (GstFluPSDemux * demux, guint64 * pos, SCAN_MODE mode, guint64 * rts); -static void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, +static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, GstClockTime new_time); -static void gst_flups_demux_clear_times (GstFluPSDemux * demux); +static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux); static GstElementClass *parent_class = NULL; @@ -281,6 +281,9 @@ gst_flups_demux_init (GstFluPSDemux * demux) demux->streams = g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->streams_found = + g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->found_count = 0; } @@ -289,6 +292,7 @@ gst_flups_demux_finalize (GstFluPSDemux * demux) { gst_flups_demux_reset (demux); g_free (demux->streams); + g_free (demux->streams_found); G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (demux)); } @@ -311,6 +315,9 @@ gst_flups_demux_reset (GstFluPSDemux * demux) demux->streams[i] = NULL; } } + memset (demux->streams_found, 0, + sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS)); + demux->found_count = 0; p_ev = &demux->lang_codes; gst_event_replace (p_ev, NULL); @@ -447,6 +454,7 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type) gst_element_add_pad (GST_ELEMENT (demux), stream->pad); demux->streams[id] = stream; + demux->streams_found[demux->found_count++] = stream; } return stream; @@ -470,13 +478,13 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, goto no_stream; /* timestamps */ - if (demux->next_pts != G_MAXUINT64) + if (G_UNLIKELY (demux->next_pts != G_MAXUINT64)) timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts); else timestamp = GST_CLOCK_TIME_NONE; /* discont */ - if (stream->need_segment) { + if (G_UNLIKELY (stream->need_segment)) { gint64 time, start, stop; GstEvent *newsegment; @@ -584,17 +592,17 @@ no_stream: } } -static void +static inline void gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont, gboolean need_segment) { - gint id; + gint i, count = demux->found_count; /* mark discont on all streams */ - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; - if (stream) { + if (G_LIKELY (stream)) { stream->discont |= discont; stream->need_segment |= need_segment; GST_DEBUG_OBJECT (demux, "marked stream as discont %d, need_segment %d", @@ -603,14 +611,14 @@ gst_flups_demux_mark_discont (GstFluPSDemux * demux, gboolean discont, } } -static gboolean +static inline gboolean gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event) { - gint id; + gint i, count = demux->found_count; gboolean ret = FALSE; - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream && !stream->notlinked) { (void) gst_event_ref (event); @@ -733,27 +741,27 @@ gst_flups_demux_flush (GstFluPSDemux * demux) demux->bytes_since_scr = 0; } -static void +static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux) { - gint id; + gint i, count = demux->found_count; /* Clear the last ts for all streams */ - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; - if (stream) { + if (G_LIKELY (stream)) { stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; } } } -static void +static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, GstClockTime new_time) { /* Advance all lagging streams by sending a segment update */ - gint id; + gint i, count = demux->found_count; GstEvent *event = NULL; /* FIXME: Handle reverse playback */ @@ -761,8 +769,8 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, if (new_time > demux->src_segment.stop) return; - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream) { if (stream->last_ts == GST_CLOCK_TIME_NONE || @@ -796,10 +804,10 @@ gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, gst_event_unref (event); } -static void +static inline void gst_flups_demux_close_segment (GstFluPSDemux * demux) { - gint id; + gint i, count = demux->found_count; GstEvent *event = NULL; guint64 base_time; @@ -813,7 +821,6 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) if ((base_time = demux->base_time) == (guint64) - 1) base_time = 0; - /* Close the current segment for a linear playback */ if (demux->src_segment.rate >= 0) { /* for forward playback, we played from start to last_stop */ @@ -835,8 +842,8 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) } if (event) { - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; + for (i = 0; i < count; i++) { + GstFluPSStream *stream = demux->streams_found[i]; if (stream && !stream->notlinked && !stream->need_segment) { (void) gst_event_ref (event); @@ -859,14 +866,7 @@ gst_flups_demux_close_segment (GstFluPSDemux * demux) static inline gboolean have_open_streams (GstFluPSDemux * demux) { - gint id; - - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - if (demux->streams[id]) - return TRUE; - } - - return FALSE; + return (demux->streams_found[0] != NULL); } static gboolean @@ -1094,6 +1094,7 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) if (flush) { /* Flush start up and downstream to make sure data flow and loops are idle */ + demux->flushing = TRUE; gst_flups_demux_send_event (demux, gst_event_new_flush_start ()); gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ()); } else { @@ -1106,6 +1107,7 @@ gst_flups_demux_handle_seek_pull (GstFluPSDemux * demux, GstEvent * event) if (flush) { /* Stop flushing upstream we need to pull */ + demux->flushing = FALSE; gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ()); } @@ -1427,6 +1429,33 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux) #undef FILL_TYPE } +/* ISO/IEC 13818-1: + * pack_header() { + * pack_start_code 32 bslbf -+ + * '01' 2 bslbf | + * system_clock_reference_base [32..30] 3 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [29..15] 15 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [14..0] 15 bslbf | + * marker_bit 1 bslbf | 112 bits + * system_clock_reference_extension 9 ubslbf | + * marker_bit 1 bslbf | + * program_mux_rate 22 ubslbf | + * marker_bit 1 bslbf | + * marker_bit 1 bslbf | + * reserved 5 bslbf | + * pack_stuffing_length 3 ubslbf -+ + * + * for (i = 0; i < pack_stuffing_length; i++) { + * stuffing_byte '1111 1111' 8 bslbf + * } + * + * 112 bits = 14 bytes, as max value for pack_stuffing_length is 7, then + * in total it's needed 14 + 7 = 21 bytes. + */ +#define PACK_START_SIZE 21 + static GstFlowReturn gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) { @@ -1436,21 +1465,24 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) guint64 scr, scr_adjusted, new_rate; guint64 scr_rate_n; guint64 scr_rate_d; + guint avail = gst_adapter_available (demux->adapter); GST_DEBUG ("parsing pack start"); - /* fixed length to begin with, start code and two scr values */ - length = 8 + 4; - - if (!(data = gst_adapter_peek (demux->adapter, length))) + if (G_UNLIKELY (avail < PACK_START_SIZE)) goto need_more_data; + data = gst_adapter_peek (demux->adapter, PACK_START_SIZE); + /* skip start code */ data += 4; scr1 = GUINT32_FROM_BE (*(guint32 *) data); scr2 = GUINT32_FROM_BE (*(guint32 *) (data + 4)); + /* fixed length to begin with, start code and two scr values */ + length = 8 + 4; + /* start parsing the stream */ if ((*data & 0xc0) == 0x40) { guint32 scr_ext; @@ -1462,13 +1494,11 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* mpeg2 has more data */ length += 2; - if (gst_adapter_available (demux->adapter) < length) - goto need_more_data; /* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */ /* check markers */ - if ((scr1 & 0xc4000400) != 0x44000400) + if (G_UNLIKELY ((scr1 & 0xc4000400) != 0x44000400)) goto lost_sync; scr = ((guint64) scr1 & 0x38000000) << 3; @@ -1477,7 +1507,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr |= ((guint64) scr2 & 0xf8000000) >> 27; /* marker:1==1 ! scr_ext:9 ! marker:1==1 */ - if ((scr2 & 0x04010000) != 0x04010000) + if (G_UNLIKELY ((scr2 & 0x04010000) != 0x04010000)) goto lost_sync; scr_ext = (scr2 & 0x03fe0000) >> 17; @@ -1494,7 +1524,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) data += 6; /* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */ next32 = (GUINT32_FROM_BE ((*(guint32 *) data))); - if ((next32 & 0x00000300) != 0x00000300) + if (G_UNLIKELY ((next32 & 0x00000300) != 0x00000300)) goto lost_sync; new_rate = (next32 & 0xfffffc00) >> 10; @@ -1503,6 +1533,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes); data += 4; + length += stuffing_bytes; while (stuffing_bytes--) { if (*data++ != 0xff) goto lost_sync; @@ -1512,10 +1543,10 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) demux->is_mpeg2_pack = FALSE; /* check markers */ - if ((scr1 & 0xf1000100) != 0x21000100) + if (G_UNLIKELY ((scr1 & 0xf1000100) != 0x21000100)) goto lost_sync; - if ((scr2 & 0x01800001) != 0x01800001) + if (G_UNLIKELY ((scr2 & 0x01800001) != 0x01800001)) goto lost_sync; /* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */ @@ -1544,14 +1575,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr))); /* keep the first src in order to calculate delta time */ - if (demux->first_scr == G_MAXUINT64) { + if (G_UNLIKELY (demux->first_scr == G_MAXUINT64)) { demux->first_scr = scr; demux->first_scr_offset = demux->cur_scr_offset; demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr); /* at begin consider the new_rate as the scr rate, bytes/clock ticks */ scr_rate_n = new_rate; scr_rate_d = CLOCK_FREQ; - } else if (demux->first_scr_offset != demux->cur_scr_offset) { + } else if (G_LIKELY (demux->first_scr_offset != demux->cur_scr_offset)) { /* estimate byte rate related to the SCR */ scr_rate_n = demux->cur_scr_offset - demux->first_scr_offset; scr_rate_d = scr_adjusted - demux->first_scr; @@ -1570,7 +1601,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr_rate_n, scr_rate_d, (float) scr_rate_n / scr_rate_d); /* adjustment of the SCR */ - if (demux->current_scr != G_MAXUINT64) { + if (G_LIKELY (demux->current_scr != G_MAXUINT64)) { gint64 diff; guint64 old_scr, old_mux_rate, bss, adjust = 0; @@ -1581,8 +1612,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* Bytes since SCR is the amount we placed in the adapter since then * (demux->bytes_since_scr) minus the amount remaining in the adapter, * clamped to >= 0 */ - bss = MAX (0, (gint) (demux->bytes_since_scr - - gst_adapter_available (demux->adapter))); + bss = MAX (0, (gint) (demux->bytes_since_scr - avail)); /* estimate the new SCR using the previous one according the notes on point 2.5.2.2 of the ISO/IEC 13818-1 document */ @@ -1601,14 +1631,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* calculate the absolute deference between the last scr and the new one */ - if (old_scr > scr_adjusted) + if (G_UNLIKELY (old_scr > scr_adjusted)) diff = old_scr - scr_adjusted; else diff = scr_adjusted - old_scr; /* if the difference is more than 1 second we need to reconfigure adjustment */ - if (diff > CLOCK_FREQ) { + if (G_UNLIKELY (diff > CLOCK_FREQ)) { demux->scr_adjust = demux->next_scr - scr; GST_DEBUG_OBJECT (demux, "discont found, diff: %" G_GINT64_FORMAT ", adjust %" G_GINT64_FORMAT, diff, demux->scr_adjust); @@ -1629,7 +1659,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* Reset the bytes_since_scr value to count the data remaining in the * adapter */ - demux->bytes_since_scr = gst_adapter_available (demux->adapter); + demux->bytes_since_scr = avail; gst_adapter_flush (demux->adapter, length); ADAPTER_OFFSET_FLUSH (length); @@ -1647,6 +1677,32 @@ need_more_data: } } +/* ISO/IEC 13818-1: + * system_header () { + * system_header_start_code 32 bslbf -+ + * header_length 16 uimsbf | + * marker_bit 1 bslbf | + * rate_bound 22 uimsbf | + * marker_bit 1 bslbf | + * audio_bound 6 uimsbf | + * fixed_flag 1 bslbf | + * CSPS_flag 1 bslbf | 96 bits + * system_audio_lock_flag 1 bslbf | + * system_video_lock_flag 1 bslbf | + * marker_bit 1 bslbf | + * video_bound 5 uimsbf | + * packet_rate_restriction_flag 1 bslbf | + * reserved_bits 7 bslbf -+ + * while (nextbits () = = '1') { + * stream_id 8 uimsbf -+ + * '11' 2 bslbf | 24 bits + * P-STD_buffer_bound_scale 1 bslbf | + * P-STD_buffer_size_bound 13 uimsbf -+ + * } + * } + * 96 bits = 12 bytes, 24 bits = 3 bytes. + */ + static GstFlowReturn gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) { @@ -1938,7 +1994,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) { guint8 nframes; - /* VDR writes A52 streams without any header bytes + /* VDR writes A52 streams without any header bytes * (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */ if (datalen >= 4) { guint hdr = GST_READ_UINT32_BE (data); @@ -1994,7 +2050,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type); } - if (demux->current_stream == NULL) { + if (G_UNLIKELY (demux->current_stream == NULL)) { GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x", id); goto done; @@ -2050,7 +2106,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) gboolean found; avail = gst_adapter_available (demux->adapter); - if (avail < 4) + if (G_UNLIKELY (avail < 4)) goto need_data; /* Common case, read 4 bytes an check it */ @@ -2066,7 +2122,7 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) return TRUE; } - /* Otherwise, we are starting at byte 4 and we need to search + /* Otherwise, we are starting at byte 4 and we need to search the sync code in all available data in the adapter */ offset = 4; if (offset >= avail) @@ -2546,6 +2602,11 @@ gst_flups_demux_loop (GstPad * pad) demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + if (G_UNLIKELY (demux->flushing)) { + ret = GST_FLOW_WRONG_STATE; + goto pause; + } + if (G_UNLIKELY (demux->sink_segment.format == GST_FORMAT_UNDEFINED)) gst_flups_sink_get_duration (demux); @@ -2794,6 +2855,10 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer) save = TRUE; while (gst_flups_demux_resync (demux, save)) { gboolean ps_sync = TRUE; + if (G_UNLIKELY (demux->flushing)) { + ret = GST_FLOW_WRONG_STATE; + goto done; + } /* now switch on last synced byte */ switch (demux->last_sync_code) { diff --git a/gst/mpegdemux/gstmpegdemux.h b/gst/mpegdemux/gstmpegdemux.h index 29b3d5e5..a8822350 100644 --- a/gst/mpegdemux/gstmpegdemux.h +++ b/gst/mpegdemux/gstmpegdemux.h @@ -101,6 +101,7 @@ struct _GstFluPSDemux GstPad *sinkpad; gboolean random_access; /* If we operate in pull mode */ + gboolean flushing; GstAdapter *adapter; GstAdapter *rev_adapter; @@ -136,6 +137,8 @@ struct _GstFluPSDemux guint64 next_pts; guint64 next_dts; GstFluPSStream **streams; + GstFluPSStream **streams_found; + gint found_count; gboolean need_no_more_pads; /* Indicates an MPEG-2 stream */ -- cgit v1.2.1 From da95f4a873624ab92ceec191f8b4ccc49247c3dd Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Fri, 7 Aug 2009 18:35:42 +0200 Subject: mpegpsdemux: added caps for AAC and fixed playback of a clip with LPCM --- gst/mpegdemux/gstmpegdemux.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdemux.c b/gst/mpegdemux/gstmpegdemux.c index 88b7bae5..5fe27711 100644 --- a/gst/mpegdemux/gstmpegdemux.c +++ b/gst/mpegdemux/gstmpegdemux.c @@ -140,7 +140,7 @@ static GstStaticPadTemplate audio_template = GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS ("audio/mpeg, " - "mpegversion = (int) 1;" + "mpegversion = (int) { 1, 4 };" "audio/x-private1-lpcm; " "audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3") ); @@ -374,7 +374,12 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) case ST_PRIVATE_DATA: case ST_MHEG: case ST_DSMCC: + break; case ST_AUDIO_AAC: + template = klass->audio_template; + name = g_strdup_printf ("audio_%02x", id); + caps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, NULL); break; case ST_VIDEO_H264: template = klass->video_template; @@ -679,7 +684,7 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) break; case 0x2: case 0x3: - /* MPEG audio without and with extension stream are + /* MPEG audio without and with extension stream are * treated the same */ stream_id = 0xC0 + i; temp = gst_flups_demux_get_stream (demux, stream_id, ST_AUDIO_MPEG1); @@ -1296,7 +1301,7 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) break; } - /* Upstream didn't know, so we can only answer TIME queries from + /* Upstream didn't know, so we can only answer TIME queries from * here on */ if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (demux, "duration not supported for format %d", @@ -1942,7 +1947,15 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info", stream_type, stream_id, stream_info_length); - demux->psm[stream_id] = stream_type; + if (G_LIKELY (stream_id != 0xbd)) + demux->psm[stream_id] = stream_type; + else { + /* Ignore stream type for private_stream_1 and discover it looking at + * the stream data. + * Fixes demuxing some clips with lpcm that was wrongly declared as + * mpeg audio */ + GST_DEBUG_OBJECT (demux, "stream type for private_stream_1 ignored"); + } es_map_base += stream_info_length; } -- cgit v1.2.1 From 1a9b54b7818b59786c524ff2604fd58cf926d3b7 Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Fri, 7 Aug 2009 19:00:23 +0200 Subject: mpegtsdemux: added VC1, EAC3 and LPCM related to blueray/hdmv --- gst/mpegdemux/gstmpegdefs.h | 65 +++++++++++++++++++++++++----------------- gst/mpegdemux/gstmpegtsdemux.c | 28 ++++++++++++++---- 2 files changed, 62 insertions(+), 31 deletions(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h index 375f1dc0..40551137 100644 --- a/gst/mpegdemux/gstmpegdefs.h +++ b/gst/mpegdemux/gstmpegdefs.h @@ -147,39 +147,52 @@ * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved * 0x80-0xFF User Private */ -#define ST_RESERVED 0x00 -#define ST_VIDEO_MPEG1 0x01 -#define ST_VIDEO_MPEG2 0x02 -#define ST_AUDIO_MPEG1 0x03 -#define ST_AUDIO_MPEG2 0x04 -#define ST_PRIVATE_SECTIONS 0x05 -#define ST_PRIVATE_DATA 0x06 -#define ST_MHEG 0x07 -#define ST_DSMCC 0x08 -#define ST_H222_1 0x09 +#define ST_RESERVED 0x00 +#define ST_VIDEO_MPEG1 0x01 +#define ST_VIDEO_MPEG2 0x02 +#define ST_AUDIO_MPEG1 0x03 +#define ST_AUDIO_MPEG2 0x04 +#define ST_PRIVATE_SECTIONS 0x05 +#define ST_PRIVATE_DATA 0x06 +#define ST_MHEG 0x07 +#define ST_DSMCC 0x08 +#define ST_H222_1 0x09 /* later extensions */ -#define ST_AUDIO_AAC 0x0f -#define ST_VIDEO_MPEG4 0x10 -#define ST_VIDEO_H264 0x1b +#define ST_AUDIO_AAC 0x0f +#define ST_VIDEO_MPEG4 0x10 +#define ST_VIDEO_H264 0x1b /* Un-official Dirac extension */ -#define ST_VIDEO_DIRAC 0xd1 +#define ST_VIDEO_DIRAC 0xd1 /* private stream types */ -#define ST_PS_AUDIO_AC3 0x81 -#define ST_PS_AUDIO_DTS 0x8a -#define ST_PS_AUDIO_LPCM 0x8b +#define ST_PS_AUDIO_AC3 0x81 +#define ST_PS_AUDIO_DTS 0x8a +#define ST_PS_AUDIO_LPCM 0x8b #define ST_PS_DVD_SUBPICTURE 0xff -/* Blu-ray PGS subpictures */ +/* Blu-ray related */ +#define ST_BD_AUDIO_LPCM 0x80 +#define ST_BD_AUDIO_AC3 0x81 +#define ST_BD_AUDIO_DTS 0x82 +#define ST_BD_AUDIO_AC3_TRUE_HD 0x83 +#define ST_BD_AUDIO_AC3_PLUS 0x84 +#define ST_BD_AUDIO_DTS_HD 0x85 #define ST_BD_PGS_SUBPICTURE 0x90 +#define ST_BD_IGS 0x91 +#define ST_BD_SUBTITLE 0x92 +#define ST_BD_SECONDARY_AC3_PLUS 0xa1 +#define ST_BD_SECONDARY_DTS_HD 0xa2 + +/* VC1 extension */ +#define ST_VIDEO_VC1 0xea /* HDV AUX stream mapping * 0xA0 ISO/IEC 61834-11 * 0xA1 ISO/IEC 61834-11 */ -#define ST_HDV_AUX_A 0xa0 -#define ST_HDV_AUX_V 0xa1 +#define ST_HDV_AUX_A 0xa0 +#define ST_HDV_AUX_V 0xa1 /* Un-official time-code stream */ #define ST_PS_TIMECODE 0xd2 @@ -202,16 +215,16 @@ /* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */ #define READ_TS(data, target, lost_sync_label) \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target = ((guint64) (*data++ & 0x0E)) << 29; \ - target |= ((guint64) (*data++ )) << 22; \ + target = ((guint64) (*data++ & 0x0E)) << 29; \ + target |= ((guint64) (*data++ )) << 22; \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target |= ((guint64) (*data++ & 0xFE)) << 14; \ - target |= ((guint64) (*data++ )) << 7; \ + target |= ((guint64) (*data++ & 0xFE)) << 14; \ + target |= ((guint64) (*data++ )) << 7; \ if ((*data & 0x01) != 0x01) goto lost_sync_label; \ target |= ((guint64) (*data++ & 0xFE)) >> 1; /* some extra GstFlowReturn values used internally */ -#define GST_FLOW_NEED_MORE_DATA -100 -#define GST_FLOW_LOST_SYNC -101 +#define GST_FLOW_NEED_MORE_DATA -100 +#define GST_FLOW_LOST_SYNC -101 #endif /* __GST_MPEG_DEFS_H__ */ diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 7873266a..811646ee 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -119,7 +119,10 @@ enum "mpegversion = (int) { 1, 2, 4 }, " \ "systemstream = (boolean) FALSE; " \ "video/x-h264;" \ - "video/x-dirac" \ + "video/x-dirac;" \ + "video/x-wmv," \ + "wmvversion = (int) 3, " \ + "format = (fourcc) WVC1" \ ) #define AUDIO_CAPS \ @@ -133,8 +136,9 @@ enum "dynamic_range = (int) [ 0, 255 ], " \ "emphasis = (boolean) { FALSE, TRUE }, " \ "mute = (boolean) { FALSE, TRUE }; " \ - "audio/x-ac3;" \ - "audio/x-dts" \ + "audio/x-ac3; audio/x-eac3;" \ + "audio/x-dts;" \ + "audio/x-private1-lpcm" \ ) /* Can also use the subpicture pads for text subtitles? */ @@ -556,6 +560,7 @@ gst_mpegts_stream_is_video (GstMpegTSStream * stream) case ST_VIDEO_MPEG2: case ST_VIDEO_MPEG4: case ST_VIDEO_H264: + case ST_VIDEO_VC1: return TRUE; case ST_VIDEO_DIRAC: return gst_mpegts_is_dirac_stream (stream); @@ -652,10 +657,18 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, caps = gst_caps_new_simple ("video/x-dirac", NULL); } break; - case ST_PS_AUDIO_AC3: + case ST_VIDEO_VC1: + template = klass->video_template; + name = g_strdup_printf ("video_%04x", stream->PID); + caps = gst_caps_new_simple ("video/x-wmv", + "wmvversion", G_TYPE_INT, 3, + "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'), + NULL); + break; + case ST_BD_AUDIO_AC3: template = klass->audio_template; name = g_strdup_printf ("audio_%04x", stream->PID); - caps = gst_caps_new_simple ("audio/x-ac3", NULL); + caps = gst_caps_new_simple ("audio/x-eac3", NULL); break; case ST_PS_AUDIO_DTS: template = klass->audio_template; @@ -667,6 +680,11 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id, name = g_strdup_printf ("audio_%04x", stream->PID); caps = gst_caps_new_simple ("audio/x-lpcm", NULL); break; + case ST_BD_AUDIO_LPCM: + template = klass->audio_template; + name = g_strdup_printf ("audio_%04x", stream->PID); + caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL); + break; case ST_PS_DVD_SUBPICTURE: template = klass->subpicture_template; name = g_strdup_printf ("subpicture_%04x", stream->PID); -- cgit v1.2.1 From 986190892662219a6b1f265219957a8c7a15b299 Mon Sep 17 00:00:00 2001 From: Josep Torra Date: Fri, 7 Aug 2009 19:12:26 +0200 Subject: mpegtsdemux: fix a memory leak --- gst/mpegdemux/gstmpegtsdemux.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 811646ee..5a9a41de 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -385,7 +385,10 @@ gst_mpegts_demux_reset (GstMpegTSDemux * demux) gst_section_filter_uninit (&stream->section_filter); break; } - + if (stream->pes_buffer) { + gst_buffer_unref (stream->pes_buffer); + stream->pes_buffer = NULL; + } g_free (stream); demux->streams[i] = NULL; } -- cgit v1.2.1 From 8433f7a9c61547ed509201e2cedf9212fb7d7d63 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: 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 +- gst/rawparse/gstrawparse.h | 6 +- gst/rawparse/gstvideoparse.c | 362 ++++++++++--------------------------------- gst/rawparse/gstvideoparse.h | 18 +-- 4 files changed, 92 insertions(+), 296 deletions(-) (limited to 'gst') diff --git a/gst/rawparse/Makefile.am b/gst/rawparse/Makefile.am index 88e4e8c0..b56db45b 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 diff --git a/gst/rawparse/gstrawparse.h b/gst/rawparse/gstrawparse.h index da0de444..3536d289 100644 --- a/gst/rawparse/gstrawparse.h +++ b/gst/rawparse/gstrawparse.h @@ -55,9 +55,9 @@ struct _GstRawParse GstActivateMode mode; GstAdapter *adapter; - int framesize; - int fps_d; - int fps_n; + gint framesize; + gint fps_d; + gint fps_n; gboolean discont; guint64 n_frames; diff --git a/gst/rawparse/gstvideoparse.c b/gst/rawparse/gstvideoparse.c index d245fa75..99d4a7f6 100644 --- a/gst/rawparse/gstvideoparse.c +++ b/gst/rawparse/gstvideoparse.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 2006 David A. Schleef - * Copyright (C) 2007 Sebastian Dröge + * Copyright (C) 2007,2009 Sebastian Dröge * * gstvideoparse.c: * @@ -31,23 +31,6 @@ #include "gstvideoparse.h" -typedef enum -{ - GST_VIDEO_PARSE_FORMAT_I420, - GST_VIDEO_PARSE_FORMAT_YV12, - GST_VIDEO_PARSE_FORMAT_YUY2, - GST_VIDEO_PARSE_FORMAT_UYVY, - GST_VIDEO_PARSE_FORMAT_v210, - GST_VIDEO_PARSE_FORMAT_RGB = 10, - GST_VIDEO_PARSE_FORMAT_GRAY -} GstVideoParseFormat; - -typedef enum -{ - GST_VIDEO_PARSE_ENDIANNESS_LITTLE = 1234, - GST_VIDEO_PARSE_ENDIANNESS_BIG = 4321 -} GstVideoParseEndianness; - static void gst_video_parse_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_video_parse_get_property (GObject * object, guint prop_id, @@ -62,76 +45,18 @@ static void gst_video_parse_update_frame_size (GstVideoParse * vp); GST_DEBUG_CATEGORY_STATIC (gst_video_parse_debug); #define GST_CAT_DEFAULT gst_video_parse_debug -static const GstElementDetails gst_video_parse_details = -GST_ELEMENT_DETAILS ("Video Parse", - "Filter/Video", - "Converts stream into video frames", - "David Schleef , " - "Sebastian Dröge "); - enum { - ARG_0, - ARG_WIDTH, - ARG_HEIGHT, - ARG_FORMAT, - ARG_PAR, - ARG_FRAMERATE, - ARG_INTERLACED, - ARG_TOP_FIELD_FIRST, - ARG_BPP, - ARG_DEPTH, - ARG_ENDIANNESS, - ARG_RED_MASK, - ARG_GREEN_MASK, - ARG_BLUE_MASK, - ARG_ALPHA_MASK + PROP_0, + PROP_FORMAT, + PROP_WIDTH, + PROP_HEIGHT, + PROP_PAR, + PROP_FRAMERATE, + PROP_INTERLACED, + PROP_TOP_FIELD_FIRST }; - -#define GST_VIDEO_PARSE_FORMAT (gst_video_parse_format_get_type ()) -static GType -gst_video_parse_format_get_type (void) -{ - static GType video_parse_format_type = 0; - static const GEnumValue format_types[] = { - {GST_VIDEO_PARSE_FORMAT_I420, "I420", "I420"}, - {GST_VIDEO_PARSE_FORMAT_YV12, "YV12", "YV12"}, - {GST_VIDEO_PARSE_FORMAT_YUY2, "YUY2", "YUY2"}, - {GST_VIDEO_PARSE_FORMAT_UYVY, "UYVY", "UYVY"}, - {GST_VIDEO_PARSE_FORMAT_v210, "v210", "v210"}, - {GST_VIDEO_PARSE_FORMAT_RGB, "RGB", "RGB"}, - {GST_VIDEO_PARSE_FORMAT_GRAY, "GRAY", "GRAY"}, - {0, NULL, NULL} - }; - - if (!video_parse_format_type) { - video_parse_format_type = - g_enum_register_static ("GstVideoParseFormat", format_types); - } - - return video_parse_format_type; -} - -#define GST_VIDEO_PARSE_ENDIANNESS (gst_video_parse_endianness_get_type ()) -static GType -gst_video_parse_endianness_get_type (void) -{ - static GType video_parse_endianness_type = 0; - static const GEnumValue endian_types[] = { - {GST_VIDEO_PARSE_ENDIANNESS_LITTLE, "Little Endian", "little"}, - {GST_VIDEO_PARSE_ENDIANNESS_BIG, "Big Endian", "big"}, - {0, NULL, NULL} - }; - - if (!video_parse_endianness_type) { - video_parse_endianness_type = - g_enum_register_static ("GstVideoParseEndianness", endian_types); - } - - return video_parse_endianness_type; -} - GST_BOILERPLATE (GstVideoParse, gst_video_parse, GstRawParse, GST_TYPE_RAW_PARSE); @@ -145,12 +70,33 @@ gst_video_parse_base_init (gpointer g_class) GST_DEBUG_CATEGORY_INIT (gst_video_parse_debug, "videoparse", 0, "videoparse element"); - gst_element_class_set_details (gstelement_class, &gst_video_parse_details); - - caps = - gst_caps_from_string (GST_VIDEO_CAPS_YUV - ("{ I420, YV12, YUY2, UYVY, v210 }") ";" - "video/x-raw-rgb; video/x-raw-gray"); + gst_element_class_set_details_simple (gstelement_class, "Video Parse", + "Filter/Video", + "Converts stream into video frames", + "David Schleef , " + "Sebastian Dröge "); + + caps = gst_caps_from_string (GST_VIDEO_CAPS_YUV ("I420") ";" + GST_VIDEO_CAPS_YUV ("YV12") ";" + GST_VIDEO_CAPS_YUV ("YUY2") ";" + GST_VIDEO_CAPS_YUV ("UYVY") ";" + GST_VIDEO_CAPS_YUV ("AYUV") ";" + GST_VIDEO_CAPS_RGBx ";" + GST_VIDEO_CAPS_BGRx ";" + GST_VIDEO_CAPS_xRGB ";" + GST_VIDEO_CAPS_xBGR ";" + GST_VIDEO_CAPS_RGBA ";" + GST_VIDEO_CAPS_BGRA ";" + GST_VIDEO_CAPS_ARGB ";" + GST_VIDEO_CAPS_ABGR ";" + GST_VIDEO_CAPS_RGB ";" + GST_VIDEO_CAPS_BGR ";" + GST_VIDEO_CAPS_YUV ("Y41B") ";" + GST_VIDEO_CAPS_YUV ("Y42B") ";" + GST_VIDEO_CAPS_YUV ("YVYU") ";" + GST_VIDEO_CAPS_YUV ("Y444") ";" + GST_VIDEO_CAPS_YUV ("Y210") ";" GST_VIDEO_CAPS_YUV ("Y216") + ); gst_raw_parse_class_set_src_pad_template (rp_class, caps); gst_raw_parse_class_set_multiple_frames_per_buffer (rp_class, FALSE); @@ -169,57 +115,32 @@ gst_video_parse_class_init (GstVideoParseClass * klass) rp_class->get_caps = gst_video_parse_get_caps; rp_class->set_buffer_flags = gst_video_parse_set_buffer_flags; - g_object_class_install_property (gobject_class, ARG_WIDTH, + g_object_class_install_property (gobject_class, PROP_FORMAT, + g_param_spec_enum ("format", "Format", "Format of images in raw stream", + GST_TYPE_VIDEO_FORMAT, GST_VIDEO_FORMAT_I420, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_WIDTH, g_param_spec_int ("width", "Width", "Width of images in raw stream", - 0, INT_MAX, 320, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_HEIGHT, + 0, INT_MAX, 320, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_HEIGHT, g_param_spec_int ("height", "Height", "Height of images in raw stream", - 0, INT_MAX, 240, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_FORMAT, - g_param_spec_enum ("format", "Format", "Format of images in raw stream", - GST_VIDEO_PARSE_FORMAT, GST_VIDEO_PARSE_FORMAT_I420, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_FRAMERATE, + 0, INT_MAX, 240, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_FRAMERATE, gst_param_spec_fraction ("framerate", "Frame Rate", "Frame rate of images in raw stream", 0, 1, 100, 1, 25, 1, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_INTERLACED, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAR, + gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio", + "Pixel aspect ratio of images in raw stream", 1, 100, 100, 1, 1, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_INTERLACED, g_param_spec_boolean ("interlaced", "Interlaced flag", - "True if video is interlaced", FALSE, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_TOP_FIELD_FIRST, + "True if video is interlaced", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_TOP_FIELD_FIRST, g_param_spec_boolean ("top-field-first", "Top field first", "True if top field is earlier than bottom field", TRUE, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_PAR, - gst_param_spec_fraction ("pixel_aspect_ratio", "Pixel Aspect Ratio", - "Pixel aspect ratio of images in raw stream", 1, 100, 100, 1, 1, 1, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_BPP, - g_param_spec_int ("bpp", "Bpp", "Bits per pixel of images in raw stream", - 0, INT_MAX, 24, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_DEPTH, - g_param_spec_int ("depth", "Depth", "Depth of images in raw stream", 0, - INT_MAX, 24, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_ENDIANNESS, - g_param_spec_enum ("endianness", "Endianness", - "Endianness of images in raw stream", GST_VIDEO_PARSE_ENDIANNESS, - G_BYTE_ORDER, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_RED_MASK, - g_param_spec_int ("red-mask", "Red mask", - "Red mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE1_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_GREEN_MASK, - g_param_spec_int ("green-mask", "Green mask", - "Green mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE2_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_BLUE_MASK, - g_param_spec_int ("blue-mask", "Blue mask", - "Blue mask of images in raw stream", INT_MIN, INT_MAX, - GST_VIDEO_BYTE3_MASK_24_INT, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_ALPHA_MASK, - g_param_spec_int ("alpha-mask", "Alpha mask", - "Alpha mask of images in raw stream", INT_MIN, INT_MAX, 0, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -227,16 +148,9 @@ gst_video_parse_init (GstVideoParse * vp, GstVideoParseClass * g_class) { vp->width = 320; vp->height = 240; - vp->format = GST_VIDEO_PARSE_FORMAT_I420; + vp->format = GST_VIDEO_FORMAT_I420; vp->par_n = 1; vp->par_d = 1; - vp->bpp = 24; - vp->depth = 24; - vp->endianness = G_BYTE_ORDER; - vp->red_mask = GST_VIDEO_BYTE1_MASK_24_INT; - vp->green_mask = GST_VIDEO_BYTE2_MASK_24_INT; - vp->blue_mask = GST_VIDEO_BYTE3_MASK_24_INT; - vp->alpha_mask = 0; gst_video_parse_update_frame_size (vp); gst_raw_parse_set_fps (GST_RAW_PARSE (vp), 25, 1); @@ -251,50 +165,29 @@ gst_video_parse_set_property (GObject * object, guint prop_id, g_return_if_fail (!gst_raw_parse_is_negotiated (GST_RAW_PARSE (vp))); switch (prop_id) { - case ARG_WIDTH: + case PROP_FORMAT: + vp->format = g_value_get_enum (value); + break; + case PROP_WIDTH: vp->width = g_value_get_int (value); break; - case ARG_HEIGHT: + case PROP_HEIGHT: vp->height = g_value_get_int (value); break; - case ARG_FORMAT: - vp->format = g_value_get_enum (value); - break; - case ARG_FRAMERATE: + case PROP_FRAMERATE: gst_raw_parse_set_fps (GST_RAW_PARSE (vp), gst_value_get_fraction_numerator (value), gst_value_get_fraction_denominator (value)); break; - case ARG_INTERLACED: - vp->interlaced = g_value_get_boolean (value); - break; - case ARG_TOP_FIELD_FIRST: - vp->top_field_first = g_value_get_boolean (value); - break; - case ARG_PAR: + case PROP_PAR: vp->par_n = gst_value_get_fraction_numerator (value); vp->par_d = gst_value_get_fraction_denominator (value); break; - case ARG_BPP: - vp->bpp = g_value_get_int (value); - break; - case ARG_DEPTH: - vp->depth = g_value_get_int (value); - break; - case ARG_ENDIANNESS: - vp->endianness = g_value_get_enum (value); - break; - case ARG_RED_MASK: - vp->red_mask = g_value_get_int (value); - break; - case ARG_GREEN_MASK: - vp->green_mask = g_value_get_int (value); - break; - case ARG_BLUE_MASK: - vp->blue_mask = g_value_get_int (value); + case PROP_INTERLACED: + vp->interlaced = g_value_get_boolean (value); break; - case ARG_ALPHA_MASK: - vp->alpha_mask = g_value_get_int (value); + case PROP_TOP_FIELD_FIRST: + vp->top_field_first = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -311,51 +204,30 @@ gst_video_parse_get_property (GObject * object, guint prop_id, GValue * value, GstVideoParse *vp = GST_VIDEO_PARSE (object); switch (prop_id) { - case ARG_WIDTH: + case PROP_FORMAT: + g_value_set_enum (value, vp->format); + break; + case PROP_WIDTH: g_value_set_int (value, vp->width); break; - case ARG_HEIGHT: + case PROP_HEIGHT: g_value_set_int (value, vp->height); break; - case ARG_FORMAT: - g_value_set_enum (value, vp->format); - break; - case ARG_FRAMERATE:{ + case PROP_FRAMERATE:{ gint fps_n, fps_d; gst_raw_parse_get_fps (GST_RAW_PARSE (vp), &fps_n, &fps_d); gst_value_set_fraction (value, fps_n, fps_d); break; } - case ARG_INTERLACED: - g_value_set_boolean (value, vp->interlaced); - break; - case ARG_TOP_FIELD_FIRST: - g_value_set_boolean (value, vp->top_field_first); - break; - case ARG_PAR: + case PROP_PAR: gst_value_set_fraction (value, vp->par_n, vp->par_d); break; - case ARG_BPP: - g_value_set_int (value, vp->bpp); - break; - case ARG_DEPTH: - g_value_set_int (value, vp->depth); - break; - case ARG_ENDIANNESS: - g_value_set_enum (value, vp->endianness); - break; - case ARG_RED_MASK: - g_value_set_int (value, vp->red_mask); - break; - case ARG_GREEN_MASK: - g_value_set_int (value, vp->green_mask); - break; - case ARG_BLUE_MASK: - g_value_set_int (value, vp->blue_mask); + case PROP_INTERLACED: + g_value_set_boolean (value, vp->interlaced); break; - case ARG_ALPHA_MASK: - g_value_set_int (value, vp->alpha_mask); + case PROP_TOP_FIELD_FIRST: + g_value_set_boolean (value, vp->top_field_first); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -363,47 +235,12 @@ gst_video_parse_get_property (GObject * object, guint prop_id, GValue * value, } } -static guint32 -gst_video_parse_format_to_fourcc (GstVideoParseFormat format) -{ - switch (format) { - case GST_VIDEO_PARSE_FORMAT_I420: - return GST_MAKE_FOURCC ('I', '4', '2', '0'); - case GST_VIDEO_PARSE_FORMAT_YV12: - return GST_MAKE_FOURCC ('Y', 'V', '1', '2'); - case GST_VIDEO_PARSE_FORMAT_YUY2: - return GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'); - case GST_VIDEO_PARSE_FORMAT_UYVY: - return GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'); - case GST_VIDEO_PARSE_FORMAT_v210: - return GST_MAKE_FOURCC ('v', '2', '1', '0'); - default: - g_assert_not_reached (); - } - return 0; -} - void gst_video_parse_update_frame_size (GstVideoParse * vp) { gint framesize; - if (vp->format == GST_VIDEO_PARSE_FORMAT_I420 || - vp->format == GST_VIDEO_PARSE_FORMAT_YV12) { - framesize = GST_ROUND_UP_4 (vp->width) * GST_ROUND_UP_2 (vp->height) - + - 2 * (GST_ROUND_UP_8 (vp->width) / 2) * (GST_ROUND_UP_2 (vp->height) / - 2); - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_YUY2 - || vp->format == GST_VIDEO_PARSE_FORMAT_UYVY) { - framesize = GST_ROUND_UP_4 (vp->width * 2) * vp->height; - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_v210) { - framesize = ((vp->width + 47) / 48) * 128 * vp->height; - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_RGB) { - framesize = GST_ROUND_UP_4 (vp->width * vp->bpp / 8) * vp->height; - } else { - framesize = GST_ROUND_UP_4 (vp->width * vp->bpp / 8) * vp->height; - } + framesize = gst_video_format_get_size (vp->format, vp->width, vp->height); gst_raw_parse_set_framesize (GST_RAW_PARSE (vp), framesize); } @@ -418,39 +255,10 @@ gst_video_parse_get_caps (GstRawParse * rp) gst_raw_parse_get_fps (rp, &fps_n, &fps_d); - if (vp->format < GST_VIDEO_PARSE_FORMAT_RGB) { - caps = gst_caps_new_simple ("video/x-raw-yuv", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "format", GST_TYPE_FOURCC, - gst_video_parse_format_to_fourcc (vp->format), "framerate", - GST_TYPE_FRACTION, fps_n, fps_d, "pixel-aspect-ratio", - GST_TYPE_FRACTION, vp->par_n, vp->par_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, NULL); - } else if (vp->format == GST_VIDEO_PARSE_FORMAT_RGB) { - caps = gst_caps_new_simple ("video/x-raw-rgb", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "bpp", G_TYPE_INT, vp->bpp, - "depth", G_TYPE_INT, vp->depth, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, - "pixel-aspect-ratio", GST_TYPE_FRACTION, vp->par_n, vp->par_d, - "red_mask", G_TYPE_INT, vp->red_mask, - "green_mask", G_TYPE_INT, vp->green_mask, - "blue_mask", G_TYPE_INT, vp->blue_mask, - "alpha_mask", G_TYPE_INT, vp->alpha_mask, - "endianness", G_TYPE_INT, vp->endianness, NULL); - } else { - caps = gst_caps_new_simple ("video/x-raw-gray", - "width", G_TYPE_INT, vp->width, - "height", G_TYPE_INT, vp->height, - "bpp", G_TYPE_INT, vp->bpp, - "depth", G_TYPE_INT, vp->depth, - "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - "interlaced", G_TYPE_BOOLEAN, vp->interlaced, - "pixel-aspect-ratio", GST_TYPE_FRACTION, vp->par_n, vp->par_d, NULL); - } + caps = + gst_video_format_new_caps_interlaced (vp->format, vp->width, vp->height, + fps_n, fps_d, vp->par_n, vp->par_d, vp->interlaced); + return caps; } @@ -459,10 +267,6 @@ gst_video_parse_set_buffer_flags (GstRawParse * rp, GstBuffer * buffer) { GstVideoParse *vp = GST_VIDEO_PARSE (rp); -/* remove this after -bad deps on -base-0.10.23 */ -#ifndef GST_VIDEO_BUFFER_TFF -#define GST_VIDEO_BUFFER_TFF (GST_MINI_OBJECT_FLAG_LAST << 5) -#endif if (vp->interlaced) { if (vp->top_field_first) { GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_TFF); diff --git a/gst/rawparse/gstvideoparse.h b/gst/rawparse/gstvideoparse.h index da210ea1..d2e2b363 100644 --- a/gst/rawparse/gstvideoparse.h +++ b/gst/rawparse/gstvideoparse.h @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 2006 David A. Schleef - * Copyright (C) 2007 Sebastian Dröge + * Copyright (C) 2007,2009 Sebastian Dröge * * gstvideoparse.h: * @@ -49,20 +49,12 @@ struct _GstVideoParse GstRawParse parent; /* properties */ - int width; - int height; - guint32 format; - int par_n; - int par_d; + GstVideoFormat format; + gint width; + gint height; + gint par_n, par_d; gboolean interlaced; gboolean top_field_first; - int bpp; - int depth; - int endianness; - int red_mask; - int blue_mask; - int green_mask; - int alpha_mask; }; struct _GstVideoParseClass -- cgit v1.2.1 From 60a5d37683805b50e746aa68b653b6594ca99e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 08:27:24 +0200 Subject: audioparse: Small cleanups --- gst/rawparse/gstaudioparse.c | 86 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 43 deletions(-) (limited to 'gst') diff --git a/gst/rawparse/gstaudioparse.c b/gst/rawparse/gstaudioparse.c index f7eb6651..37a4f69d 100644 --- a/gst/rawparse/gstaudioparse.c +++ b/gst/rawparse/gstaudioparse.c @@ -56,22 +56,16 @@ static void gst_audio_parse_update_frame_size (GstAudioParse * ap); GST_DEBUG_CATEGORY_STATIC (gst_audio_parse_debug); #define GST_CAT_DEFAULT gst_audio_parse_debug -static const GstElementDetails gst_audio_parse_details = -GST_ELEMENT_DETAILS ("Audio Parse", - "Filter/Audio", - "Converts stream into audio frames", - "Sebastian Dröge "); - enum { - ARG_0, - ARG_FORMAT, - ARG_RATE, - ARG_CHANNELS, - ARG_ENDIANNESS, - ARG_WIDTH, - ARG_DEPTH, - ARG_SIGNED + PROP_0, + PROP_FORMAT, + PROP_RATE, + PROP_CHANNELS, + PROP_ENDIANNESS, + PROP_WIDTH, + PROP_DEPTH, + PROP_SIGNED }; @@ -128,7 +122,10 @@ gst_audio_parse_base_init (gpointer g_class) GST_DEBUG_CATEGORY_INIT (gst_audio_parse_debug, "audioparse", 0, "audioparse element"); - gst_element_class_set_details (gstelement_class, &gst_audio_parse_details); + gst_element_class_set_details_simple (gstelement_class, "Audio Parse", + "Filter/Audio", + "Converts stream into audio frames", + "Sebastian Dröge "); caps = gst_caps_from_string ("audio/x-raw-int," @@ -161,38 +158,41 @@ gst_audio_parse_class_init (GstAudioParseClass * klass) rp_class->get_caps = gst_audio_parse_get_caps; - g_object_class_install_property (gobject_class, ARG_FORMAT, + g_object_class_install_property (gobject_class, PROP_FORMAT, g_param_spec_enum ("format", "Format", "Format of audio samples in raw stream", GST_AUDIO_PARSE_FORMAT, - GST_AUDIO_PARSE_FORMAT_INT, G_PARAM_READWRITE)); + GST_AUDIO_PARSE_FORMAT_INT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_RATE, + g_object_class_install_property (gobject_class, PROP_RATE, g_param_spec_int ("rate", "Rate", "Rate of audio samples in raw stream", - 1, INT_MAX, 44100, G_PARAM_READWRITE)); + 1, INT_MAX, 44100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_CHANNELS, + g_object_class_install_property (gobject_class, PROP_CHANNELS, g_param_spec_int ("channels", "Channels", "Number of channels in raw stream", 1, INT_MAX, 2, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_WIDTH, + g_object_class_install_property (gobject_class, PROP_WIDTH, g_param_spec_int ("width", "Width", "Width of audio samples in raw stream", 1, INT_MAX, 16, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_DEPTH, + g_object_class_install_property (gobject_class, PROP_DEPTH, g_param_spec_int ("depth", "Depth", "Depth of audio samples in raw stream", 1, INT_MAX, 16, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_SIGNED, + g_object_class_install_property (gobject_class, PROP_SIGNED, g_param_spec_boolean ("signed", "signed", - "Sign of audio samples in raw stream", TRUE, G_PARAM_READWRITE)); + "Sign of audio samples in raw stream", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, ARG_ENDIANNESS, + g_object_class_install_property (gobject_class, PROP_ENDIANNESS, g_param_spec_enum ("endianness", "Endianness", "Endianness of audio samples in raw stream", - GST_AUDIO_PARSE_ENDIANNESS, G_BYTE_ORDER, G_PARAM_READWRITE)); + GST_AUDIO_PARSE_ENDIANNESS, G_BYTE_ORDER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -218,25 +218,25 @@ gst_audio_parse_set_property (GObject * object, guint prop_id, g_return_if_fail (!gst_raw_parse_is_negotiated (GST_RAW_PARSE (ap))); switch (prop_id) { - case ARG_FORMAT: + case PROP_FORMAT: ap->format = g_value_get_enum (value); break; - case ARG_RATE: + case PROP_RATE: gst_raw_parse_set_fps (GST_RAW_PARSE (ap), g_value_get_int (value), 1); break; - case ARG_CHANNELS: + case PROP_CHANNELS: ap->channels = g_value_get_int (value); break; - case ARG_WIDTH: + case PROP_WIDTH: ap->width = g_value_get_int (value); break; - case ARG_DEPTH: + case PROP_DEPTH: ap->depth = g_value_get_int (value); break; - case ARG_SIGNED: + case PROP_SIGNED: ap->signedness = g_value_get_boolean (value); break; - case ARG_ENDIANNESS: + case PROP_ENDIANNESS: ap->endianness = g_value_get_enum (value); break; default: @@ -254,29 +254,29 @@ gst_audio_parse_get_property (GObject * object, guint prop_id, GValue * value, GstAudioParse *ap = GST_AUDIO_PARSE (object); switch (prop_id) { - case ARG_FORMAT: + case PROP_FORMAT: g_value_set_enum (value, ap->format); break; - case ARG_RATE:{ + case PROP_RATE:{ gint fps_n, fps_d; gst_raw_parse_get_fps (GST_RAW_PARSE (ap), &fps_n, &fps_d); g_value_set_int (value, fps_n); break; } - case ARG_CHANNELS: + case PROP_CHANNELS: g_value_set_int (value, ap->channels); break; - case ARG_WIDTH: + case PROP_WIDTH: g_value_set_int (value, ap->width); break; - case ARG_DEPTH: + case PROP_DEPTH: g_value_set_int (value, ap->depth); break; - case ARG_SIGNED: + case PROP_SIGNED: g_value_set_boolean (value, ap->signedness); break; - case ARG_ENDIANNESS: + case PROP_ENDIANNESS: g_value_set_enum (value, ap->endianness); break; default: -- cgit v1.2.1 From 5c11f80c047f95b5301071282521bf92eb39c8f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 10:27:52 +0200 Subject: rawparse: Small cleanups and implement SEEKING query --- gst/rawparse/gstrawparse.c | 31 ++++++++++++++++++++++++++----- gst/rawparse/gstrawparse.h | 2 +- 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'gst') diff --git a/gst/rawparse/gstrawparse.c b/gst/rawparse/gstrawparse.c index d5dae600..07e42c28 100644 --- a/gst/rawparse/gstrawparse.c +++ b/gst/rawparse/gstrawparse.c @@ -100,8 +100,6 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) rp->sinkpad = gst_pad_new_from_static_template (&gst_raw_parse_sink_pad_template, "sink"); - gst_element_add_pad (GST_ELEMENT (rp), rp->sinkpad); - gst_pad_set_chain_function (rp->sinkpad, GST_DEBUG_FUNCPTR (gst_raw_parse_chain)); gst_pad_set_event_function (rp->sinkpad, @@ -110,6 +108,7 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activate)); gst_pad_set_activatepull_function (rp->sinkpad, GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activatepull)); + gst_element_add_pad (GST_ELEMENT (rp), rp->sinkpad); src_pad_template = gst_element_class_get_pad_template (element_class, "src"); @@ -120,15 +119,13 @@ gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class) g_assert_not_reached (); } - gst_element_add_pad (GST_ELEMENT (rp), rp->srcpad); - gst_pad_set_event_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_event)); - gst_pad_set_query_type_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_query_type)); gst_pad_set_query_function (rp->srcpad, GST_DEBUG_FUNCPTR (gst_raw_parse_src_query)); + gst_element_add_pad (GST_ELEMENT (rp), rp->srcpad); rp->adapter = gst_adapter_new (); @@ -914,6 +911,7 @@ gst_raw_parse_src_query_type (GstPad * pad) GST_QUERY_POSITION, GST_QUERY_DURATION, GST_QUERY_CONVERT, + GST_QUERY_SEEKING, 0 }; @@ -992,6 +990,29 @@ gst_raw_parse_src_query (GstPad * pad, GstQuery * query) gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } + case GST_QUERY_SEEKING:{ + GstFormat fmt; + + ret = TRUE; + gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); + if (fmt != GST_FORMAT_TIME && fmt != GST_FORMAT_DEFAULT + && fmt != GST_FORMAT_BYTES) { + gst_query_set_seeking (query, fmt, FALSE, -1, -1); + } else if (rp->mode == GST_ACTIVATE_PUSH) { + GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES); + gboolean seekable; + + seekable = gst_pad_peer_query (rp->sinkpad, peerquery); + if (seekable) + gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL); + + gst_query_unref (peerquery); + gst_query_set_seeking (query, fmt, seekable, seekable ? 0 : -1, -1); + } else { + gst_query_set_seeking (query, fmt, TRUE, 0, -1); + } + break; + } default: /* else forward upstream */ ret = gst_pad_peer_query (rp->sinkpad, query); diff --git a/gst/rawparse/gstrawparse.h b/gst/rawparse/gstrawparse.h index 3536d289..0a2c01b3 100644 --- a/gst/rawparse/gstrawparse.h +++ b/gst/rawparse/gstrawparse.h @@ -48,7 +48,7 @@ struct _GstRawParse { GstElement parent; - /* private */ + /* */ GstPad *sinkpad; GstPad *srcpad; -- cgit v1.2.1 From 282479b4434dbfd6541d71a8891943603a12de89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 10:30:11 +0200 Subject: videoparse: Only provide very fundamental caps in the pad template We can't easily know which caps will can be used because we rely on libgstvideo's support for video formats. --- gst/rawparse/gstvideoparse.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'gst') diff --git a/gst/rawparse/gstvideoparse.c b/gst/rawparse/gstvideoparse.c index 99d4a7f6..16dde999 100644 --- a/gst/rawparse/gstvideoparse.c +++ b/gst/rawparse/gstvideoparse.c @@ -76,27 +76,9 @@ gst_video_parse_base_init (gpointer g_class) "David Schleef , " "Sebastian Dröge "); - caps = gst_caps_from_string (GST_VIDEO_CAPS_YUV ("I420") ";" - GST_VIDEO_CAPS_YUV ("YV12") ";" - GST_VIDEO_CAPS_YUV ("YUY2") ";" - GST_VIDEO_CAPS_YUV ("UYVY") ";" - GST_VIDEO_CAPS_YUV ("AYUV") ";" - GST_VIDEO_CAPS_RGBx ";" - GST_VIDEO_CAPS_BGRx ";" - GST_VIDEO_CAPS_xRGB ";" - GST_VIDEO_CAPS_xBGR ";" - GST_VIDEO_CAPS_RGBA ";" - GST_VIDEO_CAPS_BGRA ";" - GST_VIDEO_CAPS_ARGB ";" - GST_VIDEO_CAPS_ABGR ";" - GST_VIDEO_CAPS_RGB ";" - GST_VIDEO_CAPS_BGR ";" - GST_VIDEO_CAPS_YUV ("Y41B") ";" - GST_VIDEO_CAPS_YUV ("Y42B") ";" - GST_VIDEO_CAPS_YUV ("YVYU") ";" - GST_VIDEO_CAPS_YUV ("Y444") ";" - GST_VIDEO_CAPS_YUV ("Y210") ";" GST_VIDEO_CAPS_YUV ("Y216") - ); + caps = + gst_caps_from_string + ("video/x-raw-rgb; video/x-raw-yuv; video/x-raw-gray; video/x-raw-bayer"); gst_raw_parse_class_set_src_pad_template (rp_class, caps); gst_raw_parse_class_set_multiple_frames_per_buffer (rp_class, FALSE); -- cgit v1.2.1 From 6115e0cb0ef3ae85397297c7e30c2e9b4a0a181e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 8 Aug 2009 22:14:53 +0200 Subject: mpegtsdemux: Don't answer the SEEKING query if we don't know the answer yet A bitrate!=-1 is required for seeking but the bitrate is only calculated after the second PCR was read. Fixes bug #590446. --- gst/mpegdemux/gstmpegtsdemux.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'gst') diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index 5a9a41de..0a6f28a5 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -2795,6 +2795,13 @@ gst_mpegts_demux_src_pad_query (GstPad * pad, GstQuery * query) goto beach; } + /* We can't say anything about seekability if we didn't + * have a second PCR yet because the bitrate is calculated + * from this + */ + if (demux->bitrate == -1 && demux->pcr[1] == -1) + goto beach; + /* We can seek if upstream supports BYTES seeks and we * have a bitrate */ -- cgit v1.2.1