oggdemux: reimplement OGM support

OGM demuxing no longer requires helper elements.  It's done internally
in oggdemux.  Vorbis comments are still not handled because I don't
have anything to test with.
This commit is contained in:
David Schleef 2009-12-03 20:05:29 -08:00
parent 4378851102
commit 8bbe0d126a
3 changed files with 157 additions and 53 deletions

View file

@ -462,13 +462,46 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
gint64 current_time; gint64 current_time;
GstOggChain *chain; GstOggChain *chain;
gint64 duration; gint64 duration;
int offset;
int trim;
GST_DEBUG_OBJECT (ogg, GST_DEBUG_OBJECT (ogg,
"%p streaming to peer serial %08x", pad, pad->map.serialno); "%p streaming to peer serial %08x", pad, pad->map.serialno);
if (pad->map.is_ogm) {
const guint8 *data;
data = packet->packet;
if (data[0] & 1) {
/* We don't push header packets for OGM */
cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
goto done;
}
offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
if (offset > packet->bytes) {
GST_ERROR ("buffer too small");
//goto buffer_too_small;
}
if (pad->map.is_ogm) {
trim = 0;
while (data[packet->bytes - 1 - trim] == 0) {
trim++;
}
} else {
trim = 0;
}
} else {
offset = 0;
trim = 0;
}
ret = ret =
gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad), gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (pad), &buf); GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim,
GST_PAD_CAPS (pad), &buf);
/* combine flows */ /* combine flows */
cret = gst_ogg_demux_combine_flows (ogg, pad, ret); cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
@ -476,7 +509,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
goto no_buffer; goto no_buffer;
/* copy packet in buffer */ /* copy packet in buffer */
memcpy (buf->data, packet->packet, packet->bytes); memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
duration = gst_ogg_stream_get_packet_duration (&pad->map, packet); duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
@ -494,10 +527,17 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
gst_ogg_stream_granulepos_to_key_granule (&pad->map, gst_ogg_stream_granulepos_to_key_granule (&pad->map,
packet->granulepos); packet->granulepos);
} }
GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_granule_to_time (&pad->map, if (pad->map.is_ogm) {
pad->current_granule - duration); GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_granule_to_time (&pad->map,
GST_BUFFER_DURATION (buf) = gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
pad->current_granule) - GST_BUFFER_TIMESTAMP (buf); GST_BUFFER_DURATION (buf) = gst_util_uint64_scale (duration,
GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
} else {
GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_granule_to_time (&pad->map,
pad->current_granule - duration);
GST_BUFFER_DURATION (buf) = gst_ogg_stream_granule_to_time (&pad->map,
pad->current_granule) - GST_BUFFER_TIMESTAMP (buf);
}
GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_END (buf) =
gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule,
pad->keyframe_granule); pad->keyframe_granule);

View file

