Merge remote-tracking branch 'origin/master' into 0.11

Conflicts:
	ext/theora/gsttheoraenc.c
	gst-libs/gst/tag/gstexiftag.c
	gst/adder/gstadder.c
	gst/adder/gstadder.h
	gst/playback/gstdecodebin2.c
	gst/playback/gstsubtitleoverlay.c
	tests/check/libs/tag.c
This commit is contained in:
Edward Hervey 2011-12-30 13:21:35 +01:00
commit f562a29284
20 changed files with 870 additions and 613 deletions

View file

@ -310,6 +310,8 @@ gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
if (ogg->pullmode) {
seekable = TRUE;
stop = ogg->total_time;
} else if (ogg->push_disable_seeking) {
seekable = FALSE;
} else if (ogg->current_chain->streams->len) {
gint i;
@ -3238,6 +3240,10 @@ gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
ogg->push_byte_length = length;
GST_DEBUG_OBJECT (ogg,
"File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
} else {
GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
ogg->push_disable_seeking = TRUE;
return TRUE;
}
res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
gst_object_unref (peer);
@ -4545,8 +4551,22 @@ gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
ogg->push_byte_length = -1;
ogg->push_time_length = GST_CLOCK_TIME_NONE;
ogg->push_time_offset = GST_CLOCK_TIME_NONE;
ogg->push_disable_seeking = FALSE;
ogg->push_state = PUSH_PLAYING;
ogg->push_disable_seeking = FALSE;
if (!ogg->pullmode) {
GstPad *peer;
if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
gint64 length = -1;
if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length)
|| length <= 0) {
GST_DEBUG_OBJECT (ogg,
"Unable to determine stream size, assuming live, seeking disabled");
ogg->push_disable_seeking = TRUE;
}
}
}
GST_PUSH_UNLOCK (ogg);
gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
break;

View file

