diff options
31 files changed, 1740 insertions, 71 deletions
@@ -28,6 +28,7 @@ autoregen.sh ABOUT-NLS _stdint.h gst-plugins-bad-*.tar.* +tests/examples/shapewipe/shapewipe-example .deps .libs diff --git a/configure.ac b/configure.ac index a9b89f0f..294348d7 100644 --- a/configure.ac +++ b/configure.ac @@ -266,6 +266,7 @@ AG_GST_CHECK_PLUGIN(dvdspu) AG_GST_CHECK_PLUGIN(festival) AG_GST_CHECK_PLUGIN(freeze) AG_GST_CHECK_PLUGIN(h264parse) +AG_GST_CHECK_PLUGIN(hdvparse) AG_GST_CHECK_PLUGIN(id3tag) AG_GST_CHECK_PLUGIN(librfb) AG_GST_CHECK_PLUGIN(liveadder) @@ -286,6 +287,7 @@ AG_GST_CHECK_PLUGIN(rtpmux) AG_GST_CHECK_PLUGIN(scaletempo) AG_GST_CHECK_PLUGIN(sdp) AG_GST_CHECK_PLUGIN(selector) +AG_GST_CHECK_PLUGIN(shapewipe) AG_GST_CHECK_PLUGIN(siren) AG_GST_CHECK_PLUGIN(speed) AG_GST_CHECK_PLUGIN(subenc) @@ -1308,10 +1310,13 @@ AG_GST_CHECK_FEATURE(SOUNDTOUCH, [soundtouch plug-in], soundtouch, [ [HAVE_SOUNDTOUCH=yes HAVE_SOUNDTOUCH_1_4=no SOUNDTOUCH_LIBS="$SOUNDTOUCH_LIBS -lBPM"], - [PKG_CHECK_MODULES(SOUNDTOUCH, libSoundTouch, - HAVE_SOUNDTOUCH=yes - SOUNDTOUCH_LIBS="$SOUNDTOUCH_LIBS -lBPM", - HAVE_SOUNDTOUCH=no)])]) + [PKG_CHECK_MODULES(SOUNDTOUCH, libSoundTouch >= 1.4, + [HAVE_SOUNDTOUCH=yes], + [PKG_CHECK_MODULES(SOUNDTOUCH, libSoundTouch, + [HAVE_SOUNDTOUCH=yes + HAVE_SOUNDTOUCH_1_4=no + SOUNDTOUCH_LIBS="$SOUNDTOUCH_LIBS -lBPM"], + HAVE_SOUNDTOUCH=no)])])]) AC_SUBST(SOUNDTOUCH_CFLAGS) AC_SUBST(SOUNDTOUCH_LIBS) if test "x$HAVE_CXX" != "xyes"; then @@ -1585,6 +1590,7 @@ gst/dvdspu/Makefile gst/festival/Makefile gst/freeze/Makefile gst/h264parse/Makefile +gst/hdvparse/Makefile gst/id3tag/Makefile gst/librfb/Makefile gst/mpegdemux/Makefile @@ -1604,6 +1610,7 @@ gst/rtpmux/Makefile gst/scaletempo/Makefile gst/sdp/Makefile gst/selector/Makefile +gst/shapewipe/Makefile gst/siren/Makefile gst/speed/Makefile gst/subenc/Makefile @@ -1639,6 +1646,7 @@ sys/winscreencap/Makefile tests/examples/Makefile tests/examples/directfb/Makefile tests/examples/mxf/Makefile +tests/examples/shapewipe/Makefile tests/examples/scaletempo/Makefile tests/examples/switch/Makefile ext/amrwb/Makefile diff --git a/ext/jack/gstjackaudioclient.c b/ext/jack/gstjackaudioclient.c index 1aa1baf8..60e41381 100644 --- a/ext/jack/gstjackaudioclient.c +++ b/ext/jack/gstjackaudioclient.c @@ -42,6 +42,7 @@ typedef struct { gint refcount; GMutex *lock; + GCond *flush_cond; /* id/server pair and the connection */ gchar *id; @@ -61,6 +62,7 @@ struct _GstJackAudioClient GstJackClientType type; gboolean active; + gboolean deactivate; void (*shutdown) (void *arg); JackProcessCallback process; @@ -92,15 +94,25 @@ jack_process_cb (jack_nframes_t nframes, void *arg) GstJackAudioClient *client = (GstJackAudioClient *) walk->data; /* only call active clients */ - if (client->active && client->process) + if ((client->active || client->deactivate) && client->process) { res = client->process (nframes, client->user_data); + if (client->deactivate) { + client->deactivate = FALSE; + g_cond_signal (conn->flush_cond); + } + } } for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) { GstJackAudioClient *client = (GstJackAudioClient *) walk->data; /* only call active clients */ - if (client->active && client->process) + if ((client->active || client->deactivate) && client->process) { res = client->process (nframes, client->user_data); + if (client->deactivate) { + client->deactivate = FALSE; + g_cond_signal (conn->flush_cond); + } + } } g_mutex_unlock (conn->lock); @@ -203,6 +215,7 @@ gst_jack_audio_make_connection (const gchar * id, const gchar * server, conn = g_new (GstJackAudioConnection, 1); conn->refcount = 1; conn->lock = g_mutex_new (); + conn->flush_cond = g_cond_new (); conn->id = g_strdup (id); conn->server = g_strdup (server); conn->client = jclient; @@ -325,6 +338,7 @@ gst_jack_audio_unref_connection (GstJackAudioConnection * conn) /* free resources */ g_mutex_free (conn->lock); + g_cond_free (conn->flush_cond); g_free (conn->id); g_free (conn->server); g_free (conn); @@ -409,9 +423,11 @@ gst_jack_audio_client_new (const gchar * id, const gchar * server, if (conn == NULL) goto no_connection; + GST_INFO ("new client %s", id); + /* make new client using the connection */ client = g_new (GstJackAudioClient, 1); - client->active = FALSE; + client->active = client->deactivate = FALSE; client->conn = conn; client->type = type; client->shutdown = shutdown; @@ -446,6 +462,8 @@ gst_jack_audio_client_free (GstJackAudioClient * client) g_return_if_fail (client != NULL); + GST_INFO ("free client"); + conn = client->conn; /* remove from connection first so that it's not scheduled anymore after this @@ -492,6 +510,14 @@ gst_jack_audio_client_set_active (GstJackAudioClient * client, gboolean active) /* make sure that we are not dispatching the client */ g_mutex_lock (client->conn->lock); + if (client->active && !active) { + /* we need to process once more to flush the port */ + client->deactivate = TRUE; + + /* need to wait for process_cb run once more */ + while (client->deactivate) + g_cond_wait (client->conn->flush_cond, client->conn->lock); + } client->active = active; g_mutex_unlock (client->conn->lock); diff --git a/ext/jack/gstjackaudiosink.c b/ext/jack/gstjackaudiosink.c index 01383cbb..58a699d0 100644 --- a/ext/jack/gstjackaudiosink.c +++ b/ext/jack/gstjackaudiosink.c @@ -213,8 +213,8 @@ jack_process_cb (jack_nframes_t nframes, void *arg) if (nframes * sizeof (sample_t) != flen) goto wrong_size; - GST_DEBUG ("copy %d frames: %p, %d bytes, %d channels", nframes, readptr, - flen, channels); + GST_DEBUG_OBJECT (sink, "copy %d frames: %p, %d bytes, %d channels", + nframes, readptr, flen, channels); data = (sample_t *) readptr; /* the samples in the ringbuffer have the channels interleaved, we need to @@ -231,6 +231,7 @@ jack_process_cb (jack_nframes_t nframes, void *arg) /* we wrote one segment */ gst_ring_buffer_advance (buf, 1); } else { + GST_DEBUG_OBJECT (sink, "write %d frames silence", nframes); /* We are not allowed to read from the ringbuffer, write silence to all * jack output buffers */ for (i = 0; i < channels; i++) { @@ -607,7 +608,7 @@ gst_jack_ring_buffer_delay (GstRingBuffer * buf) res = latency; } - GST_DEBUG_OBJECT (sink, "delay %u", res); + GST_LOG_OBJECT (sink, "delay %u", res); return res; } diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c index 5ab26107..d9aefe53 100644 --- a/ext/resindvd/gstmpegdemux.c +++ b/ext/resindvd/gstmpegdemux.c @@ -356,7 +356,7 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type) { GstFluPSStream *stream = demux->streams[id]; - if (stream == NULL) { + if (stream == NULL && !demux->disable_stream_creation) { if (!(stream = gst_flups_demux_create_stream (demux, id, type))) goto unknown_stream; @@ -612,6 +612,8 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) GST_DEBUG_OBJECT (demux, "Handling language codes event"); + demux->disable_stream_creation = FALSE; + /* Create a video pad to ensure it exists before emitting no more pads */ temp = gst_flups_demux_get_stream (demux, 0xe0, ST_VIDEO_MPEG2); /* Send a video format event downstream */ @@ -719,6 +721,8 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) ST_PS_DVD_SUBPICTURE); } + demux->disable_stream_creation = TRUE; + GST_DEBUG_OBJECT (demux, "Created all pads from Language Codes event, " "signalling no-more-pads"); @@ -767,32 +771,33 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) stream_id += 0x80; temp = demux->streams[stream_id]; break; -#if 0 /* FIXME: Ignore non AC-3 requests until the decoder bin can handle them */ case 0x2: case 0x3: /* MPEG audio without and with extension stream are * treated the same */ - stream_id = 0xC0 + i; + stream_id += 0xC0; temp = demux->streams[stream_id]; break; case 0x4: /* LPCM */ - stream_id = 0xA0 + i; + stream_id += 0xA0; temp = demux->streams[stream_id]; break; case 0x6: /* DTS */ - stream_id = 0x88 + i; + stream_id += 0x88; temp = demux->streams[stream_id]; break; case 0x7: /* FIXME: What range is SDDS? */ + temp = NULL; break; -#endif default: temp = NULL; break; } + GST_INFO_OBJECT (demux, "Have DVD audio stream select event: " + "stream 0x%02x", stream_id); if (temp != NULL && temp->pad != NULL) { /* Send event to the selector to activate the desired pad */ GstStructure *s = gst_structure_new ("application/x-gst-dvd", diff --git a/ext/resindvd/gstmpegdemux.h b/ext/resindvd/gstmpegdemux.h index 683b7cde..f6291500 100644 --- a/ext/resindvd/gstmpegdemux.h +++ b/ext/resindvd/gstmpegdemux.h @@ -106,6 +106,7 @@ struct _GstFluPSDemux { /* Indicates an MPEG-2 stream */ gboolean is_mpeg2_pack; + gboolean disable_stream_creation; /* Language codes event is stored when a dvd-lang-codes * custom event arrives from upstream */ diff --git a/ext/resindvd/resindvdsrc.c b/ext/resindvd/resindvdsrc.c index f24e45aa..122f2b67 100644 --- a/ext/resindvd/resindvdsrc.c +++ b/ext/resindvd/resindvdsrc.c @@ -1882,7 +1882,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) GST_DEBUG_OBJECT (src, "mapped logical audio %d to MPEG substream %d", i, phys_id); -#if 1 +#if 0 /* FIXME: Only output A52 streams for now, until the decoder switching * is ready */ if (a->audio_format != 0) { @@ -1891,7 +1891,8 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) continue; } #endif - have_audio = TRUE; + if (a->audio_format == 0) + have_audio = TRUE; GST_DEBUG_OBJECT (src, "Audio stream %d is format %d, substream %d", i, (int) a->audio_format, phys_id); @@ -1917,7 +1918,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) } if (have_audio == FALSE) { - /* Always create at least one audio stream */ + /* Always create at least one audio stream of the required type */ gst_structure_set (s, "audio-0-format", G_TYPE_INT, (int) 0, "audio-0-stream", G_TYPE_INT, (int) 0, NULL); } diff --git a/ext/resindvd/rsnaudiomunge.c b/ext/resindvd/rsnaudiomunge.c index 50d33be7..fda2a96b 100644 --- a/ext/resindvd/rsnaudiomunge.c +++ b/ext/resindvd/rsnaudiomunge.c @@ -200,10 +200,13 @@ rsn_audiomunge_make_audio (RsnAudioMunge * munge, guint buf_size; /* Just generate a 48khz stereo buffer for now */ + /* FIXME: Adapt to the allowed formats, according to the currently + * plugged decoder, or at least add a source pad that accepts the + * caps we're outputting if the upstream decoder does not */ #if 0 caps = gst_caps_from_string - ("audio/x-raw-int,rate=48000,channels=2,width=16,depth=16,signed=(boolean)true,endianness=1234"); + ("audio/x-raw-int,rate=48000,channels=2,width=16,depth=16,signed=(boolean)true,endianness=4321"); buf_size = 4 * (48000 * fill_time / GST_SECOND); #else caps = gst_caps_from_string ("audio/x-raw-float, endianness=(int)1234," diff --git a/gst/aacparse/gstbaseparse.c b/gst/aacparse/gstbaseparse.c index 3224f445..34d28ec2 100644 --- a/gst/aacparse/gstbaseparse.c +++ b/gst/aacparse/gstbaseparse.c @@ -290,7 +290,7 @@ gst_base_parse_base_init (gpointer g_class) GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class); GstBaseParseClassPrivate *priv; - GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "flacbaseparse", 0, + GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "aacbaseparse", 0, "baseparse element"); /* TODO: Remove this once GObject supports class private data */ diff --git a/gst/amrparse/gstamrparse.c b/gst/amrparse/gstamrparse.c index aeb928e1..5ec0c6c2 100644 --- a/gst/amrparse/gstamrparse.c +++ b/gst/amrparse/gstamrparse.c @@ -309,12 +309,12 @@ gst_amrparse_parse_header (GstAmrParse * amrparse, GST_DEBUG_OBJECT (amrparse, "AMR-WB detected"); amrparse->block_size = block_size_wb; amrparse->wide = TRUE; - *skipsize = 9; + *skipsize = amrparse->header = 9; } else if (!memcmp (data, "#!AMR\n", 6)) { GST_DEBUG_OBJECT (amrparse, "AMR-NB detected"); amrparse->block_size = block_size_nb; amrparse->wide = FALSE; - *skipsize = 6; + *skipsize = amrparse->header = 6; } else return FALSE; @@ -453,6 +453,7 @@ gst_amrparse_start (GstBaseParse * parse) amrparse = GST_AMRPARSE (parse); GST_DEBUG ("start"); amrparse->need_header = TRUE; + amrparse->header = 0; amrparse->sync = TRUE; amrparse->eos = FALSE; amrparse->framecount = 0; @@ -478,6 +479,7 @@ gst_amrparse_stop (GstBaseParse * parse) amrparse = GST_AMRPARSE (parse); GST_DEBUG ("stop"); amrparse->need_header = TRUE; + amrparse->header = 0; amrparse->ts = -1; return TRUE; } @@ -548,7 +550,7 @@ gst_amrparse_convert (GstBaseParse * parse, GST_DEBUG ("converting bytes -> time"); if (amrparse->framecount) { - *dest_value = AMR_FRAME_DURATION * src_value / bpf; + *dest_value = AMR_FRAME_DURATION * (src_value - amrparse->header) / bpf; GST_DEBUG ("conversion result: %lld ms", *dest_value / GST_MSECOND); ret = TRUE; } @@ -557,7 +559,7 @@ gst_amrparse_convert (GstBaseParse * parse, GST_DEBUG ("converting time -> bytes"); if (dest_format == GST_FORMAT_BYTES) { if (amrparse->framecount) { - *dest_value = bpf * src_value / AMR_FRAME_DURATION; + *dest_value = bpf * src_value / AMR_FRAME_DURATION + amrparse->header; GST_DEBUG ("time %lld ms in bytes = %lld", src_value / GST_MSECOND, *dest_value); ret = TRUE; diff --git a/gst/amrparse/gstamrparse.h b/gst/amrparse/gstamrparse.h index e776f295..4cd432e2 100644 --- a/gst/amrparse/gstamrparse.h +++ b/gst/amrparse/gstamrparse.h @@ -61,6 +61,7 @@ struct _GstAmrParse { GstBaseParse element; const gint *block_size; gboolean need_header; + gint header; gboolean wide; gboolean eos; gboolean sync; diff --git a/gst/amrparse/gstbaseparse.c b/gst/amrparse/gstbaseparse.c index 7483c2b9..e0f1f4d7 100644 --- a/gst/amrparse/gstbaseparse.c +++ b/gst/amrparse/gstbaseparse.c @@ -290,7 +290,7 @@ gst_base_parse_base_init (gpointer g_class) GstBaseParseClass *klass = GST_BASE_PARSE_CLASS (g_class); GstBaseParseClassPrivate *priv; - GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "flacbaseparse", 0, + GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "amrbaseparse", 0, "baseparse element"); /* TODO: Remove this once GObject supports class private data */ diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c index d1d4de18..37d80236 100644 --- a/gst/dvdspu/gstspu-pgs.c +++ b/gst/dvdspu/gstspu-pgs.c @@ -134,6 +134,9 @@ dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len) pal_id = data[2]; data += 3; break; + default: + run_len = 0; + break; } } @@ -252,6 +255,9 @@ pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state, pal_id = data[2]; data += 3; break; + default: + run_len = 0; + break; } } @@ -430,12 +436,13 @@ parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED | PGS_COMPOSITION_OBJECT_FLAG_FORCED)) - g_warning ("PGS Composition Object has unknown flags: 0x%02x", + GST_ERROR ("PGS Composition Object has unknown flags: 0x%02x", obj->flags); } if (payload != end) { - g_warning ("PGS Composition Object: %ld bytes not consumed", end - payload); + GST_ERROR ("PGS Composition Object: %" G_GSSIZE_FORMAT + " bytes not consumed", (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -495,7 +502,8 @@ parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, #endif if (payload != end) { - g_warning ("PGS Set Palette: %ld bytes not consumed", end - payload); + GST_ERROR ("PGS Set Palette: %" G_GSSIZE_FORMAT " bytes not consumed", + (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -529,7 +537,8 @@ parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, state->pgs.win_h); if (payload != end) { - g_warning ("PGS Set Window: %ld bytes not consumed", end - payload); + GST_ERROR ("PGS Set Window: %" G_GSSIZE_FORMAT " bytes not consumed", + (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -590,7 +599,8 @@ parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size); if (payload != end) { - g_warning ("PGS Set Object Data: %ld bytes not consumed", end - payload); + GST_ERROR ("PGS Set Object Data: %" G_GSSIZE_FORMAT " bytes not consumed", + (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -642,7 +652,7 @@ parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, pgs_state->in_presentation_segment = FALSE; break; default: - g_warning ("Unknown PGS command: type 0x%02x len %u", type, len); + GST_ERROR ("Unknown PGS command: type 0x%02x len %u", type, len); dump_bytes (payload, len); break; } diff --git a/gst/dvdspu/gstspu-vobsub-render.c b/gst/dvdspu/gstspu-vobsub-render.c index 07abff22..3709d82a 100644 --- a/gst/dvdspu/gstspu-vobsub-render.c +++ b/gst/dvdspu/gstspu-vobsub-render.c @@ -343,7 +343,7 @@ gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3]) gstspu_blend_comp_buffers (state, planes); } -void +static void gstspu_vobsub_clear_comp_buffers (SpuState * state) { state->comp_left = state->vobsub.disp_rect.left; @@ -452,7 +452,7 @@ gstspu_vobsub_render (GstDVDSpu * dvdspu, GstBuffer * buf) || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom); /* Reset the compositing buffer */ - gstspu_clear_comp_buffers (state); + gstspu_vobsub_clear_comp_buffers (state); /* Render even line */ state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]); @@ -480,7 +480,7 @@ gstspu_vobsub_render (GstDVDSpu * dvdspu, GstBuffer * buf) /* Render a remaining lone last even line. y already has the correct value * after the above loop exited. */ - gstspu_clear_comp_buffers (state); + gstspu_vobsub_clear_comp_buffers (state); state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x; gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]); gstspu_vobsub_blend_comp_buffers (state, planes); diff --git a/gst/hdvparse/Makefile.am b/gst/hdvparse/Makefile.am new file mode 100644 index 00000000..d7eb4d28 --- /dev/null +++ b/gst/hdvparse/Makefile.am @@ -0,0 +1,13 @@ +plugin_LTLIBRARIES = libgsthdvparse.la + +libgsthdvparse_la_SOURCES = \ + gsthdvparse.c + +noinst_HEADERS = \ + gsthdvparse.h + +libgsthdvparse_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) +libgsthdvparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) +libgsthdvparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgsthdvparse_la_LIBTOOLFLAGS = --tag=disable-static + diff --git a/gst/hdvparse/gsthdvparse.c b/gst/hdvparse/gsthdvparse.c new file mode 100644 index 00000000..9914cfa2 --- /dev/null +++ b/gst/hdvparse/gsthdvparse.c @@ -0,0 +1,362 @@ +/* + * GStreamer + * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com> + * + * 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. + */ + +/** + * SECTION:element-HDVParse + * + * <refsect2> + * <title>Example launch line</title> + * <para> + * <programlisting> + * gst-launch -v -m filesrc ! mpegtsdemux ! hdvparse ! fakesink silent=TRUE + * </programlisting> + * </para> + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstbasetransform.h> + +#include "gsthdvparse.h" + +GST_DEBUG_CATEGORY_STATIC (gst_hdvparse_debug); +#define GST_CAT_DEFAULT gst_hdvparse_debug + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, +}; + +static gchar *aperture_table[] = { + "???", + "cls", + "1.0", + "1.2", + "1.4", + "1.6", + "1.7", + "1.8", + "2.0", + "2.2", + "2.4", + "2.6", + "2.8", + "3.1", + "3.4", + "3.7", + "4.0", + "4.4", + "4.8", + "5.2", + "5.6", + "6.2", + "6.8", + "7.3", + "8.0", + "8.7", + "9.6", + "10", + "11", + "12", + "14", + "14", + "16", + "17", + "18", + "6.7" +}; + +/* Observations from my HDV Camera (Canon HV20 Pal) + * FIXME : replace with with code once we've figured out the algorithm. + * Shutter speed 0x4f 0x50 + * ------------------------------------ + * 1/6 F3 95 + * 1/8 90 91 + * 1/12 FA 8A + * 1/15 C8 88 + * 1/24 7D 85 + * 1/30 64 84 + * 1/48 BE 82 + * 1/60 32 82 + * 1/100 51 81 + * 1/250 87 80 + * 1/500 43 80 + * 1/1000 22 80 + * 1/2000 11 80 + */ +typedef struct +{ + guint vala, valb, shutter; +} Shutter_t; + +static Shutter_t shutter_table[] = { + {0xf3, 0x95, 6}, + {0x90, 0x91, 8}, + {0xfa, 0x8a, 12}, + {0xc8, 0x88, 15}, + {0x7d, 0x85, 24}, + {0x64, 0x84, 30}, + {0xbe, 0x82, 48}, + {0x32, 0x82, 60}, + {0x51, 0x81, 100}, + {0x87, 0x80, 250}, + {0x43, 0x80, 500}, + {0x22, 0x80, 1000}, + {0x11, 0x80, 2000} +}; + +/* Binary-coded decimal reading macro */ +#define BCD(c) ( ((((c) >> 4) & 0x0f) * 10) + ((c) & 0x0f) ) +/* Same as before, but with a mask */ +#define BCD_M(c, mask) (BCD ((c) & (mask))) + + +/* the capabilities of the inputs and outputs. + * + * describe the real formats here. + */ +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("private/hdv-a1") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("private/hdv-a1,parsed=(boolean)True") + ); + +/* debug category for fltering log messages + * + * exchange the string 'Template HDVParse' with your description + */ +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_hdvparse_debug, "hdvparse", 0, "HDV private stream parser"); + +GST_BOILERPLATE_FULL (GstHDVParse, gst_hdvparse, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static GstFlowReturn gst_hdvparse_transform_ip (GstBaseTransform * base, + GstBuffer * outbuf); + +/* GObject vmethod implementations */ + +static void +gst_hdvparse_base_init (gpointer klass) +{ + static GstElementDetails element_details = { + "HDVParser", + "Data/Parser", + "HDV private stream Parser", + "Edward Hervey <bilboed@bilboed.com>" + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + gst_element_class_set_details (element_class, &element_details); +} + +/* initialize the HDVParse's class */ +static void +gst_hdvparse_class_init (GstHDVParseClass * klass) +{ + GST_BASE_TRANSFORM_CLASS (klass)->transform_ip = + GST_DEBUG_FUNCPTR (gst_hdvparse_transform_ip); +} + +/* initialize the new element + * initialize instance structure + */ +static void +gst_hdvparse_init (GstHDVParse * filter, GstHDVParseClass * klass) +{ + GstBaseTransform *transform = GST_BASE_TRANSFORM (filter); + + gst_base_transform_set_in_place (transform, TRUE); + gst_base_transform_set_passthrough (transform, TRUE); +} + +static guint +get_shutter_speed (guint8 vala, guint8 valb) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (shutter_table); i++) + if (shutter_table[i].vala == vala && shutter_table[i].valb == valb) + return shutter_table[i].shutter; + GST_WARNING ("Unknown shutter speed ! vala:0x%02x, valb:0x%02x", vala, valb); + return 0; +} + +static void +gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf) +{ + guint8 *data = GST_BUFFER_DATA (buf); + guint apertured, shutter; + gfloat gain; + gboolean dst = FALSE; + GstStructure *str; + GstMessage *msg; + + GST_MEMDUMP_OBJECT (filter, "BUFFER", data, GST_BUFFER_SIZE (buf)); + + str = gst_structure_empty_new ("HDV"); + + /* 0x1f - 0x23 : TimeCode */ + + if (data[0x1f] != 0xff) { + guint8 tframe, tsec, tmin, thour; + gchar *timecode = NULL; + tframe = BCD (data[0x1f] & 0x3f); + tsec = BCD (data[0x20] & 0x7f); + tmin = BCD (data[0x21] & 0x7f); + thour = BCD (data[0x22] & 0x3f); + + timecode = + g_strdup_printf ("%01d:%02d:%02d.%02d", thour, tmin, tsec, tframe); + gst_structure_set (str, "timecode", G_TYPE_STRING, timecode, NULL); + g_free (timecode); + GST_LOG_OBJECT (filter, timecode); + } + + /* 0x23 : Timezone / Dailight Saving Time */ + /* 0x24 - 0x2a : Original time */ + if (data[0x23] != 0xff) { + GDate *date = NULL; + guint tzone = 0; + guint day, month, year, hour, min, sec; + gchar *datetime; + + tzone = data[0x23]; + dst = !(tzone & 0x80); + tzone = + BCD (tzone & 0x1f) > 12 ? BCD (tzone & 0x1f) - 12 : BCD (tzone & 0x1f); + GST_LOG_OBJECT (filter, "TimeZone : %d, DST : %d", tzone, dst); + + day = BCD_M (data[0x24], 0x3f); + month = BCD_M (data[0x25], 0x1f); + year = BCD (data[0x26]); + if (year > 90) + year += 1900; + else + year += 2000; + /* 0x27: ??? */ + sec = BCD_M (data[0x28], 0x7f); + min = BCD_M (data[0x29], 0x7f); + hour = BCD_M (data[0x2a], 0x3f); + + /* FIXME : we need a date/time object ! */ + date = g_date_new_dmy (day, month, year); + datetime = + g_strdup_printf ("%02d/%02d/%02d %02d:%02d:%02d", day, month, year, + hour, min, sec); + gst_structure_set (str, "date", GST_TYPE_DATE, date, "recording-time", + G_TYPE_STRING, datetime, NULL); + g_free (datetime); + GST_LOG_OBJECT (filter, datetime); + } + + /* 0x2b : Various flags, including scene-change */ + if (!((data[0x2b] & 0x20) >> 5)) { + GST_LOG_OBJECT (filter, "Scene change !"); + gst_structure_set (str, "scene-change", G_TYPE_BOOLEAN, TRUE, NULL); + } + + /* Check for partials */ + if (GST_BUFFER_SIZE (buf) < 0x50) { + goto beach; + } + + /* 0x43 : Aperture */ + apertured = data[0x43] & 0x3f; + if (apertured < 35) { + GST_LOG_OBJECT (filter, "Aperture : F%s", aperture_table[apertured]); + gst_structure_set (str, "aperture", G_TYPE_STRING, + aperture_table[apertured], NULL); + } else { + GST_LOG_OBJECT (filter, "Aperture : %d", apertured); + } + + /* 0x44 : Gain */ + gain = ((data[0x44] & 0xf) - 1) * 1.5; + GST_LOG_OBJECT (filter, "Gain : %03f db", gain); + gst_structure_set (str, "gain", G_TYPE_FLOAT, gain, NULL); + + /* 0x4f - 0x50 : Shutter */ + shutter = get_shutter_speed (data[0x4f], data[0x50]); + GST_LOG_OBJECT (filter, "Shutter speed : 1/%d", shutter); + if (shutter) + gst_structure_set (str, "shutter-speed", GST_TYPE_FRACTION, 1, shutter, + NULL); + +beach: + msg = gst_message_new_element (GST_OBJECT (filter), str); + gst_element_post_message (GST_ELEMENT (filter), msg); + return; +} + +/* GstBaseTransform vmethod implementations */ + +static GstFlowReturn +gst_hdvparse_transform_ip (GstBaseTransform * base, GstBuffer * outbuf) +{ + GstHDVParse *filter = GST_HDVPARSE (base); + + gst_hdvparse_parse (filter, outbuf); + + return GST_FLOW_OK; +} + + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +static gboolean +HDVParse_init (GstPlugin * HDVParse) +{ + return gst_element_register (HDVParse, "hdvparse", GST_RANK_NONE, + GST_TYPE_HDVPARSE); +} + +/* gstreamer looks for this structure to register HDVParses + * + * exchange the string 'Template HDVParse' with you HDVParse description + */ +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "hdvparse", + "HDV private stream parser", + HDVParse_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") diff --git a/gst/hdvparse/gsthdvparse.h b/gst/hdvparse/gsthdvparse.h new file mode 100644 index 00000000..824634f6 --- /dev/null +++ b/gst/hdvparse/gsthdvparse.h @@ -0,0 +1,56 @@ +/* + * GStreamer + * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com> + * + * 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_HDVPARSE_H__ +#define __GST_HDVPARSE_H__ + +#include <gst/gst.h> +#include <gst/base/gstbasetransform.h> + +G_BEGIN_DECLS + +#define GST_TYPE_HDVPARSE \ + (gst_hdvparse_get_type()) +#define GST_HDVPARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HDVPARSE,GstHDVParse)) +#define GST_HDVPARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HDVPARSE,GstHDVParseClass)) +#define GST_IS_HDVPARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HDVPARSE)) +#define GST_IS_HDVPARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HDVPARSE)) + +typedef struct _GstHDVParse GstHDVParse; +typedef struct _GstHDVParseClass GstHDVParseClass; + +struct _GstHDVParse { + GstBaseTransform element; + +}; + +struct _GstHDVParseClass { + GstBaseTransformClass parent_class; +}; + +GType gst_hdvparse_get_type (void); + +G_END_DECLS + +#endif /* __GST_HDVPARSE_H__ */ diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index ef0de2c8..b75bfc6d 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -822,6 +822,39 @@ no_pcr_stream: } } +static void +gst_mpegts_demux_send_tags_for_stream (GstMpegTSDemux * demux, + GstMpegTSStream * stream) +{ + GstTagList *list = NULL; + + if (stream->ES_info) { + guint8 *iso639_languages = + gst_mpeg_descriptor_find (stream->ES_info, DESC_ISO_639_LANGUAGE); + if (iso639_languages) { + if (DESC_ISO_639_LANGUAGE_codes_n (iso639_languages)) { + gchar lang_code[4]; + gchar *language_n = (gchar *) + DESC_ISO_639_LANGUAGE_language_code_nth (iso639_languages, 0); + lang_code[0] = language_n[0]; + lang_code[1] = language_n[1]; + lang_code[2] = language_n[2]; + lang_code[3] = 0; + if (!list) + list = gst_tag_list_new (); + gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, + GST_TAG_LANGUAGE_CODE, lang_code, NULL); + } + } + } + + if (list) { + GST_DEBUG_OBJECT (demux, "Sending tags %p for pad %s:%s", + list, GST_DEBUG_PAD_NAME (stream->pad)); + gst_element_found_tags_for_pad (GST_ELEMENT (demux), stream->pad, list); + } +} + #ifndef GST_FLOW_IS_SUCCESS #define GST_FLOW_IS_SUCCESS(ret) ((ret) >= GST_FLOW_OK) #endif @@ -890,8 +923,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, * to drop. */ if (stream->PMT_pid <= MPEGTS_MAX_PID && demux->streams[stream->PMT_pid] && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID] - && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]-> - discont_PCR) { + && demux->streams[demux->streams[stream->PMT_pid]->PMT. + PCR_PID]->discont_PCR) { GST_WARNING_OBJECT (demux, "middle of discont, dropping"); goto bad_timestamp; } @@ -913,8 +946,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, */ if (stream->PMT_pid <= MPEGTS_MAX_PID && demux->streams[stream->PMT_pid] && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID] - && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]-> - last_PCR > 0) { + && demux->streams[demux->streams[stream->PMT_pid]->PMT. + PCR_PID]->last_PCR > 0) { GST_DEBUG_OBJECT (demux, "timestamps wrapped before noticed in PCR"); time = MPEGTIME_TO_GSTTIME (pts) + stream->base_time + MPEGTIME_TO_GSTTIME ((guint64) (1) << 33); @@ -1026,6 +1059,9 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first, /* send new_segment */ gst_mpegts_demux_send_new_segment (demux, stream, pts); + + /* send tags */ + gst_mpegts_demux_send_tags_for_stream (demux, stream); } GST_DEBUG_OBJECT (srcpad, "pushing buffer"); diff --git a/gst/qtmux/fourcc.h b/gst/qtmux/fourcc.h index 0fa7aa9b..3db60036 100644 --- a/gst/qtmux/fourcc.h +++ b/gst/qtmux/fourcc.h @@ -140,6 +140,7 @@ G_BEGIN_DECLS #define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s') #define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') #define FOURCC_h263 GST_MAKE_FOURCC('h','2','6','3') +#define FOURCC_s263 GST_MAKE_FOURCC('s','2','6','3') #define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C') #define FOURCC_VP31 GST_MAKE_FOURCC('V','P','3','1') #define FOURCC_rle_ GST_MAKE_FOURCC('r','l','e',' ') diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c index b0df9d71..03b0a1a6 100644 --- a/gst/qtmux/gstqtmux.c +++ b/gst/qtmux/gstqtmux.c @@ -1507,7 +1507,10 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps) break; } } else if (strcmp (mimetype, "video/x-h263") == 0) { - entry.fourcc = FOURCC_h263; + if (format == GST_QT_MUX_FORMAT_QT) + entry.fourcc = FOURCC_h263; + else + entry.fourcc = FOURCC_s263; ext_atom = build_h263_extension (); } else if (strcmp (mimetype, "video/x-divx") == 0 || strcmp (mimetype, "video/mpeg") == 0) { diff --git a/gst/rawparse/gstaudioparse.c b/gst/rawparse/gstaudioparse.c index def9803e..f7eb6651 100644 --- a/gst/rawparse/gstaudioparse.c +++ b/gst/rawparse/gstaudioparse.c @@ -33,7 +33,9 @@ typedef enum { GST_AUDIO_PARSE_FORMAT_INT, - GST_AUDIO_PARSE_FORMAT_FLOAT + GST_AUDIO_PARSE_FORMAT_FLOAT, + GST_AUDIO_PARSE_FORMAT_MULAW, + GST_AUDIO_PARSE_FORMAT_ALAW } GstAudioParseFormat; typedef enum @@ -69,7 +71,7 @@ enum ARG_ENDIANNESS, ARG_WIDTH, ARG_DEPTH, - ARG_SIGNED, + ARG_SIGNED }; @@ -81,6 +83,8 @@ gst_audio_parse_format_get_type (void) static const GEnumValue format_types[] = { {GST_AUDIO_PARSE_FORMAT_INT, "Integer", "int"}, {GST_AUDIO_PARSE_FORMAT_FLOAT, "Floating Point", "float"}, + {GST_AUDIO_PARSE_FORMAT_ALAW, "A-Law", "alaw"}, + {GST_AUDIO_PARSE_FORMAT_MULAW, "\302\265-Law", "mulaw"}, {0, NULL, NULL} }; @@ -137,7 +141,9 @@ gst_audio_parse_base_init (gpointer g_class) "audio/x-raw-float," " width=(int) { 32, 64 }," " endianness=(int) { LITTLE_ENDIAN, BIG_ENDIAN }, " - " rate=(int)[1,MAX]," " channels=(int)[1,MAX]"); + " rate=(int)[1,MAX], channels=(int)[1,MAX]; " + "audio/x-alaw, rate=(int)[1,MAX], channels=(int)[1,MAX]; " + "audio/x-mulaw, rate=(int)[1,MAX], channels=(int)[1,MAX]"); gst_raw_parse_class_set_src_pad_template (rp_class, caps); gst_raw_parse_class_set_multiple_frames_per_buffer (rp_class, TRUE); @@ -282,9 +288,21 @@ gst_audio_parse_get_property (GObject * object, guint prop_id, GValue * value, void gst_audio_parse_update_frame_size (GstAudioParse * ap) { - gint framesize; + gint framesize, width; - framesize = (ap->width / 8) * ap->channels; + switch (ap->format) { + case GST_AUDIO_PARSE_FORMAT_ALAW: + case GST_AUDIO_PARSE_FORMAT_MULAW: + width = 8; + break; + case GST_AUDIO_PARSE_FORMAT_INT: + case GST_AUDIO_PARSE_FORMAT_FLOAT: + default: + width = ap->width; + break; + } + + framesize = (width / 8) * ap->channels; gst_raw_parse_set_framesize (GST_RAW_PARSE (ap), framesize); } @@ -299,20 +317,37 @@ gst_audio_parse_get_caps (GstRawParse * rp) gst_raw_parse_get_fps (rp, &fps_n, &fps_d); - if (ap->format == GST_AUDIO_PARSE_FORMAT_INT) { - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, fps_n, - "channels", G_TYPE_INT, ap->channels, - "width", G_TYPE_INT, ap->width, - "depth", G_TYPE_INT, ap->depth, - "signed", G_TYPE_BOOLEAN, ap->signedness, - "endianness", G_TYPE_INT, ap->endianness, NULL); - } else { - caps = gst_caps_new_simple ("audio/x-raw-float", - "rate", G_TYPE_INT, fps_n, - "channels", G_TYPE_INT, ap->channels, - "width", G_TYPE_INT, ap->width, - "endianness", G_TYPE_INT, ap->endianness, NULL); + switch (ap->format) { + case GST_AUDIO_PARSE_FORMAT_INT: + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, fps_n, + "channels", G_TYPE_INT, ap->channels, + "width", G_TYPE_INT, ap->width, + "depth", G_TYPE_INT, ap->depth, + "signed", G_TYPE_BOOLEAN, ap->signedness, + "endianness", G_TYPE_INT, ap->endianness, NULL); + break; + case GST_AUDIO_PARSE_FORMAT_FLOAT: + caps = gst_caps_new_simple ("audio/x-raw-float", + "rate", G_TYPE_INT, fps_n, + "channels", G_TYPE_INT, ap->channels, + "width", G_TYPE_INT, ap->width, + "endianness", G_TYPE_INT, ap->endianness, NULL); + break; + case GST_AUDIO_PARSE_FORMAT_ALAW: + caps = gst_caps_new_simple ("audio/x-alaw", + "rate", G_TYPE_INT, fps_n, + "channels", G_TYPE_INT, ap->channels, NULL); + break; + case GST_AUDIO_PARSE_FORMAT_MULAW: + caps = gst_caps_new_simple ("audio/x-mulaw", + "rate", G_TYPE_INT, fps_n, + "channels", G_TYPE_INT, ap->channels, NULL); + break; + default: + caps = gst_caps_new_empty (); + GST_ERROR_OBJECT (rp, "unexpected format %d", ap->format); + break; } return caps; } diff --git a/gst/rtpmanager/gstrtpbin.c b/gst/rtpmanager/gstrtpbin.c index 19de4f1b..482cf017 100644 --- a/gst/rtpmanager/gstrtpbin.c +++ b/gst/rtpmanager/gstrtpbin.c @@ -1758,7 +1758,7 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) const GstStructure *s = gst_message_get_structure (message); /* we change the structure name and add the session ID to it */ - if (gst_structure_has_name (s, "GstRTPSessionSDES")) { + if (gst_structure_has_name (s, "application/x-rtp-source-sdes")) { GSList *walk; /* find the session, the message source has it */ @@ -1771,8 +1771,6 @@ gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message) message = gst_message_make_writable (message); s = gst_message_get_structure (message); - gst_structure_set_name ((GstStructure *) s, "GstRTPBinSDES"); - gst_structure_set ((GstStructure *) s, "session", G_TYPE_UINT, sess->id, NULL); break; diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 355526ee..ed080717 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -199,6 +199,7 @@ make_address_string (GstNetAddress * addr, gchar * dest, gulong n) guint16 port; gst_netaddress_get_ip4_address (addr, &address, &port); + address = g_ntohl (address); g_snprintf (dest, n, "%d.%d.%d.%d:%d", (address >> 24) & 0xff, (address >> 16) & 0xff, (address >> 8) & 0xff, address & 0xff, @@ -321,7 +322,8 @@ rtp_source_create_sdes (RTPSource * src) GstStructure *s; gchar *str; - s = gst_structure_new ("application/x-rtp-source-sdes", NULL); + s = gst_structure_new ("application/x-rtp-source-sdes", + "ssrc", G_TYPE_UINT, (guint) src->ssrc, NULL); if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME))) { gst_structure_set (s, "cname", G_TYPE_STRING, str, NULL); diff --git a/gst/sdp/gstsdpdemux.c b/gst/sdp/gstsdpdemux.c index 4deac870..3c543d0a 100644 --- a/gst/sdp/gstsdpdemux.c +++ b/gst/sdp/gstsdpdemux.c @@ -51,6 +51,9 @@ #include <unistd.h> #endif +/* include GLIB for G_OS_WIN32 */ +#include <glib.h> + #ifdef G_OS_WIN32 #ifdef _MSC_VER #include <Winsock2.h> diff --git a/gst/selector/gstoutputselector.c b/gst/selector/gstoutputselector.c index 32988af4..bf354a74 100644 --- a/gst/selector/gstoutputselector.c +++ b/gst/selector/gstoutputselector.c @@ -19,7 +19,7 @@ /** * SECTION:element-output-selector - * @see_also: #GstTee, #GstInputSelector + * @see_also: #GstOutputSelector, #GstInputSelector * * Direct input stream to one out of N output pads. */ @@ -74,6 +74,8 @@ static GstPad *gst_output_selector_request_new_pad (GstElement * element, static void gst_output_selector_release_pad (GstElement * element, GstPad * pad); static GstFlowReturn gst_output_selector_chain (GstPad * pad, GstBuffer * buf); +static GstFlowReturn gst_output_selector_buffer_alloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); static GstStateChangeReturn gst_output_selector_change_state (GstElement * element, GstStateChange transition); static gboolean gst_output_selector_handle_sink_event (GstPad * pad, @@ -136,17 +138,17 @@ gst_output_selector_init (GstOutputSelector * sel, GST_DEBUG_FUNCPTR (gst_output_selector_chain)); gst_pad_set_event_function (sel->sinkpad, GST_DEBUG_FUNCPTR (gst_output_selector_handle_sink_event)); - - gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad); - + gst_pad_set_bufferalloc_function (sel->sinkpad, + GST_DEBUG_FUNCPTR (gst_output_selector_buffer_alloc)); /* - gst_pad_set_bufferalloc_function (sel->sinkpad, - GST_DEBUG_FUNCPTR (gst_output_selector_bufferalloc)); gst_pad_set_setcaps_function (sel->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps)); gst_pad_set_getcaps_function (sel->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); */ + + gst_element_add_pad (GST_ELEMENT (sel), sel->sinkpad); + /* srcpad management */ sel->active_srcpad = NULL; sel->nb_srcpads = 0; @@ -251,6 +253,37 @@ gst_output_selector_get_property (GObject * object, guint prop_id, } } +static GstFlowReturn +gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstOutputSelector *sel; + GstFlowReturn res; + GstPad *allocpad; + + sel = GST_OUTPUT_SELECTOR (GST_PAD_PARENT (pad)); + + res = GST_FLOW_NOT_LINKED; + + GST_OBJECT_LOCK (sel); + if ((allocpad = sel->active_srcpad)) { + /* if we had a previous pad we used for allocating a buffer, continue using + * it. */ + GST_DEBUG_OBJECT (sel, "using pad %s:%s for alloc", + GST_DEBUG_PAD_NAME (allocpad)); + gst_object_ref (allocpad); + GST_OBJECT_UNLOCK (sel); + + res = gst_pad_alloc_buffer (allocpad, offset, size, caps, buf); + gst_object_unref (allocpad); + + GST_OBJECT_LOCK (sel); + } + GST_OBJECT_UNLOCK (sel); + + return res; +} + static GstPad * gst_output_selector_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name) diff --git a/gst/shapewipe/Makefile.am b/gst/shapewipe/Makefile.am new file mode 100644 index 00000000..7f6df372 --- /dev/null +++ b/gst/shapewipe/Makefile.am @@ -0,0 +1,11 @@ +plugin_LTLIBRARIES = libgstshapewipe.la + +libgstshapewipe_la_SOURCES = gstshapewipe.c + +libgstshapewipe_la_CFLAGS = $(GIO_CFLAGS) $(GST_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) +libgstshapewipe_la_LIBADD = $(GIO_LIBS) $(GST_LIBS) $(GST_CONTROLLER_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ +libgstshapewipe_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstshapewipe_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstshapewipe.h + diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c new file mode 100644 index 00000000..ec33f0a7 --- /dev/null +++ b/gst/shapewipe/gstshapewipe.c @@ -0,0 +1,847 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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 <string.h> + +#include <gst/gst.h> +#include <gst/controller/gstcontroller.h> + +#include "gstshapewipe.h" + +static void gst_shape_wipe_finalize (GObject * object); +static void gst_shape_wipe_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_shape_wipe_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void gst_shape_wipe_reset (GstShapeWipe * self); + +static GstStateChangeReturn gst_shape_wipe_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_shape_wipe_video_sink_chain (GstPad * pad, + GstBuffer * buffer); +static gboolean gst_shape_wipe_video_sink_event (GstPad * pad, + GstEvent * event); +static gboolean gst_shape_wipe_video_sink_setcaps (GstPad * pad, + GstCaps * caps); +static GstCaps *gst_shape_wipe_video_sink_getcaps (GstPad * pad); +static GstFlowReturn gst_shape_wipe_mask_sink_chain (GstPad * pad, + GstBuffer * buffer); +static gboolean gst_shape_wipe_mask_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_shape_wipe_mask_sink_setcaps (GstPad * pad, GstCaps * caps); +static GstCaps *gst_shape_wipe_mask_sink_getcaps (GstPad * pad); +static gboolean gst_shape_wipe_src_event (GstPad * pad, GstEvent * event); +static GstCaps *gst_shape_wipe_src_getcaps (GstPad * pad); + +enum +{ + PROP_0, + PROP_POSITION, + PROP_BORDER +}; + +static GstStaticPadTemplate video_sink_pad_template = +GST_STATIC_PAD_TEMPLATE ("video_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))); + +static GstStaticPadTemplate mask_sink_pad_template = + GST_STATIC_PAD_TEMPLATE ("mask_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-gray, " + "bpp = 8, " + "depth = 8, " + "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = 0/1 ; " + "video/x-raw-gray, " "bpp = 16, " "depth = 16, " + "endianness = BYTE_ORDER, " "width = " GST_VIDEO_SIZE_RANGE ", " + "height = " GST_VIDEO_SIZE_RANGE ", " "framerate = 0/1")); + +static GstStaticPadTemplate src_pad_template = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))); + +GST_DEBUG_CATEGORY_STATIC (gst_shape_wipe_debug); +#define GST_CAT_DEFAULT gst_shape_wipe_debug + +GST_BOILERPLATE (GstShapeWipe, gst_shape_wipe, GstElement, GST_TYPE_ELEMENT); + +static void +gst_shape_wipe_base_init (gpointer g_class) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (gstelement_class, + "Shape Wipe transition filter", + "Filter/Editor/Video", + "Adds a shape wipe transition to a video stream", + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&video_sink_pad_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&mask_sink_pad_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_pad_template)); +} + +static void +gst_shape_wipe_class_init (GstShapeWipeClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = gst_shape_wipe_finalize; + gobject_class->set_property = gst_shape_wipe_set_property; + gobject_class->get_property = gst_shape_wipe_get_property; + + g_object_class_install_property (gobject_class, PROP_POSITION, + g_param_spec_float ("position", "Position", "Position of the mask", + 0.0, 1.0, 0.0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); + g_object_class_install_property (gobject_class, PROP_BORDER, + g_param_spec_float ("border", "Border", "Border of the mask", + 0.0, 1.0, 0.0, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_shape_wipe_change_state); +} + +static void +gst_shape_wipe_init (GstShapeWipe * self, GstShapeWipeClass * g_class) +{ + self->video_sinkpad = + gst_pad_new_from_static_template (&video_sink_pad_template, "video_sink"); + gst_pad_set_chain_function (self->video_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_chain)); + gst_pad_set_event_function (self->video_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_event)); + gst_pad_set_setcaps_function (self->video_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_setcaps)); + gst_pad_set_getcaps_function (self->video_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_getcaps)); + gst_element_add_pad (GST_ELEMENT (self), self->video_sinkpad); + + self->mask_sinkpad = + gst_pad_new_from_static_template (&mask_sink_pad_template, "mask_sink"); + gst_pad_set_chain_function (self->mask_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_mask_sink_chain)); + gst_pad_set_event_function (self->mask_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_mask_sink_event)); + gst_pad_set_setcaps_function (self->mask_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_mask_sink_setcaps)); + gst_pad_set_getcaps_function (self->mask_sinkpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_mask_sink_getcaps)); + gst_element_add_pad (GST_ELEMENT (self), self->mask_sinkpad); + + self->srcpad = gst_pad_new_from_static_template (&src_pad_template, "src"); + gst_pad_set_event_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_src_event)); + gst_pad_set_getcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_shape_wipe_src_getcaps)); + gst_element_add_pad (GST_ELEMENT (self), self->srcpad); + + self->mask_mutex = g_mutex_new (); + self->mask_cond = g_cond_new (); + + gst_shape_wipe_reset (self); +} + +static void +gst_shape_wipe_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (object); + + switch (prop_id) { + case PROP_POSITION: + g_value_set_float (value, self->mask_position); + break; + case PROP_BORDER: + g_value_set_float (value, self->mask_border); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_shape_wipe_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (object); + + switch (prop_id) { + case PROP_POSITION: + self->mask_position = g_value_get_float (value); + break; + case PROP_BORDER: + self->mask_border = g_value_get_float (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_shape_wipe_finalize (GObject * object) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (object); + + gst_shape_wipe_reset (self); + + if (self->mask_cond) + g_cond_free (self->mask_cond); + self->mask_cond = NULL; + + if (self->mask_mutex) + g_mutex_free (self->mask_mutex); + self->mask_mutex = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_shape_wipe_reset (GstShapeWipe * self) +{ + if (self->mask) + gst_buffer_unref (self->mask); + self->mask = NULL; + + g_cond_signal (self->mask_cond); + + self->width = self->height = 0; + self->mask_position = 0.0; + self->mask_border = 0.0; + self->mask_bpp = 0; + + gst_segment_init (&self->segment, GST_FORMAT_TIME); +} + +static gboolean +gst_shape_wipe_video_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + GstStructure *s; + gint width, height; + + GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "height", &height)) { + ret = FALSE; + goto done; + } + + if (self->width != width || self->height != height) { + g_mutex_lock (self->mask_mutex); + self->width = width; + self->height = height; + + if (self->mask) + gst_buffer_unref (self->mask); + self->mask = NULL; + g_mutex_unlock (self->mask_mutex); + } + + ret = gst_pad_set_caps (self->srcpad, caps); + +done: + gst_object_unref (self); + + return ret; +} + +static GstCaps * +gst_shape_wipe_video_sink_getcaps (GstPad * pad) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + GstCaps *ret, *tmp; + + if (GST_PAD_CAPS (pad)) + return gst_caps_copy (GST_PAD_CAPS (pad)); + + tmp = gst_pad_peer_get_caps (self->srcpad); + if (tmp) { + ret = gst_caps_intersect (tmp, gst_pad_get_pad_template_caps (pad)); + gst_caps_unref (tmp); + } else { + ret = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + } + + tmp = gst_pad_peer_get_caps (pad); + if (tmp) { + GstCaps *intersection; + + intersection = gst_caps_intersect (tmp, ret); + gst_caps_unref (tmp); + gst_caps_unref (ret); + ret = intersection; + } + + if (self->height && self->width) { + guint i, n; + + n = gst_caps_get_size (ret); + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (ret, i); + + gst_structure_set (s, "width", G_TYPE_INT, self->width, "height", + G_TYPE_INT, self->height, NULL); + } + } + + tmp = gst_pad_peer_get_caps (self->mask_sinkpad); + if (tmp) { + GstCaps *intersection, *tmp2; + guint i, n; + + tmp = gst_caps_make_writable (tmp); + + tmp2 = gst_caps_copy (gst_pad_get_pad_template_caps (self->mask_sinkpad)); + + intersection = gst_caps_intersect (tmp, tmp2); + gst_caps_unref (tmp); + gst_caps_unref (tmp2); + tmp = intersection; + + n = gst_caps_get_size (tmp); + + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (tmp, i); + + gst_structure_remove_fields (s, "bpp", "depth", "endianness", "framerate", + NULL); + gst_structure_set_name (s, "video/x-raw-yuv"); + } + + intersection = gst_caps_intersect (tmp, ret); + gst_caps_unref (tmp); + gst_caps_unref (ret); + ret = intersection; + } + + gst_object_unref (self); + + GST_DEBUG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret); + + return ret; +} + +static gboolean +gst_shape_wipe_mask_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + gboolean ret = TRUE; + GstStructure *s; + gint width, height, bpp; + + GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "height", &height) || + !gst_structure_get_int (s, "bpp", &bpp)) { + ret = FALSE; + goto done; + } + + if ((self->width != width || self->height != height) && + self->width > 0 && self->height > 0) { + GST_ERROR_OBJECT (pad, "Mask caps must have the same width/height " + "as the video caps"); + ret = FALSE; + goto done; + } else { + self->width = width; + self->height = height; + } + + self->mask_bpp = bpp; + +done: + gst_object_unref (self); + + return ret; +} + +static GstCaps * +gst_shape_wipe_mask_sink_getcaps (GstPad * pad) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + GstCaps *ret, *tmp; + guint i, n; + + if (GST_PAD_CAPS (pad)) + return gst_caps_copy (GST_PAD_CAPS (pad)); + + tmp = gst_pad_peer_get_caps (self->video_sinkpad); + if (tmp) { + ret = + gst_caps_intersect (tmp, + gst_pad_get_pad_template_caps (self->video_sinkpad)); + gst_caps_unref (tmp); + } else { + ret = gst_caps_copy (gst_pad_get_pad_template_caps (self->video_sinkpad)); + } + + tmp = gst_pad_peer_get_caps (self->srcpad); + if (tmp) { + GstCaps *intersection; + + intersection = gst_caps_intersect (ret, tmp); + gst_caps_unref (ret); + gst_caps_unref (tmp); + ret = intersection; + } + + n = gst_caps_get_size (ret); + tmp = gst_caps_new_empty (); + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (ret, i); + GstStructure *t; + + gst_structure_set_name (s, "video/x-raw-gray"); + gst_structure_remove_fields (s, "format", "framerate", NULL); + + if (self->width && self->height) + gst_structure_set (s, "width", G_TYPE_INT, self->width, "height", + G_TYPE_INT, self->height, NULL); + + gst_structure_set (s, "framerate", GST_TYPE_FRACTION, 0, 1, NULL); + + t = gst_structure_copy (s); + + gst_structure_set (s, "bpp", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, + "endianness", G_TYPE_INT, G_BYTE_ORDER, NULL); + gst_structure_set (t, "bpp", G_TYPE_INT, 8, "depth", G_TYPE_INT, 8, NULL); + + gst_caps_append_structure (tmp, t); + } + gst_caps_merge (ret, tmp); + + tmp = gst_pad_peer_get_caps (pad); + if (tmp) { + GstCaps *intersection; + + intersection = gst_caps_intersect (tmp, ret); + gst_caps_unref (tmp); + gst_caps_unref (ret); + ret = intersection; + } + + gst_object_unref (self); + + GST_DEBUG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret); + + return ret; +} + +static GstCaps * +gst_shape_wipe_src_getcaps (GstPad * pad) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + GstCaps *ret, *tmp; + + if (GST_PAD_CAPS (pad)) + return gst_caps_copy (GST_PAD_CAPS (pad)); + else if (GST_PAD_CAPS (self->video_sinkpad)) + return gst_caps_copy (GST_PAD_CAPS (self->video_sinkpad)); + + tmp = gst_pad_peer_get_caps (self->video_sinkpad); + if (tmp) { + ret = + gst_caps_intersect (tmp, + gst_pad_get_pad_template_caps (self->video_sinkpad)); + gst_caps_unref (tmp); + } else { + ret = gst_caps_copy (gst_pad_get_pad_template_caps (self->video_sinkpad)); + } + + tmp = gst_pad_peer_get_caps (pad); + if (tmp) { + GstCaps *intersection; + + intersection = gst_caps_intersect (tmp, ret); + gst_caps_unref (tmp); + gst_caps_unref (ret); + ret = intersection; + } + + if (self->height && self->width) { + guint i, n; + + n = gst_caps_get_size (ret); + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (ret, i); + + gst_structure_set (s, "width", G_TYPE_INT, self->width, "height", + G_TYPE_INT, self->height, NULL); + } + } + + tmp = gst_pad_peer_get_caps (self->mask_sinkpad); + if (tmp) { + GstCaps *intersection, *tmp2; + guint i, n; + + tmp = gst_caps_make_writable (tmp); + tmp2 = gst_caps_copy (gst_pad_get_pad_template_caps (self->mask_sinkpad)); + + intersection = gst_caps_intersect (tmp, tmp2); + gst_caps_unref (tmp); + gst_caps_unref (tmp2); + + tmp = intersection; + n = gst_caps_get_size (tmp); + + for (i = 0; i < n; i++) { + GstStructure *s = gst_caps_get_structure (tmp, i); + + gst_structure_remove_fields (s, "bpp", "depth", "endianness", "framerate", + NULL); + gst_structure_set_name (s, "video/x-raw-yuv"); + } + + intersection = gst_caps_intersect (tmp, ret); + gst_caps_unref (tmp); + gst_caps_unref (ret); + ret = intersection; + } + + gst_object_unref (self); + + GST_DEBUG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret); + + return ret; +} + +static GstFlowReturn +gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf, + GstBuffer * maskbuf, GstBuffer * outbuf) +{ + const guint16 *mask = (const guint16 *) GST_BUFFER_DATA (maskbuf); + const guint8 *input = (const guint8 *) GST_BUFFER_DATA (inbuf); + guint8 *output = (guint8 *) GST_BUFFER_DATA (outbuf); + guint i, j; + guint mask_increment = GST_ROUND_UP_2 (self->width) - self->width; + gfloat position = self->mask_position; + gfloat low = position - (self->mask_border / 2.0f); + gfloat high = position + (self->mask_border / 2.0f); + + if (low < 0.0f) { + high = 0.0f; + low = 0.0f; + } + + if (high > 1.0f) { + low = 1.0f; + high = 1.0f; + } + + for (i = 0; i < self->height; i++) { + for (j = 0; j < self->width; j++) { + gfloat in = *mask / 65535.0f; + + if (in < low) { + output[0] = 0x00; /* A */ + output[1] = 0x00; /* Y */ + output[2] = 0x80; /* U */ + output[3] = 0x80; /* V */ + } else if (in >= high) { + output[0] = 0xff; /* A */ + output[1] = input[1]; /* Y */ + output[2] = input[2]; /* U */ + output[3] = input[3]; /* V */ + } else { + gfloat val = 255.0f * ((in - low) / (high - low)); + + output[0] = CLAMP (val, 0, 255); /* A */ + output[1] = input[1]; /* Y */ + output[2] = input[2]; /* U */ + output[3] = input[3]; /* V */ + } + + mask++; + input += 4; + output += 4; + } + mask += mask_increment; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_shape_wipe_blend_8 (GstShapeWipe * self, GstBuffer * inbuf, + GstBuffer * maskbuf, GstBuffer * outbuf) +{ + const guint8 *mask = (const guint8 *) GST_BUFFER_DATA (maskbuf); + const guint8 *input = (const guint8 *) GST_BUFFER_DATA (inbuf); + guint8 *output = (guint8 *) GST_BUFFER_DATA (outbuf); + guint i, j; + guint mask_increment = GST_ROUND_UP_4 (self->width) - self->width; + gfloat position = self->mask_position; + gfloat low = position - (self->mask_border / 2.0f); + gfloat high = position + (self->mask_border / 2.0f); + + if (low < 0.0f) { + high = 0.0f; + low = 0.0f; + } + + if (high > 1.0f) { + low = 1.0f; + high = 1.0f; + } + + for (i = 0; i < self->height; i++) { + for (j = 0; j < self->width; j++) { + gfloat in = *mask / 255.0f; + + if (in < low) { + output[0] = 0x00; /* A */ + output[1] = 0x00; /* Y */ + output[2] = 0x80; /* U */ + output[3] = 0x80; /* V */ + } else if (in >= high) { + output[0] = 0xff; /* A */ + output[1] = input[1]; /* Y */ + output[2] = input[2]; /* U */ + output[3] = input[3]; /* V */ + } else { + gfloat val = 255.0f * ((in - low) / (high - low)); + + output[0] = CLAMP (val, 0, 255); /* A */ + output[1] = input[1]; /* Y */ + output[2] = input[2]; /* U */ + output[3] = input[3]; /* V */ + } + + mask++; + input += 4; + output += 4; + } + mask += mask_increment; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_shape_wipe_video_sink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (GST_PAD_PARENT (pad)); + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *mask = NULL, *outbuf = NULL; + GstClockTime timestamp; + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + timestamp = + gst_segment_to_stream_time (&self->segment, GST_FORMAT_TIME, timestamp); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) + gst_object_sync_values (G_OBJECT (self), timestamp); + + GST_DEBUG_OBJECT (self, + "Blending buffer with timestamp %" GST_TIME_FORMAT " at position %lf", + GST_TIME_ARGS (timestamp), self->mask_position); + + g_mutex_lock (self->mask_mutex); + mask = self->mask; + if (self->mask) + gst_buffer_ref (self->mask); + else + g_cond_wait (self->mask_cond, self->mask_mutex); + + if (self->mask == NULL) { + g_mutex_unlock (self->mask_mutex); + return GST_FLOW_UNEXPECTED; + } + + mask = gst_buffer_ref (self->mask); + + g_mutex_unlock (self->mask_mutex); + + ret = + gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, + GST_BUFFER_SIZE (buffer), GST_PAD_CAPS (self->srcpad), &outbuf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + return ret; + + if (self->mask_bpp == 16) + ret = gst_shape_wipe_blend_16 (self, buffer, mask, outbuf); + else + ret = gst_shape_wipe_blend_8 (self, buffer, mask, outbuf); + + gst_buffer_unref (mask); + gst_buffer_unref (buffer); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (outbuf); + return ret; + } + + ret = gst_pad_push (self->srcpad, outbuf); + return ret; +} + +static GstFlowReturn +gst_shape_wipe_mask_sink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (GST_PAD_PARENT (pad)); + GstFlowReturn ret = GST_FLOW_OK; + + g_mutex_lock (self->mask_mutex); + GST_DEBUG_OBJECT (self, "Setting new mask buffer: %" GST_PTR_FORMAT, buffer); + + gst_buffer_replace (&self->mask, buffer); + g_cond_signal (self->mask_cond); + g_mutex_unlock (self->mask_mutex); + + return ret; +} + +static GstStateChangeReturn +gst_shape_wipe_change_state (GstElement * element, GstStateChange transition) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + default: + break; + } + + /* Unblock video sink chain function */ + if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) + g_cond_signal (self->mask_cond); + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_shape_wipe_reset (self); + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_shape_wipe_video_sink_event (GstPad * pad, GstEvent * event) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + gboolean ret; + + GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + GstFormat fmt; + gboolean is_update; + gint64 start, end, base; + gdouble rate; + + gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start, + &end, &base); + if (fmt == GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (pad, + "Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%" + GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start), + GST_TIME_ARGS (end)); + gst_segment_set_newsegment (&self->segment, is_update, rate, fmt, start, + end, base); + } else { + gst_segment_init (&self->segment, GST_FORMAT_TIME); + } + } + /* fall through */ + default: + ret = gst_pad_push_event (self->srcpad, event); + break; + } + + gst_object_unref (self); + return ret; +} + +static gboolean +gst_shape_wipe_mask_sink_event (GstPad * pad, GstEvent * event) +{ + GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + /* Dropping all events here */ + gst_event_unref (event); + return TRUE; +} + +static gboolean +gst_shape_wipe_src_event (GstPad * pad, GstEvent * event) +{ + GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad)); + gboolean ret; + + switch (GST_EVENT_TYPE (event)) { + default: + ret = gst_pad_push_event (self->video_sinkpad, event); + break; + } + + gst_object_unref (self); + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_shape_wipe_debug, "shapewipe", 0, + "shapewipe element"); + + gst_controller_init (NULL, NULL); + + if (!gst_element_register (plugin, "shapewipe", GST_RANK_NONE, + GST_TYPE_SHAPE_WIPE)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "shapewipe", + "Shape Wipe transition filter", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/shapewipe/gstshapewipe.h b/gst/shapewipe/gstshapewipe.h new file mode 100644 index 00000000..00ed776e --- /dev/null +++ b/gst/shapewipe/gstshapewipe.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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_SHAPE_WIPE_H__ +#define __GST_SHAPE_WIPE_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> + +G_BEGIN_DECLS + +#define GST_TYPE_SHAPE_WIPE \ + (gst_shape_wipe_get_type()) +#define GST_SHAPE_WIPE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SHAPE_WIPE,GstShapeWipe)) +#define GST_SHAPE_WIPE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SHAPE_WIPE,GstShapeWipeClass)) +#define GST_SHAPE_WIPE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_SHAPE_WIPE,GstShapeWipeClass)) +#define GST_IS_SHAPE_WIPE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SHAPE_WIPE)) +#define GST_IS_SHAPE_WIPE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SHAPE_WIPE)) + +typedef struct _GstShapeWipe GstShapeWipe; +typedef struct _GstShapeWipeClass GstShapeWipeClass; + +struct _GstShapeWipe +{ + GstElement parent; + + /* private */ + GstPad *video_sinkpad; + GstPad *mask_sinkpad; + + GstPad *srcpad; + + GstSegment segment; + + GstBuffer *mask; + gfloat mask_position; + gfloat mask_border; + GMutex *mask_mutex; + GCond *mask_cond; + gint mask_bpp; + + gint width, height; +}; + +struct _GstShapeWipeClass +{ + GstElementClass parent_class; +}; + +GType gst_shape_wipe_get_type (void); + +G_END_DECLS + +#endif /* __GST_SHAPE_WIPE_H__ */ diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index e20db8df..b36232de 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -10,5 +10,5 @@ else DIRECTFB_DIR= endif -SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) switch -DIST_SUBDIRS= directfb switch scaletempo mxf +SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) switch shapewipe mxf +DIST_SUBDIRS= directfb switch scaletempo shapewipe mxf diff --git a/tests/examples/shapewipe/Makefile.am b/tests/examples/shapewipe/Makefile.am new file mode 100644 index 00000000..0a5eecf1 --- /dev/null +++ b/tests/examples/shapewipe/Makefile.am @@ -0,0 +1,8 @@ +noinst_PROGRAMS = shapewipe-example + +shapewipe_example_SOURCES = shapewipe-example.c +shapewipe_example_CFLAGS = $(GST_CFLAGS) $(GST_CONTROLLER_CFLAGS) +shapewipe_example_LDFLAGS = $(GST_LIBS) $(GST_CONTROLLER_LIBS) + +noinst_HEADERS = + diff --git a/tests/examples/shapewipe/shapewipe-example.c b/tests/examples/shapewipe/shapewipe-example.c new file mode 100644 index 00000000..1179aab3 --- /dev/null +++ b/tests/examples/shapewipe/shapewipe-example.c @@ -0,0 +1,128 @@ +/* GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * 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 <gst/gst.h> +#include <gst/controller/gstcontroller.h> +#include <gst/controller/gstlfocontrolsource.h> + +static gboolean +on_message (GstBus * bus, GstMessage * message, gpointer user_data) +{ + GMainLoop *loop = (GMainLoop *) user_data; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + g_warning ("Got ERROR"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_WARNING: + g_warning ("Got WARNING"); + g_main_loop_quit (loop); + break; + case GST_MESSAGE_EOS: + g_main_loop_quit (loop); + break; + default: + break; + } + + return TRUE; +} + +gint +main (gint argc, gchar ** argv) +{ + GstElement *pipeline; + GstElement *shapewipe; + GstController *ctrl; + GstLFOControlSource *csource; + GValue val = { 0, }; + GMainLoop *loop; + GstBus *bus; + gchar *pipeline_string; + + if (argc != 2) { + g_print ("Usage: shapewipe mask.png\n"); + return -1; + } + + gst_init (&argc, &argv); + gst_controller_init (&argc, &argv); + + pipeline_string = + g_strdup_printf + ("videotestsrc ! video/x-raw-yuv,width=640,height=480 ! shapewipe name=shape border=0.05 ! videomixer name=mixer ! ffmpegcolorspace ! autovideosink filesrc location=%s ! typefind ! decodebin2 ! ffmpegcolorspace ! videoscale ! queue ! shape.mask_sink videotestsrc pattern=snow ! video/x-raw-yuv,width=640,height=480 ! queue ! mixer.", + argv[1]); + + pipeline = gst_parse_launch (pipeline_string, NULL); + g_free (pipeline_string); + + if (pipeline == NULL) { + g_print ("Failed to create pipeline\n"); + return -2; + } + + shapewipe = gst_bin_get_by_name (GST_BIN (pipeline), "shape"); + + if (!(ctrl = gst_controller_new (G_OBJECT (shapewipe), "position", NULL))) { + g_print ("can't control shapewipe element\n"); + return -3; + } + + csource = gst_lfo_control_source_new (); + + gst_controller_set_control_source (ctrl, "position", + GST_CONTROL_SOURCE (csource)); + + g_value_init (&val, G_TYPE_FLOAT); + g_value_set_float (&val, 0.5); + g_object_set (G_OBJECT (csource), "amplitude", &val, NULL); + g_value_set_float (&val, 0.5); + g_object_set (G_OBJECT (csource), "offset", &val, NULL); + g_value_unset (&val); + + g_object_set (G_OBJECT (csource), "frequency", 0.5, NULL); + g_object_set (G_OBJECT (csource), "timeshift", 500 * GST_MSECOND, NULL); + + g_object_unref (csource); + + loop = g_main_loop_new (NULL, FALSE); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (on_message), loop); + gst_object_unref (GST_OBJECT (bus)); + + if (gst_element_set_state (pipeline, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + g_error ("Failed to go into PLAYING state"); + return -4; + } + + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + g_main_loop_unref (loop); + + g_object_unref (G_OBJECT (ctrl)); + gst_object_unref (G_OBJECT (pipeline)); + + return 0; +} |