oggmux: add skeleton write support

Version written is 3.0

Base times are left empty for now.

Content-Type should be the MIME type of the stream. It is set to
the GStreamer media type for now, which is probably the same for
the streams oggmux supports.

https://bugzilla.gnome.org/show_bug.cgi?id=563251
This commit is contained in:
Vincent Penquerc'h 2011-08-17 17:09:44 +01:00 committed by Sebastian Dröge
parent 8a752e44e2
commit 2301f1806f
2 changed files with 142 additions and 9 deletions

View file

@ -41,6 +41,7 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstcollectpads.h> #include <gst/base/gstcollectpads.h>
#include <gst/base/gstbytewriter.h>
#include <gst/tag/tag.h> #include <gst/tag/tag.h>
#include "gstoggmux.h" #include "gstoggmux.h"
@ -83,12 +84,15 @@ enum
#define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000) #define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000) #define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000) #define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000)
#define DEFAULT_SKELETON FALSE
enum enum
{ {
ARG_0, ARG_0,
ARG_MAX_DELAY, ARG_MAX_DELAY,
ARG_MAX_PAGE_DELAY, ARG_MAX_PAGE_DELAY,
ARG_MAX_TOLERANCE ARG_MAX_TOLERANCE,
ARG_SKELETON
}; };
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
@ -211,6 +215,11 @@ gst_ogg_mux_class_init (GstOggMuxClass * klass)
"Maximum timestamp difference for maintaining perfect granules", "Maximum timestamp difference for maintaining perfect granules",
0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE, 0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); (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; gstelement_class->change_state = gst_ogg_mux_change_state;
@ -1121,16 +1130,103 @@ gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
} }
static void static void
create_header_packet (ogg_packet * packet, GstBuffer * buf, GstOggPadData * pad) gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
GstBuffer * buf, gboolean bos, gboolean eos)
{ {
packet->packet = GST_BUFFER_DATA (buf); packet->packet = GST_BUFFER_DATA (buf);
packet->bytes = GST_BUFFER_SIZE (buf); packet->bytes = GST_BUFFER_SIZE (buf);
packet->granulepos = 0; packet->granulepos = 0;
/* mark BOS and packet number */ /* mark BOS and packet number */
packet->b_o_s = (pad->packetno == 0); packet->b_o_s = bos;
packet->packetno = pad->packetno++;
/* mark EOS */ /* mark EOS */
packet->e_o_s = 0; packet->e_o_s = eos;
}
static void
gst_ogg_mux_create_header_packet (ogg_packet * packet, GstBuffer * buf,
GstOggPadData * pad)
{
gst_ogg_mux_create_header_packet_with_flags (packet, buf, 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;
gst_ogg_mux_create_header_packet_with_flags (&packet, buf, 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");
fishead = gst_buffer_new_and_alloc (64);
gst_byte_writer_init_with_buffer (&bw, fishead, 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) == GST_BUFFER_SIZE (fishead));
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);
} }
/* /*
@ -1148,6 +1244,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GList *hbufs, *hwalk; GList *hbufs, *hwalk;
GstCaps *caps; GstCaps *caps;
GstFlowReturn ret; GstFlowReturn ret;
ogg_page page;
ogg_stream_state skeleton_stream;
hbufs = NULL; hbufs = NULL;
ret = GST_FLOW_OK; ret = GST_FLOW_OK;
@ -1180,7 +1278,6 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
GstOggPadData *pad; GstOggPadData *pad;
GstBuffer *buf; GstBuffer *buf;
ogg_packet packet; ogg_packet packet;
ogg_page page;
GstPad *thepad; GstPad *thepad;
GstCaps *caps; GstCaps *caps;
GstStructure *structure; GstStructure *structure;
@ -1213,7 +1310,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
} }
/* create a packet from the buffer */ /* create a packet from the buffer */
create_header_packet (&packet, buf, pad); gst_ogg_mux_create_header_packet (&packet, buf, pad);
/* swap the packet in */ /* swap the packet in */
ogg_stream_packetin (&pad->map.stream, &packet); ogg_stream_packetin (&pad->map.stream, &packet);
@ -1245,6 +1342,16 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
gst_caps_unref (caps); 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"); GST_LOG_OBJECT (mux, "creating next headers");
walk = mux->collect->data; walk = mux->collect->data;
while (walk) { while (walk) {
@ -1256,6 +1363,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
walk = walk->next; 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_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
GST_DEBUG_PAD_NAME (thepad)); GST_DEBUG_PAD_NAME (thepad));
@ -1263,12 +1373,11 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
while (hwalk) { while (hwalk) {
GstBuffer *buf = GST_BUFFER (hwalk->data); GstBuffer *buf = GST_BUFFER (hwalk->data);
ogg_packet packet; ogg_packet packet;
ogg_page page;
hwalk = hwalk->next; hwalk = hwalk->next;
/* create a packet from the buffer */ /* create a packet from the buffer */
create_header_packet (&packet, buf, pad); gst_ogg_mux_create_header_packet (&packet, buf, pad);
/* swap the packet in */ /* swap the packet in */
ogg_stream_packetin (&pad->map.stream, &packet); ogg_stream_packetin (&pad->map.stream, &packet);
@ -1299,6 +1408,21 @@ gst_ogg_mux_send_headers (GstOggMux * mux)
g_list_free (pad->map.headers); g_list_free (pad->map.headers);
pad->map.headers = NULL; 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 */ /* hbufs holds all buffers for the headers now */
/* create caps with the buffers */ /* create caps with the buffers */
@ -1741,6 +1865,9 @@ gst_ogg_mux_get_property (GObject * object,
case ARG_MAX_TOLERANCE: case ARG_MAX_TOLERANCE:
g_value_set_uint64 (value, ogg_mux->max_tolerance); g_value_set_uint64 (value, ogg_mux->max_tolerance);
break; break;
case ARG_SKELETON:
g_value_set_boolean (value, ogg_mux->use_skeleton);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -1765,6 +1892,9 @@ gst_ogg_mux_set_property (GObject * object,
case ARG_MAX_TOLERANCE: case ARG_MAX_TOLERANCE:
ogg_mux->max_tolerance = g_value_get_uint64 (value); ogg_mux->max_tolerance = g_value_get_uint64 (value);
break; break;
case ARG_SKELETON:
ogg_mux->use_skeleton = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;

View file

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