@ -452,7 +452,6 @@ gst_ogg_mux_request_new_pad (GstElement * element,
ogg_mux->active_pads++;
oggpad->map.serialno = serial;
ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
oggpad->packetno = 0;
oggpad->pageno = 0;
oggpad->eos = FALSE;
@ -462,12 +461,18 @@ gst_ogg_mux_request_new_pad (GstElement * element,
oggpad->first_delta = FALSE;
oggpad->prev_delta = FALSE;
oggpad->data_pushed = FALSE;
oggpad->pagebuffers = g_queue_new ();
oggpad->map.headers = NULL;
oggpad->map.queued = NULL;
oggpad->next_granule = 0;
oggpad->keyframe_granule = -1;
if (GST_STATE (ogg_mux) > GST_STATE_READY) {
/* This will be initialized in init_collectpads when going from ready
* paused state */
ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
oggpad->pagebuffers = g_queue_new ();
}
gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);

View file

@ -61,6 +61,7 @@
#include <stdlib.h> /* free */
#include <gst/tag/tag.h>
#include <gst/video/video.h>
#define GST_CAT_DEFAULT theoraenc_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@ -129,6 +130,7 @@ _ilog (unsigned int v)
#define THEORA_DEF_RATE_BUFFER 0
#define THEORA_DEF_MULTIPASS_CACHE_FILE NULL
#define THEORA_DEF_MULTIPASS_MODE MULTIPASS_MODE_SINGLE_PASS
#define THEORA_DEF_DUP_ON_GAP FALSE
enum
{
PROP_0,
@ -151,7 +153,8 @@ enum
PROP_CAP_UNDERFLOW,
PROP_RATE_BUFFER,
PROP_MULTIPASS_CACHE_FILE,
PROP_MULTIPASS_MODE
PROP_MULTIPASS_MODE,
PROP_DUP_ON_GAP
/* FILL ME */
};
@ -270,6 +273,11 @@ static gboolean theora_enc_write_multipass_cache (GstTheoraEnc * enc,
static char *theora_enc_get_supported_formats (void);
static void theora_timefifo_free (GstTheoraEnc * enc);
static GstFlowReturn
theora_enc_encode_and_push (GstTheoraEnc * enc, ogg_packet op,
GstBuffer * buffer);
static void
gst_theora_enc_class_init (GstTheoraEncClass * klass)
{
@ -394,6 +402,16 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass)
"Single pass or first/second pass", GST_TYPE_MULTIPASS_MODE,
THEORA_DEF_MULTIPASS_MODE,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_DUP_ON_GAP,
g_param_spec_boolean ("dup-on-gap", "Create DUP frame on GAP flag",
"Allow codec to handle frames with GAP flag as duplicates "
"of previous frame. "
"This is good to work with variable frame rate stabilized "
"by videorate element. It will add variable latency with maximal "
"size of keyframe distance, this way it is a bad idea "
"to use with live streams.",
THEORA_DEF_DUP_ON_GAP,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&theora_enc_src_factory));
@ -447,6 +465,7 @@ gst_theora_enc_init (GstTheoraEnc * enc)
enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW;
enc->cap_underflow = THEORA_DEF_CAP_UNDERFLOW;
enc->rate_buffer = THEORA_DEF_RATE_BUFFER;
enc->dup_on_gap = THEORA_DEF_DUP_ON_GAP;
enc->multipass_mode = THEORA_DEF_MULTIPASS_MODE;
enc->multipass_cache_file = THEORA_DEF_MULTIPASS_CACHE_FILE;
@ -549,6 +568,8 @@ theora_enc_clear (GstTheoraEnc * enc)
enc->granulepos_offset = 0;
enc->timestamp_offset = 0;
theora_timefifo_free (enc);
enc->next_ts = GST_CLOCK_TIME_NONE;
enc->next_discont = FALSE;
enc->expected_ts = GST_CLOCK_TIME_NONE;
@ -924,6 +945,9 @@ theora_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
}
case GST_EVENT_EOS:
if (enc->initialised) {
/* clear all standing buffers */
if (enc->dup_on_gap)
theora_enc_encode_and_push (enc, op, NULL);
/* push last packet with eos flag, should not be called */
while (th_encode_packetout (enc->encoder, 1, &op)) {
GstClockTime next_time =
@ -945,6 +969,7 @@ theora_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
case GST_EVENT_FLUSH_STOP:
gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
res = gst_pad_push_event (enc->srcpad, event);
theora_timefifo_free (enc);
break;
case GST_EVENT_CUSTOM_DOWNSTREAM:
{
@ -1152,20 +1177,197 @@ theora_enc_write_multipass_cache (GstTheoraEnc * enc, gboolean begin,
return TRUE;
}
/**
* g_slice_free can't be used with g_queue_foreach.
* so we create new function with predefined GstClockTime size.
*/
static void
theora_free_gstclocktime (gpointer mem)
{
g_slice_free (GstClockTime, mem);
}
static void
theora_timefifo_in (GstTheoraEnc * enc, const GstClockTime * timestamp)
{
GstClockTime *ptr;
if (!enc->t_queue)
enc->t_queue = g_queue_new ();
g_assert (enc->t_queue != NULL);
ptr = g_slice_new (GstClockTime);
*ptr = *timestamp;
g_queue_push_head (enc->t_queue, ptr);
}
static GstClockTime
theora_timefifo_out (GstTheoraEnc * enc)
{
GstClockTime ret, *ptr;
g_assert (enc->t_queue != NULL);
ptr = g_queue_pop_tail (enc->t_queue);
g_assert (ptr != NULL);
ret = *ptr;
theora_free_gstclocktime (ptr);
return ret;
}
/**
* theora_timefifo_truncate - truncate the timestamp queue.
* After frame encoding we should have only one buffer for next time.
* The count of timestamps should be the same. If it is less,
* some thing really bad has happened. If it is bigger, encoder
* decided to return less then we ordered.
* TODO: for now we will just drop this timestamps. The better solution
* probably will be to recovery frames by recovery timestamps with
* last buffer.
*/
static void
theora_timefifo_truncate (GstTheoraEnc * enc)
{
if (enc->dup_on_gap) {
guint length;
g_assert (enc->t_queue != NULL);
length = g_queue_get_length (enc->t_queue);
if (length > 1) {
/* it is also not good if we have more then 1. */
GST_DEBUG_OBJECT (enc, "Dropping %u time stamps", length - 1);
while (g_queue_get_length (enc->t_queue) > 1) {
theora_timefifo_out (enc);
}
}
}
}
static void
theora_timefifo_free (GstTheoraEnc * enc)
{
if (enc->t_queue) {
if (g_queue_get_length (enc->t_queue))
g_queue_foreach (enc->t_queue, (GFunc) theora_free_gstclocktime, NULL);
g_queue_free (enc->t_queue);
enc->t_queue = NULL;
}
/* prevbuf makes no sense without timestamps,
* so clear it too. */
if (enc->prevbuf) {
gst_buffer_unref (enc->prevbuf);
enc->prevbuf = NULL;
}
}
static void
theora_update_prevbuf (GstTheoraEnc * enc, GstBuffer * buffer)
{
if (enc->prevbuf) {
gst_buffer_unref (enc->prevbuf);
enc->prevbuf = NULL;
}
enc->prevbuf = gst_buffer_ref (buffer);
}
/**
* theora_enc_encode_and_push - encode buffer or queued previous buffer
* buffer - buffer to encode. If set to NULL it should encode only
* queued buffers and produce dups if needed.
*/
static GstFlowReturn
theora_enc_encode_and_push (GstTheoraEnc * enc, ogg_packet op,
GstClockTime timestamp, GstClockTime running_time,
GstClockTime duration, GstBuffer * buffer)
GstBuffer * buffer)
{
GstFlowReturn ret;
GstVideoFrame frame;
th_ycbcr_buffer ycbcr;
gint res;
GstVideoFrame frame;
gst_video_frame_map (&frame, &enc->vinfo, buffer, GST_MAP_READ);
theora_enc_init_buffer (ycbcr, &frame);
if (enc->dup_on_gap) {
guint t_queue_length;
if (theora_enc_is_discontinuous (enc, running_time, duration)) {
if (enc->t_queue)
t_queue_length = g_queue_get_length (enc->t_queue);
else
t_queue_length = 0;
if (buffer) {
GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
/* videorate can easy create 200 dup frames in one shot.
* In this case th_encode_ctl will just return TH_EINVAL
* and we will generate only one frame as result.
* To make us more bullet proof, make sure we have no
* more dup frames than keyframe interval.
*/
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP) &&
enc->keyframe_force > t_queue_length) {
GST_DEBUG_OBJECT (enc, "Got GAP frame, queue as duplicate.");
theora_timefifo_in (enc, &timestamp);
gst_buffer_unref (buffer);
return GST_FLOW_OK;
} else {
theora_timefifo_in (enc, &timestamp);
/* We should have one frame delay to create correct frame order.
* First time we got buffer, prevbuf should be empty. Nothing else
* should be done here.
*/
if (!enc->prevbuf) {
theora_update_prevbuf (enc, buffer);
gst_buffer_unref (buffer);
return GST_FLOW_OK;
} else {
theora_update_prevbuf (enc, buffer);
/* after theora_update_prevbuf t_queue_length was changed */
t_queue_length++;
if (t_queue_length > 2) {
/* now in t_queue_length should be two real buffers: current and
* previous. All others are timestamps of duplicate frames. */
t_queue_length -= 2;
res = th_encode_ctl (enc->encoder, TH_ENCCTL_SET_DUP_COUNT,
&t_queue_length, sizeof (t_queue_length));
if (res < 0)
GST_WARNING_OBJECT (enc, "Failed marking dups for last frame");
}
}
}
} else {
/* if there is no buffer, then probably we got EOS or discontinuous.
* We need to encode every thing what was left in the queue
*/
GST_DEBUG_OBJECT (enc, "Encode collected buffers.");
if (t_queue_length > 1) {
t_queue_length--;
res = th_encode_ctl (enc->encoder, TH_ENCCTL_SET_DUP_COUNT,
&t_queue_length, sizeof (t_queue_length));
if (res < 0)
GST_WARNING_OBJECT (enc, "Failed marking dups for last frame.");
} else {
GST_DEBUG_OBJECT (enc, "Prevbuffer is empty. Nothing to encode.");
return GST_FLOW_OK;
}
}
gst_video_frame_map (&frame, &enc->vinfo, enc->prevbuf, GST_MAP_READ);
theora_enc_init_buffer (ycbcr, &frame);
} else {
gst_video_frame_map (&frame, &enc->vinfo, buffer, GST_MAP_READ);
theora_enc_init_buffer (ycbcr, &frame);
}
/* check for buffer, it can be optional */
if (enc->current_discont && buffer) {
GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
GstClockTime running_time =
gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
theora_enc_reset (enc);
enc->granulepos_offset =
gst_util_uint64_scale (running_time, enc->vinfo.fps_n,
@ -1197,22 +1399,34 @@ theora_enc_encode_and_push (GstTheoraEnc * enc, ogg_packet op,
ret = GST_FLOW_OK;
while (th_encode_packetout (enc->encoder, 0, &op)) {
GstClockTime next_time;
GstClockTime next_time, duration;
GstClockTime timestamp = 0;
GST_DEBUG_OBJECT (enc, "encoded. granule:%" G_GINT64_FORMAT ", packet:%p, "
"bytes:%ld", op.granulepos, op.packet, op.bytes);
next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;
duration = next_time - enc->next_ts;
ret =
theora_push_packet (enc, &op, timestamp, enc->next_ts,
next_time - enc->next_ts);
if (enc->dup_on_gap && !enc->current_discont)
timestamp = theora_timefifo_out (enc);
else
timestamp = GST_BUFFER_TIMESTAMP (buffer);
ret = theora_push_packet (enc, &op, timestamp, enc->next_ts, duration);
enc->next_ts = next_time;
if (ret != GST_FLOW_OK)
if (ret != GST_FLOW_OK) {
theora_timefifo_truncate (enc);
goto data_push;
}
}
theora_timefifo_truncate (enc);
done:
gst_video_frame_unmap (&frame);
gst_buffer_unref (buffer);
if (buffer)
gst_buffer_unref (buffer);
enc->current_discont = FALSE;
return ret;
@ -1380,8 +1594,14 @@ theora_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
enc->next_ts = 0;
}
ret = theora_enc_encode_and_push (enc, op, timestamp, running_time, duration,
buffer);
enc->current_discont = theora_enc_is_discontinuous (enc,
running_time, duration);
/* empty queue if discontinuous */
if (enc->current_discont && enc->dup_on_gap)
theora_enc_encode_and_push (enc, op, NULL);
ret = theora_enc_encode_and_push (enc, op, buffer);
return ret;
@ -1563,6 +1783,9 @@ theora_enc_set_property (GObject * object, guint prop_id,
case PROP_MULTIPASS_MODE:
enc->multipass_mode = g_value_get_enum (value);
break;
case PROP_DUP_ON_GAP:
enc->dup_on_gap = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1640,6 +1863,9 @@ theora_enc_get_property (GObject * object, guint prop_id,
case PROP_MULTIPASS_MODE:
g_value_set_enum (value, enc->multipass_mode);
break;
case PROP_DUP_ON_GAP:
g_value_set_boolean (value, enc->dup_on_gap);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -122,6 +122,13 @@ struct _GstTheoraEnc
gboolean cap_underflow;
int rate_buffer;
/* variables for dup-on-gap */
gboolean dup_on_gap;
gboolean current_discont;
GstBuffer *prevbuf;
GQueue *t_queue;
/* end dup-on-gap */
GstTheoraEncMultipassMode multipass_mode;
GIOChannel *multipass_cache_fd;
GstAdapter *multipass_cache_adapter;

View file

@ -1921,7 +1921,8 @@ out_of_segment:
too_late:
{
GST_DEBUG_OBJECT (sink, "dropping late sample");
return GST_FLOW_OK;
ret = GST_FLOW_OK;
goto done;
}
/* ERRORS */
payload_failed:

View file

@ -440,6 +440,7 @@ gst_audio_decoder_reset (GstAudioDecoder * dec, gboolean full)
gst_audio_info_init (&dec->priv->ctx.info);
memset (&dec->priv->ctx, 0, sizeof (dec->priv->ctx));
dec->priv->ctx.max_errors = GST_AUDIO_DECODER_MAX_ERRORS;
if (dec->priv->taglist) {
gst_tag_list_free (dec->priv->taglist);
@ -2225,7 +2226,8 @@ gst_audio_decoder_get_delay (GstAudioDecoder * dec)
* @num: max tolerated errors
*
* Sets numbers of tolerated decoder errors, where a tolerated one is then only
* warned about, but more than tolerated will lead to fatal error.
* warned about, but more than tolerated will lead to fatal error. Default
* is set to GST_AUDIO_DECODER_MAX_ERRORS.
*
* Since: 0.10.36
*/

View file

@ -127,6 +127,16 @@ G_STMT_START { \
GST_FUNCTION, __LINE__); \
} G_STMT_END
/**
* GST_AUDIO_DECODER_MAX_ERRORS:
*
* Default maximum number of errors tolerated before signaling error.
*
* Since: 0.10.36
*/
#define GST_AUDIO_DECODER_MAX_ERRORS 10
/**
* GstAudioDecoder:
*
@ -179,7 +189,7 @@ struct _GstAudioDecoder
* most notably a call to gst_audio_decoder_finish_frame().
* @flush: Optional.
* Instructs subclass to clear any codec caches and discard
* any pending samples and not yet returned encoded data.
* any pending samples and not yet returned decoded data.
* @hard indicates whether a FLUSH is being processed,
* or otherwise a DISCONT (or conceptually similar).
* @event: Optional.

View file

@ -805,10 +805,13 @@ gst_audio_encoder_push_buffers (GstAudioEncoder * enc, gboolean force)
static GstFlowReturn
gst_audio_encoder_drain (GstAudioEncoder * enc)
{
GST_DEBUG_OBJECT (enc, "draining");
if (enc->priv->drained)
return GST_FLOW_OK;
else
else {
GST_DEBUG_OBJECT (enc, "... really");
return gst_audio_encoder_push_buffers (enc, TRUE);
}
}
static void

View file

@ -3,6 +3,21 @@
* Functions copied from glib 2.10
*
* Copyright 2005 David Schleef <ds@schleef.org>
*
* 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 __GLIB_COMPAT_PRIVATE_H__

View file

@ -612,7 +612,7 @@ gst_tag_list_has_ifd_tags (const GstTagList * taglist,
static void
gst_exif_writer_write_tag_header (GstExifWriter * writer,
guint16 exif_tag, guint16 exif_type, guint32 count, guint32 offset,
gboolean is_data)
const guint32 * offset_data)
{
GST_DEBUG ("Writing tag entry: id %x, type %u, count %u, offset %u",
exif_tag, exif_type, count, offset);
@ -621,13 +621,17 @@ gst_exif_writer_write_tag_header (GstExifWriter * writer,
gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_tag);
gst_byte_writer_put_uint16_le (&writer->tagwriter, exif_type);
gst_byte_writer_put_uint32_le (&writer->tagwriter, count);
gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
if (offset_data != NULL) {
gst_byte_writer_put_data (&writer->tagwriter, (guint8 *) offset_data, 4);
} else {
gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
}
} else if (writer->byte_order == G_BIG_ENDIAN) {
gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_tag);
gst_byte_writer_put_uint16_be (&writer->tagwriter, exif_type);
gst_byte_writer_put_uint32_be (&writer->tagwriter, count);
if (is_data) {
gst_byte_writer_put_uint32_le (&writer->tagwriter, offset);
if (offset_data != NULL) {
gst_byte_writer_put_data (&writer->tagwriter, (guint8 *) offset_data, 4);
} else {
gst_byte_writer_put_uint32_be (&writer->tagwriter, offset);
}
@ -671,7 +675,7 @@ gst_exif_writer_write_rational_tag (GstExifWriter * writer,
guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_RATIONAL,
1, offset, FALSE);
1, offset, NULL);
gst_exif_writer_write_rational_data (writer, frac_n, frac_d);
}
@ -683,7 +687,7 @@ gst_exif_writer_write_signed_rational_tag (GstExifWriter * writer,
guint32 offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SRATIONAL,
1, offset, FALSE);
1, offset, NULL);
gst_exif_writer_write_signed_rational_data (writer, frac_n, frac_d);
}
@ -720,7 +724,7 @@ gst_exif_writer_write_byte_tag (GstExifWriter * writer, guint16 tag,
GST_WRITE_UINT8 ((guint8 *) & offset, value);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_BYTE,
1, offset, TRUE);
1, offset, &offset);
}
static void
@ -736,7 +740,7 @@ gst_exif_writer_write_short_tag (GstExifWriter * writer, guint16 tag,
}
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_SHORT,
1, offset, TRUE);
1, offset, &offset);
}
static void
@ -751,7 +755,7 @@ gst_exif_writer_write_long_tag (GstExifWriter * writer, guint16 tag,
}
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_LONG,
1, offset, TRUE);
1, offset, &offset);
}
@ -766,29 +770,25 @@ write_exif_undefined_tag (GstExifWriter * writer, guint16 tag,
* resulting tag headers offset and the base offset */
offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
size, offset, FALSE);
size, offset, NULL);
gst_byte_writer_put_data (&writer->datawriter, data, size);
} else {
/* small enough to go in the offset */
memcpy ((guint8 *) & offset, data, size);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_UNDEFINED,
size, offset, TRUE);
size, offset, &offset);
}
}
static void
write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str)
{
gint size;
guint32 offset = 0;
gchar *ascii_str;
gsize ascii_size;
GError *error = NULL;
size = strlen (str) + 1;
ascii_str =
g_convert (str, size, "latin1", "utf8", NULL, &ascii_size, &error);
ascii_str = g_convert (str, -1, "latin1", "utf8", NULL, &ascii_size, &error);
if (error) {
GST_WARNING ("Failed to convert exif tag to ascii: 0x%x - %s. Error: %s",
@ -806,13 +806,13 @@ write_exif_ascii_tag (GstExifWriter * writer, guint16 tag, const gchar * str)
* resulting tag headers offset and the base offset */
offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
ascii_size, offset, FALSE);
ascii_size, offset, NULL);
gst_byte_writer_put_string (&writer->datawriter, ascii_str);
} else {
/* small enough to go in the offset */
memcpy ((guint8 *) & offset, ascii_str, ascii_size);
gst_exif_writer_write_tag_header (writer, tag, EXIF_TYPE_ASCII,
ascii_size, offset, TRUE);
ascii_size, offset, &offset);
}
g_free (ascii_str);
@ -1460,7 +1460,7 @@ parse_exif_rational_tag (GstExifReader * exif_reader,
}
static GstBuffer *
write_exif_ifd (const GstTagList * taglist, gboolean byte_order,
write_exif_ifd (const GstTagList * taglist, guint byte_order,
guint32 base_offset, const GstExifTagMatch * tag_map)
{
GstExifWriter writer;
@ -1520,7 +1520,7 @@ write_exif_ifd (const GstTagList * taglist, gboolean byte_order,
GST_DEBUG ("Adding inner ifd: %x", tag_map[i].exif_tag);
gst_exif_writer_write_tag_header (&writer, tag_map[i].exif_tag,
EXIF_TYPE_LONG, 1,
gst_byte_writer_get_size (&writer.datawriter), FALSE);
gst_byte_writer_get_size (&writer.datawriter), NULL);
data = gst_buffer_map (inner_ifd, &size, NULL, GST_MAP_READ);
gst_byte_writer_put_data (&writer.datawriter, data, size);
@ -1998,7 +1998,7 @@ serialize_geo_coordinate (GstExifWriter * writer, const GstTagList * taglist,
offset = gst_byte_writer_get_size (&writer->datawriter);
gst_exif_writer_write_tag_header (writer, exiftag->exif_tag,
EXIF_TYPE_RATIONAL, 3, offset, FALSE);
EXIF_TYPE_RATIONAL, 3, offset, NULL);
gst_exif_writer_write_rational_data (writer, degrees, 1);
gst_exif_writer_write_rational_data (writer, minutes, 1);
gst_exif_writer_write_rational_data (writer, seconds, 1);

View file

@ -1325,7 +1325,10 @@ gst_tag_list_from_xmp_buffer (GstBuffer * buffer)
if (*xp1 != '>')
goto missing_header;
max_ft_len = 1 + strlen ("<?xpacket end=\".\"?>");
/* Use 2 here to count for an extra trailing \n that was added
* in old versions, this makes it able to parse xmp packets with
* and without this trailing char */
max_ft_len = 2 + strlen ("<?xpacket end=\".\"?>");
if (len < max_ft_len)
goto missing_footer;

View file

@ -1193,9 +1193,9 @@ matrix_yuv_to_rgb (guint8 * tmpline, guint width)
g = (298 * y - 55 * u - 136 * v + 19681) >> 8;
b = (298 * y + 541 * u - 73988) >> 8;
tmpline[i * 4 + ARGB_R] = CLAMP (r, 0, 255);
tmpline[i * 4 + ARGB_G] = CLAMP (g, 0, 255);
tmpline[i * 4 + ARGB_B] = CLAMP (b, 0, 255);
tmpline[i * 4 + 1] = CLAMP (r, 0, 255);
tmpline[i * 4 + 2] = CLAMP (g, 0, 255);
tmpline[i * 4 + 3] = CLAMP (b, 0, 255);
}
}

