Merge branch 'master' into 0.11

Conflicts:
	ext/ogg/gstoggmux.c
	ext/vorbis/gstvorbisenc.c
This commit is contained in:
Wim Taymans 2011-08-24 11:04:53 +02:00
commit 232a5a3d0a
5 changed files with 223 additions and 54 deletions

View file

@ -491,8 +491,13 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
}
/* get timing info for the packet */
duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
duration = 0;
GST_DEBUG_OBJECT (ogg, "packet is header");
} else {
duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
}
if (packet->b_o_s) {
out_timestamp = GST_CLOCK_TIME_NONE;
@ -671,13 +676,18 @@ gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
if (pad->map.is_sparse)
if (pad->map.is_skeleton)
continue;
/* can do this if the pad start time is not defined */
GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
GST_TIME_ARGS (pad->start_time));
if (pad->start_time == GST_CLOCK_TIME_NONE) {
start_time = G_MAXUINT64;
break;
if (!pad->map.is_sparse) {
start_time = G_MAXUINT64;
break;
}
} else {
start_time = MIN (start_time, pad->start_time);
}
@ -734,7 +744,7 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
&serialno, &type)) {
GST_WARNING_OBJECT (pad->ogg,
GST_DEBUG_OBJECT (pad->ogg,
"got skeleton packet for stream 0x%08x", serialno);
skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
@ -2626,7 +2636,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
GstFlowReturn ret;
GstOggChain *chain = NULL;
gint64 offset = ogg->offset;
ogg_page op;
ogg_page og;
gboolean done;
gint i;
@ -2638,17 +2648,23 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
GstOggPad *pad;
guint32 serial;
ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
if (ret == GST_FLOW_UNEXPECTED) {
GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
} else {
GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
}
break;
}
if (!ogg_page_bos (&op)) {
GST_WARNING_OBJECT (ogg, "page is not BOS page");
if (!ogg_page_bos (&og)) {
GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
/* if we did not find a chain yet, assume this is a bogus stream and
* ignore it */
if (!chain)
if (!chain) {
GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
ret = GST_FLOW_UNEXPECTED;
}
break;
}
@ -2657,7 +2673,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
chain->offset = offset;
}
serial = ogg_page_serialno (&op);
serial = ogg_page_serialno (&og);
if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
GST_WARNING_OBJECT (ogg,
"found serial %08x BOS page twice, ignoring", serial);
@ -2665,7 +2681,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
}
pad = gst_ogg_chain_new_stream (chain, serial);
gst_ogg_pad_submit_page (pad, &op);
gst_ogg_pad_submit_page (pad, &og);
}
if (ret != GST_FLOW_OK || chain == NULL) {
@ -2704,7 +2720,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
gboolean known_serial = FALSE;
GstFlowReturn ret;
serial = ogg_page_serialno (&op);
serial = ogg_page_serialno (&og);
done = TRUE;
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
@ -2718,10 +2734,10 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
/* submit the page now, this will fill in the start_time when the
* internal decoder finds it */
gst_ogg_pad_submit_page (pad, &op);
gst_ogg_pad_submit_page (pad, &og);
if (!pad->map.is_skeleton && pad->start_time == -1
&& ogg_page_eos (&op)) {
&& ogg_page_eos (&og)) {
/* got EOS on a pad before we could find its start_time.
* We have no chance of finding a start_time for every pad so
* stop searching for the other start_time(s).
@ -2747,7 +2763,7 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
}
if (!done) {
ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
if (ret != GST_FLOW_OK)
break;
}
@ -2805,7 +2821,7 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
if (pad->map.is_sparse)
if (pad->map.is_skeleton)
continue;
if (pad->map.serialno == ogg_page_serialno (&og)) {
@ -3677,7 +3693,8 @@ gst_ogg_print (GstOggDemux * ogg)
for (j = 0; j < chain->streams->len; j++) {
GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
GST_INFO_OBJECT (ogg, " stream %08x:", stream->map.serialno);
GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
gst_ogg_stream_get_media_type (&stream->map));
GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->start_time));
}

View file

@ -41,6 +41,7 @@
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/base/gstbytewriter.h>
#include <gst/tag/tag.h>
#include "gstoggmux.h"
@ -83,12 +84,15 @@ enum
#define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000)
#define DEFAULT_SKELETON FALSE
enum
{
ARG_0,
ARG_MAX_DELAY,
ARG_MAX_PAGE_DELAY,
ARG_MAX_TOLERANCE
ARG_MAX_TOLERANCE,
ARG_SKELETON
};
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
@ -169,6 +173,11 @@ gst_ogg_mux_class_init (GstOggMuxClass * klass)
"Maximum timestamp difference for maintaining perfect granules",
0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_SKELETON,
g_param_spec_boolean ("skeleton", "Skeleton",
"Whether to include a Skeleton track",
DEFAULT_SKELETON,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gst_ogg_mux_change_state;
@ -1075,14 +1084,103 @@ gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
}
static void
create_header_packet (ogg_packet * packet, GstOggPadData * pad)
gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
gboolean bos, gboolean eos)
{
packet->granulepos = 0;
/* mark BOS and packet number */
packet->b_o_s = (pad->packetno == 0);
packet->packetno = pad->packetno++;
packet->b_o_s = bos;
/* mark EOS */
packet->e_o_s = 0;
packet->e_o_s = eos;
}
static void
gst_ogg_mux_create_header_packet (ogg_packet * packet, GstOggPadData * pad)
{
gst_ogg_mux_create_header_packet_with_flags (packet, pad->packetno == 0, 0);
packet->packetno = pad->packetno++;
}
static void
gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
{
ogg_packet packet;
gsize size;
packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
packet.bytes = size;
gst_ogg_mux_create_header_packet_with_flags (&packet, bos, eos);
ogg_stream_packetin (os, &packet);
gst_buffer_unref (buf);
}
static void
gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
{
GstByteWriter bw;
GstBuffer *fishead;
GST_DEBUG_OBJECT (mux, "Creating fishead");
gst_byte_writer_init_with_size (&bw, 64, TRUE);
gst_byte_writer_put_string_utf8 (&bw, "fishead");
gst_byte_writer_put_int16_le (&bw, 3); /* version major */
gst_byte_writer_put_int16_le (&bw, 0); /* version minor */
gst_byte_writer_put_int64_le (&bw, 0); /* presentation time numerator */
gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
gst_byte_writer_put_int64_le (&bw, 0); /* base time numerator */
gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
gst_byte_writer_fill (&bw, ' ', 20); /* UTC time */
g_assert (gst_byte_writer_get_pos (&bw) == 64);
fishead = gst_byte_writer_reset_and_get_buffer (&bw);
gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
}
static void
gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
{
gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
}
static void
gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
GstOggPadData * pad)
{
GstByteWriter bw;
GST_DEBUG_OBJECT (mux,
"Creating %s fisbone for serial %08x",
gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
gst_byte_writer_init (&bw);
gst_byte_writer_put_string_utf8 (&bw, "fisbone");
gst_byte_writer_put_int32_le (&bw, 44); /* offset to message headers */
gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
gst_byte_writer_put_uint64_le (&bw, 0); /* base granule */
gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
gst_byte_writer_fill (&bw, 0, 3); /* padding */
/* message header fields - MIME type for now */
gst_ogg_mux_byte_writer_put_string_utf8 (&bw, "Content-Type: ");
gst_ogg_mux_byte_writer_put_string_utf8 (&bw,
gst_ogg_stream_get_media_type (&pad->map));
gst_ogg_mux_byte_writer_put_string_utf8 (&bw, "\r\n");
gst_ogg_mux_submit_skeleton_header_packet (mux, os,
gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
}
static void
gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
{
GST_DEBUG_OBJECT (mux, "Creating fistail");
gst_ogg_mux_submit_skeleton_header_packet (mux, os,
gst_buffer_new_and_alloc (0), 0, 1);
}
/*
@ -1100,6 +1198,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GList *hbufs, *hwalk;
GstCaps *caps;
GstFlowReturn ret;
ogg_page page;
ogg_stream_state skeleton_stream;
hbufs = NULL;
ret = GST_FLOW_OK;
@ -1132,7 +1232,6 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GstOggPadData *pad;
GstBuffer *buf;
ogg_packet packet;
ogg_page page;
GstPad *thepad;
GstCaps *caps;
GstStructure *structure;
@ -1169,7 +1268,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
packet.bytes = size;
create_header_packet (&packet, pad);
gst_ogg_mux_create_header_packet (&packet, pad);
/* swap the packet in */
ogg_stream_packetin (&pad->map.stream, &packet);
@ -1203,6 +1302,16 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
gst_caps_unref (caps);
}
/* The Skeleton BOS goes first - even before the video that went first before */
if (mux->use_skeleton) {
ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
gst_ogg_mux_make_fishead (mux, &skeleton_stream);
while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
hbufs = g_list_append (hbufs, hbuf);
}
}
GST_LOG_OBJECT (mux, "creating next headers");
walk = mux->collect->data;
while (walk) {
@ -1214,6 +1323,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
walk = walk->next;
if (mux->use_skeleton)
gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
GST_DEBUG_PAD_NAME (thepad));
@ -1230,7 +1342,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
packet.bytes = size;
create_header_packet (&packet, pad);
gst_ogg_mux_create_header_packet (&packet, pad);
/* swap the packet in */
ogg_stream_packetin (&pad->map.stream, &packet);
@ -1262,6 +1374,21 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
g_list_free (pad->map.headers);
pad->map.headers = NULL;
}
if (mux->use_skeleton) {
/* flush accumulated fisbones, the fistail must be on a separate page */
while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
hbufs = g_list_append (hbufs, hbuf);
}
gst_ogg_mux_make_fistail (mux, &skeleton_stream);
while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
hbufs = g_list_append (hbufs, hbuf);
}
ogg_stream_clear (&skeleton_stream);
}
/* hbufs holds all buffers for the headers now */
/* create caps with the buffers */
@ -1706,6 +1833,9 @@ gst_ogg_mux_get_property (GObject * object,
case ARG_MAX_TOLERANCE:
g_value_set_uint64 (value, ogg_mux->max_tolerance);
break;
case ARG_SKELETON:
g_value_set_boolean (value, ogg_mux->use_skeleton);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1730,6 +1860,9 @@ gst_ogg_mux_set_property (GObject * object,
case ARG_MAX_TOLERANCE:
ogg_mux->max_tolerance = g_value_get_uint64 (value);
break;
case ARG_SKELETON:
ogg_mux->use_skeleton = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -127,6 +127,9 @@ struct _GstOggMux
pages as delta frames up to the page that has the
keyframe */
/* whether to create a skeleton track */
gboolean use_skeleton;
};
struct _GstOggMuxClass

View file

@ -139,7 +139,8 @@ gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
}
if (mappers[pad->map].granulepos_to_granule_func == NULL) {
GST_WARNING ("Failed to convert granulepos to granule");
GST_WARNING ("Failed to convert %s granulepos to granule",
gst_ogg_stream_get_media_type (pad));
return -1;
}
@ -168,7 +169,8 @@ gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
}
if (mappers[pad->map].granule_to_granulepos_func == NULL) {
GST_WARNING ("Failed to convert granule to granulepos");
GST_WARNING ("Failed to convert %s granule to granulepos",
gst_ogg_stream_get_media_type (pad));
return -1;
}
@ -184,7 +186,8 @@ gst_ogg_stream_granulepos_is_key_frame (GstOggStream * pad, gint64 granulepos)
}
if (mappers[pad->map].is_key_frame_func == NULL) {
GST_WARNING ("Failed to determine key frame");
GST_WARNING ("Failed to determine keyframeness for %s granulepos",
gst_ogg_stream_get_media_type (pad));
return FALSE;
}
@ -195,7 +198,8 @@ gboolean
gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
{
if (mappers[pad->map].is_header_func == NULL) {
GST_WARNING ("Failed to determine header");
GST_WARNING ("Failed to determine headerness of %s packet",
gst_ogg_stream_get_media_type (pad));
return FALSE;
}
@ -206,7 +210,8 @@ gint64
gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
{
if (mappers[pad->map].packet_duration_func == NULL) {
GST_WARNING ("Failed to determine packet duration");
GST_WARNING ("Failed to determine %s packet duration",
gst_ogg_stream_get_media_type (pad));
return -1;
}
@ -1087,7 +1092,7 @@ setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
pad->is_skeleton = TRUE;
pad->is_sparse = TRUE;
pad->caps = gst_caps_new_simple ("none/none", NULL);
pad->caps = gst_caps_new_simple ("application/x-ogg-skeleton", NULL);
return TRUE;
}
@ -1099,12 +1104,15 @@ gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size,
GstOggSkeleton stype;
guint serial_offset;
if (size < SKELETON_FISBONE_MIN_SIZE) {
if (size != 0 && size < SKELETON_FISBONE_MIN_SIZE) {
GST_WARNING ("small fisbone packet of size %d, ignoring", size);
return FALSE;
}
if (memcmp (data, "fisbone\0", 8) == 0) {
if (size == 0) {
/* Skeleton EOS packet is zero bytes */
return FALSE;
} else if (memcmp (data, "fisbone\0", 8) == 0) {
GST_INFO ("got fisbone packet");
stype = GST_OGG_SKELETON_FISBONE;
serial_offset = 12;

View file

@ -1000,12 +1000,12 @@ gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc,
vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
timestamp + duration != vorbisenc->expected_ts) {
/* It turns out that a lot of elements don't generate perfect streams due
* to rounding errors. So, we permit small errors (< 1/2 a sample) without
* to rounding errors. So, we permit small errors (< 3 samples) without
* causing a discont.
*/
int halfsample = GST_SECOND / vorbisenc->frequency / 2;
int threesample = GST_SECOND / vorbisenc->frequency * 3;
if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > halfsample) {
if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > threesample) {
GST_DEBUG_OBJECT (vorbisenc, "Expected TS %" GST_TIME_FORMAT
", buffer TS %" GST_TIME_FORMAT,
GST_TIME_ARGS (vorbisenc->expected_ts), GST_TIME_ARGS (timestamp));
@ -1119,30 +1119,38 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer)
if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE &&
timestamp < vorbisenc->expected_ts) {
int threesample = GST_SECOND / vorbisenc->frequency * 3;
guint64 diff = vorbisenc->expected_ts - timestamp;
guint64 diff_bytes;
gsize size;
GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous "
"timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
"), cannot handle. Clipping buffer.",
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts));
/* Don't freak out on tiny jitters; use the same < 3 sample
tolerance as in the discontinuous detection */
if ((GstClockTimeDiff) (vorbisenc->expected_ts - timestamp) > threesample) {
size = gst_buffer_get_size (buffer);
GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous "
"timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
"), cannot handle. Clipping buffer.",
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts));
diff_bytes =
GST_CLOCK_TIME_TO_FRAMES (diff,
vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat);
if (diff_bytes >= size) {
gst_buffer_unref (buffer);
return GST_FLOW_OK;
size = gst_buffer_get_size (buffer);
diff_bytes =
GST_CLOCK_TIME_TO_FRAMES (diff,
vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat);
if (diff_bytes >= size) {
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
buffer = gst_buffer_make_writable (buffer);
gst_buffer_resize (buffer, diff_bytes, size - diff_bytes);
if (GST_BUFFER_DURATION_IS_VALID (buffer))
GST_BUFFER_DURATION (buffer) -= diff;
}
buffer = gst_buffer_make_writable (buffer);
gst_buffer_resize (buffer, diff_bytes, size - diff_bytes);
/* adjust the input timestamp in either case */
GST_BUFFER_TIMESTAMP (buffer) += diff;
if (GST_BUFFER_DURATION_IS_VALID (buffer))
GST_BUFFER_DURATION (buffer) -= diff;
}
if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, timestamp,