@ -25,6 +25,8 @@
#include "gstoggstream.h" #include "gstoggstream.h"
#include "dirac_parse.h" #include "dirac_parse.h"
#include <gst/riff/riff-media.h>
#include <string.h> #include <string.h>
GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug); GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
@ -241,12 +243,14 @@ granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
} }
} }
#ifdef unused
static gboolean static gboolean
is_header_unknown (GstOggStream * pad, ogg_packet * packet) is_header_unknown (GstOggStream * pad, ogg_packet * packet)
{ {
GST_WARNING ("don't know how to detect header"); GST_WARNING ("don't know how to detect header");
return FALSE; return FALSE;
} }
#endif
static gboolean static gboolean
is_header_true (GstOggStream * pad, ogg_packet * packet) is_header_true (GstOggStream * pad, ogg_packet * packet)
@ -688,10 +692,6 @@ gst_ogg_map_add_fisbone (GstOggStream * pad,
return TRUE; return TRUE;
} }
#define STREAMHEADER_OGM_AUDIO_MIN_SIZE 53
#define STREAMHEADER_OGM_VIDEO_MIN_SIZE 53
#define STREAMHEADER_OGM_TEXT_MIN_SIZE 9
/* Do we need these for something? /* Do we need these for something?
* ogm->hdr.size = GST_READ_UINT32_LE (&data[13]); * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
* ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]); * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
@ -701,28 +701,68 @@ gst_ogg_map_add_fisbone (GstOggStream * pad,
* ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]); * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
*/ */
static gboolean
is_header_ogm (GstOggStream * pad, ogg_packet * packet)
{
if (packet->bytes >= 1 && (packet->packet[0] & 0x01)) {
return TRUE;
}
return FALSE;
}
static gint64
packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
{
const guint8 *data;
int samples;
int offset;
int n;
data = packet->packet;
offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
if (offset > packet->bytes) {
GST_ERROR ("buffer too small");
return -1;
}
samples = 0;
for (n = offset - 1; n > 0; n--) {
samples = (samples << 8) | data[n];
}
return samples;
}
static gboolean static gboolean
setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet) setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
{ {
guint8 *data = packet->packet; guint8 *data = packet->packet;
guint size = packet->bytes; guint32 fourcc;
if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
memcmp (data, "\001audio\000\000\000", 9) != 0) {
GST_WARNING ("not an ogm audio identification header");
return FALSE;
}
pad->granulerate_n = GST_READ_UINT64_LE (data + 25); pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
pad->granulerate_d = 1; pad->granulerate_d = 1;
fourcc = GST_READ_UINT32_LE (data + 9);
GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
GST_LOG ("sample rate: %d", pad->granulerate_n); GST_LOG ("sample rate: %d", pad->granulerate_n);
if (pad->granulerate_n == 0) if (pad->granulerate_n == 0)
return FALSE; return FALSE;
/* FIXME */ if (pad->caps) {
pad->caps = gst_caps_new_simple ("audio/x-unknown", gst_caps_set_simple (pad->caps,
"rate", G_TYPE_INT, pad->granulerate_n, NULL); "rate", G_TYPE_INT, pad->granulerate_n, NULL);
} else {
pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
"fourcc", GST_TYPE_FOURCC, fourcc,
"rate", G_TYPE_INT, pad->granulerate_n, NULL);
}
pad->n_header_packets = 1;
pad->is_ogm = TRUE;
return TRUE; return TRUE;
} }
@ -731,28 +771,47 @@ static gboolean
setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet) setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
{ {
guint8 *data = packet->packet; guint8 *data = packet->packet;
guint size = packet->bytes; guint32 fourcc;
int width, height;
gint64 time_unit;
if (size < STREAMHEADER_OGM_VIDEO_MIN_SIZE || GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
memcmp (data, "\001video\000\000\000", 9) != 0) { GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
GST_WARNING ("not an ogm video identification header");
return FALSE;
}
pad->granulerate_n = 10000000; pad->granulerate_n = 10000000;
pad->granulerate_d = GST_READ_UINT64_LE (data + 17); time_unit = GST_READ_UINT64_LE (data + 17);
if (time_unit > G_MAXINT || time_unit < G_MININT) {
GST_WARNING ("timeunit is out of range");
}
pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
GST_LOG ("fps = %d/%d = %.3f", GST_LOG ("fps = %d/%d = %.3f",
pad->granulerate_n, pad->granulerate_d, pad->granulerate_n, pad->granulerate_d,
(double) pad->granulerate_n / pad->granulerate_d); (double) pad->granulerate_n / pad->granulerate_d);
if (pad->granulerate_d <= 0) fourcc = GST_READ_UINT32_LE (data + 9);
return FALSE; width = GST_READ_UINT32_LE (data + 45);
height = GST_READ_UINT32_LE (data + 49);
GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
/* FIXME */ pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
pad->caps = gst_caps_new_simple ("video/x-unknown",
"framerate", GST_TYPE_FRACTION, pad->granulerate_n, if (pad->caps == NULL) {
pad->granulerate_d, NULL); pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
"fourcc", GST_TYPE_FOURCC, fourcc,
"framerate", GST_TYPE_FRACTION, pad->granulerate_n,
pad->granulerate_d, NULL);
} else {
gst_caps_set_simple (pad->caps,
"framerate", GST_TYPE_FRACTION, pad->granulerate_n,
pad->granulerate_d,
"width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
}
GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
pad->n_header_packets = 1;
pad->frame_size = 1;
pad->is_ogm = TRUE;
return TRUE; return TRUE;
} }
@ -761,16 +820,14 @@ static gboolean
setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet) setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
{ {
guint8 *data = packet->packet; guint8 *data = packet->packet;
guint size = packet->bytes; gint64 time_unit;
if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
memcmp (data, "\001text\000\000\000\000", 9) != 0) {
GST_WARNING ("not an ogm text identification header");
return FALSE;
}
pad->granulerate_n = 10000000; pad->granulerate_n = 10000000;
pad->granulerate_d = GST_READ_UINT64_LE (data + 17); time_unit = GST_READ_UINT64_LE (data + 17);
if (time_unit > G_MAXINT || time_unit < G_MININT) {
GST_WARNING ("timeunit is out of range");
}
pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
GST_LOG ("fps = %d/%d = %.3f", GST_LOG ("fps = %d/%d = %.3f",
pad->granulerate_n, pad->granulerate_d, pad->granulerate_n, pad->granulerate_d,
@ -779,8 +836,11 @@ setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
if (pad->granulerate_d <= 0) if (pad->granulerate_d <= 0)
return FALSE; return FALSE;
/* FIXME */ pad->caps = gst_caps_new_simple ("text/plain", NULL);
pad->caps = gst_caps_new_simple ("text/x-unknown", NULL);
pad->n_header_packets = 1;
pad->is_ogm = TRUE;
pad->is_ogm_text = TRUE;
return TRUE; return TRUE;
} }
@ -1123,34 +1183,34 @@ static const GstOggMap mappers[] = {
packet_duration_constant packet_duration_constant
}, },
{ {
"OGM audio", 100, 0, "\001audio\0\0\0", 9, 53,
"application/x-ogm-audio", "application/x-ogm-audio",
setup_ogmaudio_mapper, setup_ogmaudio_mapper,
granulepos_to_granule_default, granulepos_to_granule_default,
granule_to_granulepos_default, granule_to_granulepos_default,
is_keyframe_true, is_keyframe_true,
is_header_unknown, is_header_ogm,
NULL packet_duration_ogm
}, },
{ {
"OGM video", 100, 0, "\001video\0\0\0", 9, 53,
"application/x-ogm-video", "application/x-ogm-video",
setup_ogmvideo_mapper, setup_ogmvideo_mapper,
granulepos_to_granule_default, granulepos_to_granule_default,
granule_to_granulepos_default, granule_to_granulepos_default,
NULL, NULL,
is_header_unknown, is_header_ogm,
NULL packet_duration_ogm
}, },
{ {
"OGM text", 100, 0, "\001text\0\0\0", 9, 9,
"application/x-ogm-text", "application/x-ogm-text",
setup_ogmtext_mapper, setup_ogmtext_mapper,
granulepos_to_granule_default, granulepos_to_granule_default,
granule_to_granulepos_default, granule_to_granulepos_default,
is_keyframe_true, is_keyframe_true,
is_header_unknown, is_header_ogm,
NULL packet_duration_ogm
} }
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
@ -1171,7 +1231,8 @@ gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
pad->map = i; pad->map = i;
return TRUE; return TRUE;
} else { } else {
GST_WARNING ("mapper '%s' did not accept setup header", mappers[i].id); GST_WARNING ("mapper '%s' did not accept setup header",
mappers[i].media_type);
} }
} }
} }

View file

@ -65,6 +65,9 @@ struct _GstOggStream
int last_size; int last_size;
/* theora stuff */ /* theora stuff */
gboolean theora_has_zero_keyoffset; gboolean theora_has_zero_keyoffset;
/* OGM stuff */
gboolean is_ogm;
gboolean is_ogm_text;
}; };