View file

@ -44,6 +44,7 @@
#include "config.h"
#endif
#include "gstadder.h"
#include <gst/audio/audio.h>
#include <string.h> /* strcmp */
#include "gstadderorc.h"
@ -122,10 +123,13 @@ static void gst_adder_release_pad (GstElement * element, GstPad * pad);
static GstStateChangeReturn gst_adder_change_state (GstElement * element,
GstStateChange transition);
static GstBuffer *gst_adder_do_clip (GstCollectPads * pads,
GstCollectData * data, GstBuffer * buffer, gpointer user_data);
static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
static GstFlowReturn gst_adder_do_clip (GstCollectPads2 * pads,
GstCollectData2 * data, GstBuffer * buffer, GstBuffer ** out,
gpointer user_data);
static GstFlowReturn gst_adder_collected (GstCollectPads2 * pads,
gpointer user_data);
static gboolean gst_adder_event (GstCollectPads2 * pads, GstCollectData2 * pad,
GstEvent * event, gpointer user_data);
/* non-clipping versions (for float) */
#define MAKE_FUNC_NC(name,type) \
@ -678,12 +682,14 @@ gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
/* check if we are flushing */
if (flush) {
/* make sure we accept nothing anymore and return WRONG_STATE */
gst_collect_pads_set_flushing (adder->collect, TRUE);
/* flushing seek, start flush downstream, the flush will be done
* when all pads received a FLUSH_STOP. */
* when all pads received a FLUSH_STOP.
* Make sure we accept nothing anymore and return WRONG_STATE.
* We send a flush-start before, to ensure no streaming is done
* as we need to take the stream lock.
*/
gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
gst_collect_pads2_set_flushing (adder->collect, TRUE);
/* We can't send FLUSH_STOP here since upstream could start pushing data
* after we unlock adder->collect.
@ -698,7 +704,7 @@ gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
/* now wait for the collected to be finished and mark a new
* segment. After we have the lock, no collect function is running and no
* new collect function will be called for as long as we're flushing. */
GST_OBJECT_LOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_LOCK (adder->collect);
adder->segment.rate = rate;
if (curtype == GST_SEEK_TYPE_SET)
adder->segment.start = cur;
@ -711,9 +717,9 @@ gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
if (flush) {
/* Yes, we need to call _set_flushing again *WHEN* the streaming threads
* have stopped so that the cookie gets properly updated. */
gst_collect_pads_set_flushing (adder->collect, TRUE);
gst_collect_pads2_set_flushing (adder->collect, TRUE);
}
GST_OBJECT_UNLOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_UNLOCK (adder->collect);
GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
event);
@ -776,29 +782,27 @@ gst_adder_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
goto beach;
}
case GST_EVENT_FLUSH_STOP:
/* we received a flush-stop. The collect_event function will push the
* event past our element. We simply forward all flush-stop events, even
* when no flush-stop was pending, this is required because collectpads
* does not provide an API to handle-but-not-forward the flush-stop.
* We unset the pending flush-stop flag so that we don't send anymore
* flush-stop from the collect function later.
/* we received a flush-stop. The collect_event function will call the
* gst_adder_event function we have set on the GstCollectPads2, so we
* have control over whether the event is sent past our element.
* We will only forward it when flush_stop_pending is set, and we will
* unset it then.
*/
GST_OBJECT_LOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_LOCK (adder->collect);
g_atomic_int_set (&adder->new_segment_pending, TRUE);
g_atomic_int_set (&adder->flush_stop_pending, FALSE);
/* Clear pending tags */
if (adder->pending_events) {
g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
g_list_free (adder->pending_events);
adder->pending_events = NULL;
}
GST_OBJECT_UNLOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_UNLOCK (adder->collect);
break;
case GST_EVENT_TAG:
GST_OBJECT_LOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_LOCK (adder->collect);
/* collect tags here so we can push them out when we collect data */
adder->pending_events = g_list_append (adder->pending_events, event);
GST_OBJECT_UNLOCK (adder->collect);
GST_COLLECT_PADS2_STREAM_UNLOCK (adder->collect);
goto beach;
case GST_EVENT_SEGMENT:
if (g_atomic_int_compare_and_exchange (&adder->wait_for_new_segment,
@ -812,7 +816,7 @@ gst_adder_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
break;
}
/* now GstCollectPads can take care of the rest, e.g. EOS */
/* now GstCollectPads2 can take care of the rest, e.g. EOS */
ret = adder->collect_event (pad, parent, event);
beach:
@ -879,11 +883,13 @@ gst_adder_init (GstAdder * adder)
adder->filter_caps = NULL;
/* keep track of the sinkpads requested */
adder->collect = gst_collect_pads_new ();
gst_collect_pads_set_function (adder->collect,
adder->collect = gst_collect_pads2_new ();
gst_collect_pads2_set_function (adder->collect,
GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
gst_collect_pads_set_clip_function (adder->collect,
gst_collect_pads2_set_clip_function (adder->collect,
GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
gst_collect_pads2_set_event_function (adder->collect,
GST_DEBUG_FUNCPTR (gst_adder_event), adder);
}
static void
@ -985,11 +991,10 @@ gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
g_free (name);
gst_pad_set_query_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_query));
gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData),
NULL);
gst_collect_pads2_add_pad (adder->collect, newpad, sizeof (GstCollectData2));
/* FIXME: hacked way to override/extend the event function of
* GstCollectPads; because it sets its own event function giving the
* GstCollectPads2; because it sets its own event function giving the
* element no access to events */
adder->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_event));
@ -1009,7 +1014,7 @@ not_sink:
could_not_add:
{
GST_DEBUG_OBJECT (adder, "could not add pad");
gst_collect_pads_remove_pad (adder->collect, newpad);
gst_collect_pads2_remove_pad (adder->collect, newpad);
gst_object_unref (newpad);
return NULL;
}
@ -1024,13 +1029,13 @@ gst_adder_release_pad (GstElement * element, GstPad * pad)
GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
gst_collect_pads_remove_pad (adder->collect, pad);
gst_collect_pads2_remove_pad (adder->collect, pad);
gst_element_remove_pad (element, pad);
}
static GstBuffer *
gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
GstBuffer * buffer, gpointer user_data)
static GstFlowReturn
gst_adder_do_clip (GstCollectPads2 * pads, GstCollectData2 * data,
GstBuffer * buffer, GstBuffer ** out, gpointer user_data)
{
GstAdder *adder = GST_ADDER (user_data);
gint rate, bpf;
@ -1040,11 +1045,12 @@ gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf);
return buffer;
*out = buffer;
return GST_FLOW_OK;
}
static GstFlowReturn
gst_adder_collected (GstCollectPads * pads, gpointer user_data)
gst_adder_collected (GstCollectPads2 * pads, gpointer user_data)
{
/*
* combine streams by adding data values
@ -1086,7 +1092,7 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
/* get available bytes for reading, this can be 0 which could mean empty
* buffers or EOS, which we will catch when we loop over the pads. */
outsize = gst_collect_pads_available (pads);
outsize = gst_collect_pads2_available (pads);
/* can only happen when no pads to collect or all EOS */
if (outsize == 0)
goto eos;
@ -1100,18 +1106,18 @@ gst_adder_collected (GstCollectPads * pads, gpointer user_data)
outsize, bps, bpf);
for (collected = pads->data; collected; collected = next) {
GstCollectData *collect_data;
GstCollectData2 *collect_data;
GstBuffer *inbuf;
gboolean is_gap;
/* take next to see if this is the last collectdata */
next = g_slist_next (collected);
collect_data = (GstCollectData *) collected->data;
collect_data = (GstCollectData2 *) collected->data;
/* get a buffer of size bytes, if we get a buffer, it is at least outsize
* bytes big. */
inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize);
inbuf = gst_collect_pads2_take_buffer (pads, collect_data, outsize);
/* NULL means EOS or an empty buffer so we still need to flush in
* case of an empty buffer. */
if (inbuf == NULL) {
@ -1286,6 +1292,24 @@ eos:
}
}
static gboolean
gst_adder_event (GstCollectPads2 * pads, GstCollectData2 * pad,
GstEvent * event, gpointer user_data)
{
GstAdder *adder = GST_ADDER (user_data);
if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
TRUE, FALSE)) {
return FALSE;
} else {
gst_event_unref (event);
return TRUE;
}
} else {
return FALSE;
}
}
static GstStateChangeReturn
gst_adder_change_state (GstElement * element, GstStateChange transition)
{
@ -1304,14 +1328,14 @@ gst_adder_change_state (GstElement * element, GstStateChange transition)
adder->new_segment_pending = TRUE;
adder->wait_for_new_segment = FALSE;
gst_segment_init (&adder->segment, GST_FORMAT_TIME);
gst_collect_pads_start (adder->collect);
gst_collect_pads2_start (adder->collect);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
/* need to unblock the collectpads before calling the
/* need to unblock the collectpads2 before calling the
* parent change_state so that streaming can finish */
gst_collect_pads_stop (adder->collect);
gst_collect_pads2_stop (adder->collect);
break;
default:
break;

View file

@ -24,7 +24,7 @@
#define __GST_ADDER_H__
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/base/gstcollectpads2.h>
#include <gst/audio/audio.h>
G_BEGIN_DECLS
@ -51,7 +51,7 @@ struct _GstAdder {
GstElement element;
GstPad *srcpad;
GstCollectPads *collect;
GstCollectPads2 *collect;
/* pad counter, used for creating unique request pads */
gint padcount;

View file

@ -3442,7 +3442,8 @@ gst_decode_chain_get_topology (GstDecodeChain * chain)
u = gst_structure_new_id_empty (topology_structure_name);
/* Now at the last element */
if (chain->elements && (chain->endpad || chain->deadend)) {
if ((chain->elements || !chain->active_group) &&
(chain->endpad || chain->deadend)) {
s = gst_structure_new_id_empty (topology_structure_name);
gst_structure_id_set (u, topology_caps, GST_TYPE_CAPS, chain->endcaps,
NULL);

File diff suppressed because it is too large Load diff

View file

@ -103,6 +103,9 @@ get_int_caps (guint channels, gint endianness, guint width,
gchar *string;
GstAudioFormat fmt;
GST_DEBUG ("channels:%d, endianness:%d, width:%d, depth:%d, signedness:%d",
channels, endianness, width, depth, signedness);
fmt = gst_audio_format_build_integer (signedness, endianness, width, depth);
string = g_strdup_printf ("audio/x-raw, "

View file

@ -168,6 +168,8 @@ GST_START_TEST (test_reuse_without_decoders)
/* there shouldn't be any errors */
fail_if (gst_bus_poll (GST_ELEMENT_BUS (pipe), GST_MESSAGE_ERROR, 0) != NULL);
GST_DEBUG ("Resetting pipeline");
/* reset */
gst_element_set_state (pipe, GST_STATE_READY);

View file

@ -986,13 +986,21 @@ GST_START_TEST (test_xmp_parsing)
{
GstTagList *list;
GstBuffer *buf;
guint i, result_size;
guint i, j, result_size;
gchar *text;
const gchar *xmp_header =
"<?xpacket begin=\"\xEF\xBB\xBF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>"
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"GStreamer\">"
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">";
const gchar *xmp_footer = "</rdf:RDF>" "</x:xmpmeta>" "<?xpacket end=\"r\"?>";
/* We used to write an extra trailing \n after the footer, keep compatibility
* with our old generated media by checking that it still can be parsed */
const gchar *xmp_footers[] = {
"</rdf:RDF>" "</x:xmpmeta>" "<?xpacket end=\"r\"?>",
"</rdf:RDF>" "</x:xmpmeta>" "<?xpacket end=\"r\"?>\n",
NULL
};
struct
{
const gchar *xmp_data;
@ -1012,41 +1020,46 @@ GST_START_TEST (test_xmp_parsing)
};
/* test data */
j = 0;
i = 0;
while (test_data[i].xmp_data) {
gsize len;
while (xmp_footers[j]) {
while (test_data[i].xmp_data) {
gsize len;
GST_DEBUG ("trying test-data %u", i);
GST_DEBUG ("trying test-data %u", i);
text = g_strconcat (xmp_header, test_data[i].xmp_data, xmp_footer, NULL);
text =
g_strconcat (xmp_header, test_data[i].xmp_data, xmp_footers[j], NULL);
buf = gst_buffer_new ();
len = strlen (text) + 1;
gst_buffer_take_memory (buf, -1,
gst_memory_new_wrapped (0, text, NULL, len, 0, len));
buf = gst_buffer_new ();
len = strlen (text) + 1;
gst_buffer_take_memory (buf, -1,
gst_memory_new_wrapped (0, text, NULL, len, 0, len));
list = gst_tag_list_from_xmp_buffer (buf);
if (test_data[i].result_size >= 0) {
fail_unless (list != NULL);
list = gst_tag_list_from_xmp_buffer (buf);
if (test_data[i].result_size >= 0) {
fail_unless (list != NULL);
result_size = gst_structure_n_fields ((GstStructure *) list);
fail_unless (result_size == test_data[i].result_size);
result_size = gst_structure_n_fields ((GstStructure *) list);
fail_unless (result_size == test_data[i].result_size);
/* check the taglist content */
switch (test_data[i].result_test) {
case 0:
ASSERT_TAG_LIST_HAS_STRING (list, "description", "test");
break;
default:
break;
/* check the taglist content */
switch (test_data[i].result_test) {
case 0:
ASSERT_TAG_LIST_HAS_STRING (list, "description", "test");
break;
default:
break;
}
}
}
if (list)
gst_tag_list_free (list);
if (list)
gst_tag_list_free (list);
gst_buffer_unref (buf);
g_free (text);
i++;
gst_buffer_unref (buf);
g_free (text);
i++;
}
j++;
}
}

View file

@ -413,15 +413,17 @@ GST_START_TEST (test_video_formats)
fourcc = GST_MAKE_FOURCC (s[0], s[1], s[2], s[3]);
fmt = gst_video_format_from_fourcc (fourcc);
if (fmt == GST_VIDEO_FORMAT_UNKNOWN)
if (fmt == GST_VIDEO_FORMAT_UNKNOWN) {
GST_DEBUG ("Unknown format %s, skipping tests", fourcc_list[i].fourcc);
continue;
}
vf_info = gst_video_format_get_info (fmt);
fail_unless (vf_info != NULL);
fail_unless_equals_int (GST_VIDEO_FORMAT_INFO_FORMAT (vf_info), fmt);
GST_INFO ("Fourcc %s, packed=%", fourcc_list[i].fourcc,
GST_INFO ("Fourcc %s, packed=%d", fourcc_list[i].fourcc,
gst_video_format_is_packed (fmt));
fail_unless (GST_VIDEO_FORMAT_INFO_IS_YUV (vf_info));
@ -632,7 +634,9 @@ GST_START_TEST (test_parse_caps_rgb)
/* make sure they're serialised back correctly */
caps2 = gst_video_info_to_caps (&vinfo);
fail_unless (caps != NULL);
fail_unless (gst_caps_is_equal (caps, caps2));
fail_unless (gst_caps_is_equal (caps, caps2),
"caps [%" GST_PTR_FORMAT "] not equal to caps2 [%" GST_PTR_FORMAT "]",
caps, caps2);
gst_caps_unref (caps);
gst_caps_unref (caps2);