From 35a66554b44f7a4226fde3880b707cf51c6790f1 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 27 May 2009 00:47:05 +0100 Subject: gstspu: Fix a nasty regression, with DVDs randomly crashing Use the local wrapper function when clearing the compositing buffers to ensure the correct portion is being cleared. --- gst/dvdspu/gstspu-vobsub-render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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); -- cgit v1.2.1 From 0716d36b13bef1565af62a2d9b053936e03f1d10 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 27 May 2009 00:51:45 +0100 Subject: gstspu: Convert g_warnings to GST_ERROR, and fix format specifiers Use G_GSSIZE_FORMAT for printing pointer differences, which should work on both 32-bit and 64-bit systems. Use GST_ERROR instead of g_warning for printing messages about unknown packet contents. --- gst/dvdspu/gstspu-pgs.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c index d1d4de18..c2ca171c 100644 --- a/gst/dvdspu/gstspu-pgs.c +++ b/gst/dvdspu/gstspu-pgs.c @@ -430,12 +430,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", end - payload); dump_bytes (payload, end - payload); } @@ -495,7 +496,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", + end - payload); dump_bytes (payload, end - payload); } @@ -529,7 +531,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", + end - payload); dump_bytes (payload, end - payload); } @@ -590,7 +593,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", + end - payload); dump_bytes (payload, end - payload); } @@ -642,7 +646,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; } -- cgit v1.2.1 From af89b6de1c08115ea4e31267de3c36b955ac079a Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 27 May 2009 11:03:14 +0200 Subject: rtpsouce: the network address is in network order Bring the network address in netowkr byte order to the host order. --- gst/rtpmanager/rtpsource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 355526ee..ac79322c 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, -- cgit v1.2.1 From 4d5a48db9779ac1ff4cfda42a5180f8bc1462882 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Wed, 27 May 2009 22:54:51 +0100 Subject: resindvd: Create all the audio streams of all formats Enable creation of the demuxer pads for all audio streams, even types we don't yet support. This means that unsupported types (LPCM, DTS) are output, but not linked to anything yet. If only unsupported streams are available, the user hears silence instead of having the pipeline not pre-roll correctly. This is a prerequisite for hooking up the automatic decoder switching. --- ext/resindvd/gstmpegdemux.c | 17 +++++++++++------ ext/resindvd/gstmpegdemux.h | 1 + ext/resindvd/resindvdsrc.c | 7 ++++--- ext/resindvd/rsnaudiomunge.c | 5 ++++- 4 files changed, 20 insertions(+), 10 deletions(-) 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," -- cgit v1.2.1 From addb24743672a6d123d3f7223864910e9968bf6a Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Thu, 28 May 2009 10:12:58 +0300 Subject: outputselector: implement pad_alloc on active pad. --- gst/selector/gstoutputselector.c | 45 ++++++++++++++++++++++++++++++++++------ gst/selector/gstoutputselector.h | 1 + 2 files changed, 40 insertions(+), 6 deletions(-) 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/selector/gstoutputselector.h b/gst/selector/gstoutputselector.h index 5389d473..4347fd2e 100644 --- a/gst/selector/gstoutputselector.h +++ b/gst/selector/gstoutputselector.h @@ -42,6 +42,7 @@ struct _GstOutputSelector { GstElement element; GstPad *sinkpad; + GstPad *allocpad; GstPad *active_srcpad; GstPad *pending_srcpad; -- cgit v1.2.1 From a0898363d9dd1bb524da2d9715afd83d2f267887 Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Mon, 11 May 2009 16:12:54 +0300 Subject: jack: when stopping playback, do one more cycle to flush the port. Fixes #582167 The gst_jack_audio_client_set_active() flags the port as deactivating and uses a GCond to wait until the jack_process_cb() has run once more and cleared the flag. This way the client zero's the buffer. This happens if one manyally go to PAUSED and then to READY, while leting the mainloop run inbetween. --- ext/jack/gstjackaudioclient.c | 32 +++++++++++++++++++++++++++++--- ext/jack/gstjackaudiosink.c | 7 ++++--- 2 files changed, 33 insertions(+), 6 deletions(-) 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; } -- cgit v1.2.1 From ff6bea9231efb825df35c2e148026b459e72699e Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Thu, 28 May 2009 10:38:50 +0300 Subject: selector: remove not needed instance var (previous commit). --- gst/selector/gstoutputselector.h | 1 - 1 file changed, 1 deletion(-) diff --git a/gst/selector/gstoutputselector.h b/gst/selector/gstoutputselector.h index 4347fd2e..5389d473 100644 --- a/gst/selector/gstoutputselector.h +++ b/gst/selector/gstoutputselector.h @@ -42,7 +42,6 @@ struct _GstOutputSelector { GstElement element; GstPad *sinkpad; - GstPad *allocpad; GstPad *active_srcpad; GstPad *pending_srcpad; -- cgit v1.2.1 From c07be49896edd98a8560f95961d73eccfa8c7997 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Tue, 26 May 2009 19:43:53 +0200 Subject: baseparse: fix debug category --- gst/aacparse/gstbaseparse.c | 2 +- gst/amrparse/gstbaseparse.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 */ -- cgit v1.2.1 From 840955c22672261e9fe77d67a81e5772173cf0dc Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 28 May 2009 13:09:24 +0200 Subject: amrparse: consider header size in byte <-> time conversions --- gst/amrparse/gstamrparse.c | 10 ++++++---- gst/amrparse/gstamrparse.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) 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; -- cgit v1.2.1 From 646d95cfe8fe3070cf9e06e1420d8dbe6cbe5bc7 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 28 May 2009 13:07:37 +0200 Subject: soundtouch: fix detection of libsoundtouch >= 1.4 --- configure.ac | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index eb624b8f..72249488 100644 --- a/configure.ac +++ b/configure.ac @@ -1300,10 +1300,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 -- cgit v1.2.1 From f7f056c6b2fd1e7e997ad417d70756eb66c0d9d2 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Thu, 28 May 2009 13:56:10 +0200 Subject: qtmux: use different stsd atom type for H263 for ISO and QT variants Fixes #584114. --- gst/qtmux/fourcc.h | 1 + gst/qtmux/gstqtmux.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) 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) { -- cgit v1.2.1 From 530377c40f1586378b1fc34be4b5f99b92108078 Mon Sep 17 00:00:00 2001 From: Tristan Matthews Date: Thu, 28 May 2009 23:08:17 +0100 Subject: gstspu: Fix compiler warnings on OS/X Fix some warnings for format string args and uninitialized vars on OS/X Fixes: #584164 Signed-off-by: Jan Schmidt --- gst/dvdspu/gstspu-pgs.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c index c2ca171c..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; } } @@ -436,7 +442,7 @@ parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, if (payload != end) { GST_ERROR ("PGS Composition Object: %" G_GSSIZE_FORMAT - " bytes not consumed", end - payload); + " bytes not consumed", (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -497,7 +503,7 @@ parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, if (payload != end) { GST_ERROR ("PGS Set Palette: %" G_GSSIZE_FORMAT " bytes not consumed", - end - payload); + (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -532,7 +538,7 @@ parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, if (payload != end) { GST_ERROR ("PGS Set Window: %" G_GSSIZE_FORMAT " bytes not consumed", - end - payload); + (gssize) (end - payload)); dump_bytes (payload, end - payload); } @@ -594,7 +600,7 @@ parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, if (payload != end) { GST_ERROR ("PGS Set Object Data: %" G_GSSIZE_FORMAT " bytes not consumed", - end - payload); + (gssize) (end - payload)); dump_bytes (payload, end - payload); } -- cgit v1.2.1 From 53eeb90c217f0cc51297de894c152f364208eea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 28 May 2009 23:12:13 +0100 Subject: =?UTF-8?q?audioparse:=20add=20support=20for=20A-Law=20and=20?= =?UTF-8?q?=C2=B5-Law?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gst/rawparse/gstaudioparse.c | 73 ++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 19 deletions(-) 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; } -- cgit v1.2.1 From 71fe0d7bd3b0704a679e511db2f080cd75864e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 28 May 2009 17:33:10 -0400 Subject: Add ssrc to application/x-rtp-source-sdes structure --- gst/rtpmanager/rtpsource.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index ac79322c..ed080717 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -322,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); -- cgit v1.2.1 From f6be4da21df895dccac5f28d17c18da159a087dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Thu, 28 May 2009 17:37:44 -0400 Subject: rtpbin: Transform the right session sdes message Fixes #584165 --- gst/rtpmanager/gstrtpbin.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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; -- cgit v1.2.1 From febef56cf2c9553ece69cf0f5e7e8ed4e110d7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 May 2009 15:32:24 +0200 Subject: shapewipe: Add a simple shapewipe transition filter & example application --- configure.ac | 3 + gst/shapewipe/Makefile.am | 11 + gst/shapewipe/gstshapewipe.c | 793 +++++++++++++++++++++++++++ gst/shapewipe/gstshapewipe.h | 73 +++ tests/examples/Makefile.am | 4 +- tests/examples/shapewipe/Makefile.am | 6 + tests/examples/shapewipe/shapewipe-example.c | 128 +++++ 7 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 gst/shapewipe/Makefile.am create mode 100644 gst/shapewipe/gstshapewipe.c create mode 100644 gst/shapewipe/gstshapewipe.h create mode 100644 tests/examples/shapewipe/Makefile.am create mode 100644 tests/examples/shapewipe/shapewipe-example.c diff --git a/configure.ac b/configure.ac index 72249488..6c47bc82 100644 --- a/configure.ac +++ b/configure.ac @@ -286,6 +286,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) @@ -1599,6 +1600,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 @@ -1633,6 +1635,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/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..4c30d897 --- /dev/null +++ b/gst/shapewipe/gstshapewipe.c @@ -0,0 +1,793 @@ +/* GStreamer + * Copyright (C) 2009 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 + +#include +#include + +#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 +}; + +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 "); + + 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)); + + 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; + 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; + 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_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; + + for (i = 0; i < self->height; i++) { + for (j = 0; j < self->width; j++) { + if (*mask / 65535.0 < position) { + output[0] = 0x00; /* A */ + output[1] = 0x00; /* Y */ + output[2] = 0x80; /* U */ + output[3] = 0x80; /* V */ + } else { + output[0] = 0xff; /* 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; + + for (i = 0; i < self->height; i++) { + for (j = 0; j < self->width; j++) { + if (*mask / 255.0 < position) { + output[0] = 0x00; /* A */ + output[1] = 0x00; /* Y */ + output[2] = 0x80; /* U */ + output[3] = 0x80; /* V */ + } else { + output[0] = 0xff; /* 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..6ce57c3c --- /dev/null +++ b/gst/shapewipe/gstshapewipe.h @@ -0,0 +1,73 @@ +/* GStreamer + * Copyright (C) 2009 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_SHAPE_WIPE_H__ +#define __GST_SHAPE_WIPE_H__ + +#include +#include + +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; + 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..fc4981a0 --- /dev/null +++ b/tests/examples/shapewipe/Makefile.am @@ -0,0 +1,6 @@ +noinst_PROGRAMS = shapewipe-example + +shapewipe_example_CFLAGS = $(GST_CFLAGS) $(GST_CONTROLLER_LIBS) +shapewipe_example_LDADD = $(GST_LIBS) $(GST_CONTROLLER_LIBS) +shapewipe_example_SOURCES = shapewipe-example.c + diff --git a/tests/examples/shapewipe/shapewipe-example.c b/tests/examples/shapewipe/shapewipe-example.c new file mode 100644 index 00000000..3b57bcc8 --- /dev/null +++ b/tests/examples/shapewipe/shapewipe-example.c @@ -0,0 +1,128 @@ +/* GStreamer + * Copyright (C) 2009 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. + */ + +#include +#include +#include + +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 ! 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; +} -- cgit v1.2.1 From 8d1166bb85981ef14f3183513c1b83852b32f88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 May 2009 16:00:16 +0200 Subject: shapewipe: Fix Makefile of the example application --- tests/examples/shapewipe/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/examples/shapewipe/Makefile.am b/tests/examples/shapewipe/Makefile.am index fc4981a0..0a5eecf1 100644 --- a/tests/examples/shapewipe/Makefile.am +++ b/tests/examples/shapewipe/Makefile.am @@ -1,6 +1,8 @@ noinst_PROGRAMS = shapewipe-example -shapewipe_example_CFLAGS = $(GST_CFLAGS) $(GST_CONTROLLER_LIBS) -shapewipe_example_LDADD = $(GST_LIBS) $(GST_CONTROLLER_LIBS) shapewipe_example_SOURCES = shapewipe-example.c +shapewipe_example_CFLAGS = $(GST_CFLAGS) $(GST_CONTROLLER_CFLAGS) +shapewipe_example_LDFLAGS = $(GST_LIBS) $(GST_CONTROLLER_LIBS) + +noinst_HEADERS = -- cgit v1.2.1 From f3a9f525366cef7f81ea7763c3051a1a0a34126c Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 29 May 2009 15:01:42 +0100 Subject: mpegtsdemux: Use the ISO 639 language code descriptor to send tags. If there is an ISO 639 language descriptor for a stream, send a language code tag so that players can show a meaningful language for the audio and subtitle streams. --- gst/mpegdemux/gstmpegtsdemux.c | 45 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index ef0de2c8..de376f3a 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -822,6 +822,40 @@ 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); + gint i; + 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, i); + 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 +924,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 +947,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 +1060,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"); -- cgit v1.2.1 From db7d1a7eeb9b4a981330b77c942966b09b05ef0d Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Fri, 29 May 2009 15:44:51 +0100 Subject: mpegtsdemux: Fix bogus uninitialised variable access Typo in the previous commit --- gst/mpegdemux/gstmpegtsdemux.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c index de376f3a..b75bfc6d 100644 --- a/gst/mpegdemux/gstmpegtsdemux.c +++ b/gst/mpegdemux/gstmpegtsdemux.c @@ -831,12 +831,11 @@ gst_mpegts_demux_send_tags_for_stream (GstMpegTSDemux * demux, if (stream->ES_info) { guint8 *iso639_languages = gst_mpeg_descriptor_find (stream->ES_info, DESC_ISO_639_LANGUAGE); - gint i; 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, i); + 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]; -- cgit v1.2.1 From 19b4c4f3358641585dac57ec11f7fff80efcb2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 May 2009 16:51:50 +0200 Subject: shapewipe: Add border property to allow smooth borders ...and use a border of 0.01 in the example application. --- gst/shapewipe/gstshapewipe.c | 44 ++++++++++++++++++++++++---- gst/shapewipe/gstshapewipe.h | 1 + tests/examples/shapewipe/shapewipe-example.c | 2 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c index 4c30d897..3efe7cc9 100644 --- a/gst/shapewipe/gstshapewipe.c +++ b/gst/shapewipe/gstshapewipe.c @@ -57,7 +57,8 @@ static GstCaps *gst_shape_wipe_src_getcaps (GstPad * pad); enum { PROP_0, - PROP_POSITION + PROP_POSITION, + PROP_BORDER }; static GstStaticPadTemplate video_sink_pad_template = @@ -121,6 +122,10 @@ gst_shape_wipe_class_init (GstShapeWipeClass * klass) 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); @@ -176,6 +181,9 @@ gst_shape_wipe_get_property (GObject * object, guint 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; @@ -192,6 +200,9 @@ gst_shape_wipe_set_property (GObject * object, guint 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; @@ -227,6 +238,7 @@ gst_shape_wipe_reset (GstShapeWipe * self) 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); @@ -544,19 +556,30 @@ gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_2 (self->width) - self->width; gfloat position = self->mask_position; + gfloat low = MAX (0.0, position - self->mask_border); + gfloat high = MIN (1.0, position + self->mask_border); for (i = 0; i < self->height; i++) { for (j = 0; j < self->width; j++) { - if (*mask / 65535.0 < position) { + gfloat in = *mask / 65535.0; + + if (in <= low) { output[0] = 0x00; /* A */ output[1] = 0x00; /* Y */ output[2] = 0x80; /* U */ output[3] = 0x80; /* V */ - } else { + } 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 * ((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++; @@ -579,19 +602,30 @@ gst_shape_wipe_blend_8 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_4 (self->width) - self->width; gfloat position = self->mask_position; + gfloat low = MAX (0.0, position - self->mask_border); + gfloat high = MIN (1.0, position + self->mask_border); for (i = 0; i < self->height; i++) { for (j = 0; j < self->width; j++) { - if (*mask / 255.0 < position) { + gfloat in = *mask / 255.0; + + if (in <= low) { output[0] = 0x00; /* A */ output[1] = 0x00; /* Y */ output[2] = 0x80; /* U */ output[3] = 0x80; /* V */ - } else { + } 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 * ((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++; diff --git a/gst/shapewipe/gstshapewipe.h b/gst/shapewipe/gstshapewipe.h index 6ce57c3c..00ed776e 100644 --- a/gst/shapewipe/gstshapewipe.h +++ b/gst/shapewipe/gstshapewipe.h @@ -54,6 +54,7 @@ struct _GstShapeWipe GstBuffer *mask; gfloat mask_position; + gfloat mask_border; GMutex *mask_mutex; GCond *mask_cond; gint mask_bpp; diff --git a/tests/examples/shapewipe/shapewipe-example.c b/tests/examples/shapewipe/shapewipe-example.c index 3b57bcc8..a3aed085 100644 --- a/tests/examples/shapewipe/shapewipe-example.c +++ b/tests/examples/shapewipe/shapewipe-example.c @@ -67,7 +67,7 @@ main (gint argc, gchar ** argv) pipeline_string = g_strdup_printf - ("videotestsrc ! video/x-raw-yuv,width=640,height=480 ! shapewipe name=shape ! 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.", + ("videotestsrc ! video/x-raw-yuv,width=640,height=480 ! shapewipe name=shape border=0.01 ! 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); -- cgit v1.2.1 From 332dae71981747841370a239b3b557d5ecb1afb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 May 2009 16:55:25 +0200 Subject: shapewipe: Divide the border value by two, otherwise we use a twice a wide border --- gst/shapewipe/gstshapewipe.c | 8 ++++---- tests/examples/shapewipe/shapewipe-example.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c index 3efe7cc9..69155990 100644 --- a/gst/shapewipe/gstshapewipe.c +++ b/gst/shapewipe/gstshapewipe.c @@ -556,8 +556,8 @@ gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_2 (self->width) - self->width; gfloat position = self->mask_position; - gfloat low = MAX (0.0, position - self->mask_border); - gfloat high = MIN (1.0, position + self->mask_border); + gfloat low = MAX (0.0, position - (self->mask_border / 2.0)); + gfloat high = MIN (1.0, position + (self->mask_border / 2.0)); for (i = 0; i < self->height; i++) { for (j = 0; j < self->width; j++) { @@ -602,8 +602,8 @@ gst_shape_wipe_blend_8 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_4 (self->width) - self->width; gfloat position = self->mask_position; - gfloat low = MAX (0.0, position - self->mask_border); - gfloat high = MIN (1.0, position + self->mask_border); + gfloat low = MAX (0.0, position - (self->mask_border / 2.0)); + gfloat high = MIN (1.0, position + (self->mask_border / 2.0)); for (i = 0; i < self->height; i++) { for (j = 0; j < self->width; j++) { diff --git a/tests/examples/shapewipe/shapewipe-example.c b/tests/examples/shapewipe/shapewipe-example.c index a3aed085..1179aab3 100644 --- a/tests/examples/shapewipe/shapewipe-example.c +++ b/tests/examples/shapewipe/shapewipe-example.c @@ -67,7 +67,7 @@ main (gint argc, gchar ** argv) pipeline_string = g_strdup_printf - ("videotestsrc ! video/x-raw-yuv,width=640,height=480 ! shapewipe name=shape border=0.01 ! 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.", + ("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); -- cgit v1.2.1 From 01b8bacd1b1d1c0793db475fb593cb705143e981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 29 May 2009 21:07:26 +0200 Subject: shapewipe: Adjust border to still have everything transparent at 1.0 and the other way around --- gst/shapewipe/gstshapewipe.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c index 69155990..ec33f0a7 100644 --- a/gst/shapewipe/gstshapewipe.c +++ b/gst/shapewipe/gstshapewipe.c @@ -556,14 +556,24 @@ gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_2 (self->width) - self->width; gfloat position = self->mask_position; - gfloat low = MAX (0.0, position - (self->mask_border / 2.0)); - gfloat high = MIN (1.0, position + (self->mask_border / 2.0)); + 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.0; + gfloat in = *mask / 65535.0f; - if (in <= low) { + if (in < low) { output[0] = 0x00; /* A */ output[1] = 0x00; /* Y */ output[2] = 0x80; /* U */ @@ -574,7 +584,7 @@ gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf, output[2] = input[2]; /* U */ output[3] = input[3]; /* V */ } else { - gfloat val = 255 * ((in - low) / (high - low)); + gfloat val = 255.0f * ((in - low) / (high - low)); output[0] = CLAMP (val, 0, 255); /* A */ output[1] = input[1]; /* Y */ @@ -602,14 +612,24 @@ gst_shape_wipe_blend_8 (GstShapeWipe * self, GstBuffer * inbuf, guint i, j; guint mask_increment = GST_ROUND_UP_4 (self->width) - self->width; gfloat position = self->mask_position; - gfloat low = MAX (0.0, position - (self->mask_border / 2.0)); - gfloat high = MIN (1.0, position + (self->mask_border / 2.0)); + 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.0; + gfloat in = *mask / 255.0f; - if (in <= low) { + if (in < low) { output[0] = 0x00; /* A */ output[1] = 0x00; /* Y */ output[2] = 0x80; /* U */ @@ -620,7 +640,7 @@ gst_shape_wipe_blend_8 (GstShapeWipe * self, GstBuffer * inbuf, output[2] = input[2]; /* U */ output[3] = input[3]; /* V */ } else { - gfloat val = 255 * ((in - low) / (high - low)); + gfloat val = 255.0f * ((in - low) / (high - low)); output[0] = CLAMP (val, 0, 255); /* A */ output[1] = input[1]; /* Y */ -- cgit v1.2.1 From c6cd4d09e2ebfc424e73f5153328ce6260a9da3f Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Sun, 31 May 2009 14:15:11 +0200 Subject: hdvparse: New element for parsing private/hdv-a1 streams. --- configure.ac | 2 + gst/hdvparse/Makefile.am | 13 ++++ gst/hdvparse/gsthdvparse.c | 167 +++++++++++++++++++++++++++++++++++++++++++++ gst/hdvparse/gsthdvparse.h | 56 +++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 gst/hdvparse/Makefile.am create mode 100644 gst/hdvparse/gsthdvparse.c create mode 100644 gst/hdvparse/gsthdvparse.h diff --git a/configure.ac b/configure.ac index 6c47bc82..6222af2d 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) @@ -1581,6 +1582,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 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..0ea5cd20 --- /dev/null +++ b/gst/hdvparse/gsthdvparse.c @@ -0,0 +1,167 @@ +/* + * GStreamer + * Copyright (C) 2009 Edward Hervey + * + * 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 + * + * + * Example launch line + * + * + * gst-launch -v -m filesrc ! mpegtsdemux ! hdvparse ! fakesink silent=TRUE + * + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#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, +}; + +/* 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 " + }; + 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 void +gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf) +{ + GST_MEMDUMP_OBJECT (filter, "BUFFER", GST_BUFFER_DATA (buf), + GST_BUFFER_SIZE (buf)); + 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_PRIMARY, + 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 + * + * 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 +#include + +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__ */ -- cgit v1.2.1 From 5df5059d5675aee4a249fcd3690ac9baf1776849 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Sun, 31 May 2009 18:23:08 +0200 Subject: hdvparse: Emit application message with the parsed information. --- gst/hdvparse/gsthdvparse.c | 199 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 2 deletions(-) diff --git a/gst/hdvparse/gsthdvparse.c b/gst/hdvparse/gsthdvparse.c index 0ea5cd20..d069a363 100644 --- a/gst/hdvparse/gsthdvparse.c +++ b/gst/hdvparse/gsthdvparse.c @@ -55,6 +55,90 @@ 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. @@ -124,11 +208,122 @@ gst_hdvparse_init (GstHDVParse * filter, GstHDVParseClass * klass) 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) { - GST_MEMDUMP_OBJECT (filter, "BUFFER", GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (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; } -- cgit v1.2.1 From c80da130d5f0bb24a0b9b26f5f974c53e3d310ee Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Sun, 31 May 2009 18:23:50 +0200 Subject: hdvparse: Setting rank to NONE so it doesn't get picked up by playbin2. --- gst/hdvparse/gsthdvparse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gst/hdvparse/gsthdvparse.c b/gst/hdvparse/gsthdvparse.c index d069a363..9914cfa2 100644 --- a/gst/hdvparse/gsthdvparse.c +++ b/gst/hdvparse/gsthdvparse.c @@ -347,7 +347,7 @@ gst_hdvparse_transform_ip (GstBaseTransform * base, GstBuffer * outbuf) static gboolean HDVParse_init (GstPlugin * HDVParse) { - return gst_element_register (HDVParse, "hdvparse", GST_RANK_PRIMARY, + return gst_element_register (HDVParse, "hdvparse", GST_RANK_NONE, GST_TYPE_HDVPARSE); } -- cgit v1.2.1 From 7516719b72d1dadd64f511c2f2ad02209aa9c2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 31 May 2009 20:23:19 +0100 Subject: .gitignore: ignore new shapewipe example binary --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 90d737e0..ddb619a4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ autoregen.sh ABOUT-NLS _stdint.h gst-plugins-bad-*.tar.* +tests/examples/shapewipe/shapewipe-example .deps .libs -- cgit v1.2.1 From 7c5ff224fcb10b82d6e591677d556143569e2844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Sun, 31 May 2009 20:24:44 +0100 Subject: sdpdemux: include glib.h before checking if G_OS_WIN32 is defined --- gst/sdp/gstsdpdemux.c | 3 +++ 1 file changed, 3 insertions(+) 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 #endif +/* include GLIB for G_OS_WIN32 */ +#include + #ifdef G_OS_WIN32 #ifdef _MSC_VER #include -- cgit v1.2.1