diff options
Diffstat (limited to 'gst/matroska/ebml-write.c')
-rwxr-xr-x | gst/matroska/ebml-write.c | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/gst/matroska/ebml-write.c b/gst/matroska/ebml-write.c new file mode 100755 index 0000000..08868db --- /dev/null +++ b/gst/matroska/ebml-write.c @@ -0,0 +1,932 @@ +/* GStreamer EBML I/O + * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net> + * (c) 2005 Michal Benes <michal.benes@xeris.cz> + * + * ebml-write.c: write EBML data to file/stream + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "ebml-write.h" +#include "ebml-ids.h" + + +GST_DEBUG_CATEGORY_STATIC (gst_ebml_write_debug); +#define GST_CAT_DEFAULT gst_ebml_write_debug + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (gst_ebml_write_debug, "ebmlwrite", 0, "Write EBML structured data") +#define parent_class gst_ebml_write_parent_class +G_DEFINE_TYPE_WITH_CODE (GstEbmlWrite, gst_ebml_write, GST_TYPE_OBJECT, + _do_init); + +static void gst_ebml_write_finalize (GObject * object); + +static void +gst_ebml_write_class_init (GstEbmlWriteClass * klass) +{ + GObjectClass *object = G_OBJECT_CLASS (klass); + + object->finalize = gst_ebml_write_finalize; +} + +static void +gst_ebml_write_init (GstEbmlWrite * ebml) +{ + ebml->srcpad = NULL; + ebml->pos = 0; + ebml->last_pos = G_MAXUINT64; /* force segment event */ + + ebml->cache = NULL; + ebml->streamheader = NULL; + ebml->streamheader_pos = 0; + ebml->writing_streamheader = FALSE; + ebml->caps = NULL; +} + +static void +gst_ebml_write_finalize (GObject * object) +{ + GstEbmlWrite *ebml = GST_EBML_WRITE (object); + + gst_object_unref (ebml->srcpad); + + if (ebml->cache) { + gst_byte_writer_free (ebml->cache); + ebml->cache = NULL; + } + + if (ebml->streamheader) { + gst_byte_writer_free (ebml->streamheader); + ebml->streamheader = NULL; + } + + if (ebml->caps) { + gst_caps_unref (ebml->caps); + ebml->caps = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +/** + * gst_ebml_write_new: + * @srcpad: Source pad to which the output will be pushed. + * + * Creates a new #GstEbmlWrite. + * + * Returns: a new #GstEbmlWrite + */ +GstEbmlWrite * +gst_ebml_write_new (GstPad * srcpad) +{ + GstEbmlWrite *ebml = + GST_EBML_WRITE (g_object_new (GST_TYPE_EBML_WRITE, NULL)); + + ebml->srcpad = gst_object_ref (srcpad); + ebml->timestamp = GST_CLOCK_TIME_NONE; + + gst_ebml_write_reset (ebml); + + return ebml; +} + + +/** + * gst_ebml_write_reset: + * @ebml: a #GstEbmlWrite. + * + * Reset internal state of #GstEbmlWrite. + */ +void +gst_ebml_write_reset (GstEbmlWrite * ebml) +{ + ebml->pos = 0; + ebml->last_pos = G_MAXUINT64; /* force segment event */ + + if (ebml->cache) { + gst_byte_writer_free (ebml->cache); + ebml->cache = NULL; + } + + if (ebml->caps) { + gst_caps_unref (ebml->caps); + ebml->caps = NULL; + } + + ebml->last_write_result = GST_FLOW_OK; + ebml->timestamp = GST_CLOCK_TIME_NONE; +} + + +/** + * gst_ebml_last_write_result: + * @ebml: a #GstEbmlWrite. + * + * Returns: GST_FLOW_OK if there was not write error since the last call of + * gst_ebml_last_write_result or code of the error. + */ +GstFlowReturn +gst_ebml_last_write_result (GstEbmlWrite * ebml) +{ + GstFlowReturn res = ebml->last_write_result; + + ebml->last_write_result = GST_FLOW_OK; + + return res; +} + + +void +gst_ebml_start_streamheader (GstEbmlWrite * ebml) +{ + g_return_if_fail (ebml->streamheader == NULL); + + GST_DEBUG ("Starting streamheader at %" G_GUINT64_FORMAT, ebml->pos); + ebml->streamheader = gst_byte_writer_new_with_size (1000, FALSE); + ebml->streamheader_pos = ebml->pos; + ebml->writing_streamheader = TRUE; +} + +GstBuffer * +gst_ebml_stop_streamheader (GstEbmlWrite * ebml) +{ + GstBuffer *buffer; + + if (!ebml->streamheader) + return NULL; + + buffer = gst_byte_writer_free_and_get_buffer (ebml->streamheader); + ebml->streamheader = NULL; + GST_DEBUG ("Streamheader was size %" G_GSIZE_FORMAT, + gst_buffer_get_size (buffer)); + + ebml->writing_streamheader = FALSE; + return buffer; +} + +/** + * gst_ebml_write_set_cache: + * @ebml: a #GstEbmlWrite. + * @size: size of the cache. + * Create a cache. + * + * The idea is that you use this for writing a lot + * of small elements. This will just "queue" all of + * them and they'll be pushed to the next element all + * at once. This saves memory and time for buffer + * allocation and init, and it looks better. + */ +void +gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size) +{ + g_return_if_fail (ebml->cache == NULL); + + GST_DEBUG ("Starting cache at %" G_GUINT64_FORMAT, ebml->pos); + ebml->cache = gst_byte_writer_new_with_size (size, FALSE); + ebml->cache_pos = ebml->pos; +} + +static gboolean +gst_ebml_writer_send_segment_event (GstEbmlWrite * ebml, guint64 new_pos) +{ + GstSegment segment; + gboolean res; + + GST_INFO ("seeking to %" G_GUINT64_FORMAT, new_pos); + + gst_segment_init (&segment, GST_FORMAT_BYTES); + segment.start = new_pos; + segment.stop = -1; + segment.position = 0; + + res = gst_pad_push_event (ebml->srcpad, gst_event_new_segment (&segment)); + + if (!res) + GST_WARNING ("seek to %" G_GUINT64_FORMAT "failed", new_pos); + + return res; +} + +/** + * gst_ebml_write_flush_cache: + * @ebml: a #GstEbmlWrite. + * @timestamp: timestamp of the buffer. + * + * Flush the cache. + */ +void +gst_ebml_write_flush_cache (GstEbmlWrite * ebml, gboolean is_keyframe, + GstClockTime timestamp) +{ + GstBuffer *buffer; + + if (!ebml->cache) + return; + + buffer = gst_byte_writer_free_and_get_buffer (ebml->cache); + ebml->cache = NULL; + GST_DEBUG ("Flushing cache of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (buffer)); + GST_BUFFER_TIMESTAMP (buffer) = timestamp; + GST_BUFFER_OFFSET (buffer) = ebml->pos - gst_buffer_get_size (buffer); + GST_BUFFER_OFFSET_END (buffer) = ebml->pos; + if (ebml->last_write_result == GST_FLOW_OK) { + if (GST_BUFFER_OFFSET (buffer) != ebml->last_pos) { + gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buffer)); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + } + if (ebml->writing_streamheader) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER); + } + if (!is_keyframe) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); + } + ebml->last_pos = ebml->pos; + ebml->last_write_result = gst_pad_push (ebml->srcpad, buffer); + } else { + gst_buffer_unref (buffer); + } +} + + +/** + * gst_ebml_write_element_new: + * @ebml: a #GstEbmlWrite. + * @size: size of the requested buffer. + * + * Create a buffer for one element. If there is + * a cache, use that instead. + * + * Returns: A new #GstBuffer. + */ +static GstBuffer * +gst_ebml_write_element_new (GstEbmlWrite * ebml, GstMapInfo * map, guint size) +{ + /* Create new buffer of size + ID + length */ + GstBuffer *buf; + + /* length, ID */ + size += 12; + + buf = gst_buffer_new_and_alloc (size); + GST_BUFFER_TIMESTAMP (buf) = ebml->timestamp; + + /* FIXME unmap not possible */ + gst_buffer_map (buf, map, GST_MAP_WRITE); + + return buf; +} + + +/** + * gst_ebml_write_element_id: + * @data_inout: Pointer to data pointer + * @id: Element ID that should be written. + * + * Write element ID into a buffer. + */ +static void +gst_ebml_write_element_id (guint8 ** data_inout, guint32 id) +{ + guint8 *data = *data_inout; + guint bytes = 4, mask = 0x10; + + /* get ID length */ + while (!(id & (mask << ((bytes - 1) * 8))) && bytes > 0) { + mask <<= 1; + bytes--; + } + + /* if invalid ID, use dummy */ + if (bytes == 0) { + GST_WARNING ("Invalid ID, voiding"); + bytes = 1; + id = GST_EBML_ID_VOID; + } + + /* write out, BE */ + *data_inout += bytes; + while (bytes--) { + data[bytes] = id & 0xff; + id >>= 8; + } +} + + +/** + * gst_ebml_write_element_size: + * @data_inout: Pointer to data pointer + * @size: Element length. + * + * Write element length into a buffer. + */ +static void +gst_ebml_write_element_size (guint8 ** data_inout, guint64 size) +{ + guint8 *data = *data_inout; + guint bytes = 1, mask = 0x80; + + if (size != GST_EBML_SIZE_UNKNOWN) { + /* how many bytes? - use mask-1 because an all-1 bitset is not allowed */ + while (bytes <= 8 && (size >> ((bytes - 1) * 8)) >= (mask - 1)) { + mask >>= 1; + bytes++; + } + + /* if invalid size, use max. */ + if (bytes > 8) { + GST_WARNING ("Invalid size, writing size unknown"); + mask = 0x01; + bytes = 8; + /* Now here's a real FIXME: we cannot read those yet! */ + size = GST_EBML_SIZE_UNKNOWN; + } + } else { + mask = 0x01; + bytes = 8; + } + + /* write out, BE, with length size marker */ + *data_inout += bytes; + while (bytes-- > 0) { + data[bytes] = size & 0xff; + size >>= 8; + if (!bytes) + *data |= mask; + } +} + + +/** + * gst_ebml_write_element_data: + * @data_inout: Pointer to data pointer + * @write: Data that should be written. + * @length: Length of the data. + * + * Write element data into a buffer. + */ +static void +gst_ebml_write_element_data (guint8 ** data_inout, guint8 * write, + guint64 length) +{ + memcpy (*data_inout, write, length); + *data_inout += length; +} + + +/** + * gst_ebml_write_element_push: + * @ebml: #GstEbmlWrite + * @buf: #GstBuffer to be written. + * @buf_data: Start of data to push from @buf (or NULL for whole buffer). + * @buf_data_end: Data pointer positioned after the last byte in @buf_data (or + * NULL for whole buffer). + * + * Write out buffer by moving it to the next element. + */ +static void +gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf, + guint8 * buf_data, guint8 * buf_data_end) +{ + GstMapInfo map; + guint data_size; + + map.data = NULL; + + if (buf_data_end) + data_size = buf_data_end - buf_data; + else + data_size = gst_buffer_get_size (buf); + + ebml->pos += data_size; + + /* if there's no cache, then don't push it! */ + if (ebml->writing_streamheader) { + if (!buf_data) { + gst_buffer_map (buf, &map, GST_MAP_READ); + buf_data = map.data; + } + if (!buf_data) + GST_WARNING ("Failed to map buffer"); + else if (!gst_byte_writer_put_data (ebml->streamheader, buf_data, + data_size)) + GST_WARNING ("Error writing data to streamheader"); + } + if (ebml->cache) { + if (!buf_data) { + gst_buffer_map (buf, &map, GST_MAP_READ); + buf_data = map.data; + } + if (!buf_data) + GST_WARNING ("Failed to map buffer"); + else if (!gst_byte_writer_put_data (ebml->cache, buf_data, data_size)) + GST_WARNING ("Error writing data to cache"); + if (map.data) + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + return; + } + + if (buf_data && map.data) + gst_buffer_unmap (buf, &map); + + if (ebml->last_write_result == GST_FLOW_OK) { + buf = gst_buffer_make_writable (buf); + GST_BUFFER_OFFSET (buf) = ebml->pos - data_size; + GST_BUFFER_OFFSET_END (buf) = ebml->pos; + if (ebml->writing_streamheader) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); + } + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + + if (GST_BUFFER_OFFSET (buf) != ebml->last_pos) { + gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buf)); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + } + ebml->last_pos = ebml->pos; + ebml->last_write_result = gst_pad_push (ebml->srcpad, buf); + } else { + gst_buffer_unref (buf); + } +} + + +/** + * gst_ebml_write_seek: + * @ebml: #GstEbmlWrite + * @pos: Seek position. + * + * Seek. + */ +void +gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos) +{ + if (ebml->writing_streamheader) { + GST_DEBUG ("wanting to seek to pos %" G_GUINT64_FORMAT, pos); + if (pos >= ebml->streamheader_pos && + pos <= ebml->streamheader_pos + ebml->streamheader->parent.size) { + gst_byte_writer_set_pos (ebml->streamheader, + pos - ebml->streamheader_pos); + GST_DEBUG ("seeked in streamheader to position %" G_GUINT64_FORMAT, + pos - ebml->streamheader_pos); + } else { + GST_WARNING + ("we are writing streamheader still and seek is out of bounds"); + } + } + /* Cache seeking. A bit dangerous, we assume the client writer + * knows what he's doing... */ + if (ebml->cache) { + /* within bounds? */ + if (pos >= ebml->cache_pos && + pos <= ebml->cache_pos + ebml->cache->parent.size) { + GST_DEBUG ("seeking in cache to %" G_GUINT64_FORMAT, pos); + ebml->pos = pos; + gst_byte_writer_set_pos (ebml->cache, ebml->pos - ebml->cache_pos); + return; + } else { + GST_LOG ("Seek outside cache range. Clearing..."); + gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE); + } + } + + GST_INFO ("scheduling seek to %" G_GUINT64_FORMAT, pos); + ebml->pos = pos; +} + + +/** + * gst_ebml_write_get_uint_size: + * @num: Number to be encoded. + * + * Get number of bytes needed to write a uint. + * + * Returns: Encoded uint length. + */ +static guint +gst_ebml_write_get_uint_size (guint64 num) +{ + guint size = 1; + + /* get size */ + while (size < 8 && num >= (G_GINT64_CONSTANT (1) << (size * 8))) { + size++; + } + + return size; +} + + +/** + * gst_ebml_write_set_uint: + * @data_inout: Pointer to data pointer + * @num: Number to be written. + * @size: Encoded number length. + * + * Write an uint into a buffer. + */ +static void +gst_ebml_write_set_uint (guint8 ** data_inout, guint64 num, guint size) +{ + guint8 *data = *data_inout; + + *data_inout += size; + + while (size-- > 0) { + data[size] = num & 0xff; + num >>= 8; + } +} + + +/** + * gst_ebml_write_uint: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @num: Number to be written. + * + * Write uint element. + */ +void +gst_ebml_write_uint (GstEbmlWrite * ebml, guint32 id, guint64 num) +{ + GstBuffer *buf; + guint8 *data_start, *data_end; + guint size = gst_ebml_write_get_uint_size (num); + GstMapInfo map; + + buf = gst_ebml_write_element_new (ebml, &map, sizeof (num)); + data_end = data_start = map.data; + + /* write */ + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, size); + gst_ebml_write_set_uint (&data_end, num, size); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_sint: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @num: Number to be written. + * + * Write sint element. + */ +void +gst_ebml_write_sint (GstEbmlWrite * ebml, guint32 id, gint64 num) +{ + GstBuffer *buf; + guint8 *data_start, *data_end; + GstMapInfo map; + + /* if the signed number is on the edge of a extra-byte, + * then we'll fall over when detecting it. Example: if I + * have a number (-)0x8000 (G_MINSHORT), then my abs()<<1 + * will be 0x10000; this is G_MAXUSHORT+1! So: if (<0) -1. */ + guint64 unum = (num < 0 ? (-num - 1) << 1 : num << 1); + guint size = gst_ebml_write_get_uint_size (unum); + + buf = gst_ebml_write_element_new (ebml, &map, sizeof (num)); + data_end = data_start = map.data; + + /* make unsigned */ + if (num >= 0) { + unum = num; + } else { + unum = ((guint64) 0x80) << ((size - 1) * 8); + unum += num; + unum |= ((guint64) 0x80) << ((size - 1) * 8); + } + + /* write */ + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, size); + gst_ebml_write_set_uint (&data_end, unum, size); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_float: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @num: Number to be written. + * + * Write float element. + */ +void +gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num) +{ + GstBuffer *buf; + GstMapInfo map; + guint8 *data_start, *data_end; + + buf = gst_ebml_write_element_new (ebml, &map, sizeof (num)); + data_end = data_start = map.data; + + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, 8); + num = GDOUBLE_TO_BE (num); + gst_ebml_write_element_data (&data_end, (guint8 *) & num, 8); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_ascii: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @str: String to be written. + * + * Write string element. + */ +void +gst_ebml_write_ascii (GstEbmlWrite * ebml, guint32 id, const gchar * str) +{ + gint len = strlen (str) + 1; /* add trailing '\0' */ + GstBuffer *buf; + GstMapInfo map; + guint8 *data_start, *data_end; + + buf = gst_ebml_write_element_new (ebml, &map, len); + data_end = data_start = map.data; + + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, len); + gst_ebml_write_element_data (&data_end, (guint8 *) str, len); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_utf8: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @str: String to be written. + * + * Write utf8 encoded string element. + */ +void +gst_ebml_write_utf8 (GstEbmlWrite * ebml, guint32 id, const gchar * str) +{ + gst_ebml_write_ascii (ebml, id, str); +} + + +/** + * gst_ebml_write_date: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @date: Date in seconds since the unix epoch. + * + * Write date element. + */ +void +gst_ebml_write_date (GstEbmlWrite * ebml, guint32 id, gint64 date) +{ + gst_ebml_write_sint (ebml, id, (date - GST_EBML_DATE_OFFSET) * GST_SECOND); +} + +/** + * gst_ebml_write_master_start: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * + * Start wiriting mater element. + * + * Master writing is annoying. We use a size marker of + * the max. allowed length, so that we can later fill it + * in validly. + * + * Returns: Master starting position. + */ +guint64 +gst_ebml_write_master_start (GstEbmlWrite * ebml, guint32 id) +{ + guint64 pos = ebml->pos; + GstBuffer *buf; + GstMapInfo map; + guint8 *data_start, *data_end; + + buf = gst_ebml_write_element_new (ebml, &map, 0); + data_end = data_start = map.data; + + gst_ebml_write_element_id (&data_end, id); + pos += data_end - data_start; + gst_ebml_write_element_size (&data_end, GST_EBML_SIZE_UNKNOWN); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); + + return pos; +} + + +/** + * gst_ebml_write_master_finish_full: + * @ebml: #GstEbmlWrite + * @startpos: Master starting position. + * + * Finish writing master element. Size of master element is difference between + * current position and the element start, and @extra_size added to this. + */ +void +gst_ebml_write_master_finish_full (GstEbmlWrite * ebml, guint64 startpos, + guint64 extra_size) +{ + guint64 pos = ebml->pos; + guint8 *data = g_malloc (8); + GstBuffer *buf = gst_buffer_new_wrapped (data, 8); + + gst_ebml_write_seek (ebml, startpos); + + GST_WRITE_UINT64_BE (data, + (G_GINT64_CONSTANT (1) << 56) | (pos - startpos - 8 + extra_size)); + + gst_ebml_write_element_push (ebml, buf, NULL, NULL); + gst_ebml_write_seek (ebml, pos); +} + +void +gst_ebml_write_master_finish (GstEbmlWrite * ebml, guint64 startpos) +{ + gst_ebml_write_master_finish_full (ebml, startpos, 0); +} + +/** + * gst_ebml_write_binary: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @binary: Data to be written. + * @length: Length of the data + * + * Write an element with binary data. + */ +void +gst_ebml_write_binary (GstEbmlWrite * ebml, + guint32 id, guint8 * binary, guint64 length) +{ + GstBuffer *buf; + GstMapInfo map; + guint8 *data_start, *data_end; + + buf = gst_ebml_write_element_new (ebml, &map, length); + data_end = data_start = map.data; + + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, length); + gst_ebml_write_element_data (&data_end, binary, length); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_buffer_header: + * @ebml: #GstEbmlWrite + * @id: Element ID. + * @length: Length of the data + * + * Write header of the binary element (use with gst_ebml_write_buffer function). + * + * For things like video frames and audio samples, + * you want to use this function, as it doesn't have + * the overhead of memcpy() that other functions + * such as write_binary() do have. + */ +void +gst_ebml_write_buffer_header (GstEbmlWrite * ebml, guint32 id, guint64 length) +{ + GstBuffer *buf; + GstMapInfo map; + guint8 *data_start, *data_end; + + buf = gst_ebml_write_element_new (ebml, &map, 0); + data_end = data_start = map.data; + + gst_ebml_write_element_id (&data_end, id); + gst_ebml_write_element_size (&data_end, length); + gst_buffer_unmap (buf, &map); + gst_buffer_set_size (buf, (data_end - data_start)); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); +} + + +/** + * gst_ebml_write_buffer: + * @ebml: #GstEbmlWrite + * @buf: #GstBuffer cointaining the data. + * + * Write binary element (see gst_ebml_write_buffer_header). + */ +void +gst_ebml_write_buffer (GstEbmlWrite * ebml, GstBuffer * buf) +{ + gst_ebml_write_element_push (ebml, buf, NULL, NULL); +} + + +/** + * gst_ebml_replace_uint: + * @ebml: #GstEbmlWrite + * @pos: Position of the uint that should be replaced. + * @num: New value. + * + * Replace uint with a new value. + * + * When replacing a uint, we assume that it is *always* + * 8-byte, since that's the safest guess we can do. This + * is just for simplicity. + * + * FIXME: this function needs to be replaced with something + * proper. This is a crude hack. + */ +void +gst_ebml_replace_uint (GstEbmlWrite * ebml, guint64 pos, guint64 num) +{ + guint64 oldpos = ebml->pos; + guint8 *data_start, *data_end; + GstBuffer *buf; + + data_start = g_malloc (8); + data_end = data_start; + buf = gst_buffer_new_wrapped (data_start, 8); + + gst_ebml_write_seek (ebml, pos); + gst_ebml_write_set_uint (&data_end, num, 8); + + gst_ebml_write_element_push (ebml, buf, data_start, data_end); + gst_ebml_write_seek (ebml, oldpos); +} + +/** + * gst_ebml_write_header: + * @ebml: #GstEbmlWrite + * @doctype: Document type. + * @version: Document type version. + * + * Write EBML header. + */ +void +gst_ebml_write_header (GstEbmlWrite * ebml, const gchar * doctype, + guint version) +{ + guint64 pos; + + /* write the basic EBML header */ + gst_ebml_write_set_cache (ebml, 0x40); + pos = gst_ebml_write_master_start (ebml, GST_EBML_ID_HEADER); +#if (GST_EBML_VERSION != 1) + gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLVERSION, GST_EBML_VERSION); + gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLREADVERSION, GST_EBML_VERSION); +#endif +#if 0 + /* we don't write these until they're "non-default" (never!) */ + gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLMAXIDLENGTH, sizeof (guint32)); + gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLMAXSIZELENGTH, sizeof (guint64)); +#endif + gst_ebml_write_ascii (ebml, GST_EBML_ID_DOCTYPE, doctype); + gst_ebml_write_uint (ebml, GST_EBML_ID_DOCTYPEVERSION, version); + gst_ebml_write_uint (ebml, GST_EBML_ID_DOCTYPEREADVERSION, version); + gst_ebml_write_master_finish (ebml, pos); + gst_ebml_write_flush_cache (ebml, FALSE, 0); +} |