1 /* GStreamer EBML I/O
2  * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4  *
5  * ebml-write.c: write EBML data to file/stream
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <string.h>
28 
29 #include "ebml-write.h"
30 #include "ebml-ids.h"
31 
32 
33 GST_DEBUG_CATEGORY_STATIC (gst_ebml_write_debug);
34 #define GST_CAT_DEFAULT gst_ebml_write_debug
35 
36 #define _do_init \
37       GST_DEBUG_CATEGORY_INIT (gst_ebml_write_debug, "ebmlwrite", 0, "Write EBML structured data")
38 #define parent_class gst_ebml_write_parent_class
39 G_DEFINE_TYPE_WITH_CODE (GstEbmlWrite, gst_ebml_write, GST_TYPE_OBJECT,
40     _do_init);
41 
42 static void gst_ebml_write_finalize (GObject * object);
43 
44 static void
gst_ebml_write_class_init(GstEbmlWriteClass * klass)45 gst_ebml_write_class_init (GstEbmlWriteClass * klass)
46 {
47   GObjectClass *object = G_OBJECT_CLASS (klass);
48 
49   object->finalize = gst_ebml_write_finalize;
50 }
51 
52 static void
gst_ebml_write_init(GstEbmlWrite * ebml)53 gst_ebml_write_init (GstEbmlWrite * ebml)
54 {
55   ebml->srcpad = NULL;
56   ebml->pos = 0;
57   ebml->last_pos = G_MAXUINT64; /* force segment event */
58 
59   ebml->cache = NULL;
60   ebml->streamheader = NULL;
61   ebml->streamheader_pos = 0;
62   ebml->writing_streamheader = FALSE;
63   ebml->caps = NULL;
64 }
65 
66 static void
gst_ebml_write_finalize(GObject * object)67 gst_ebml_write_finalize (GObject * object)
68 {
69   GstEbmlWrite *ebml = GST_EBML_WRITE (object);
70 
71   gst_object_unref (ebml->srcpad);
72 
73   if (ebml->cache) {
74     gst_byte_writer_free (ebml->cache);
75     ebml->cache = NULL;
76   }
77 
78   if (ebml->streamheader) {
79     gst_byte_writer_free (ebml->streamheader);
80     ebml->streamheader = NULL;
81   }
82 
83   if (ebml->caps) {
84     gst_caps_unref (ebml->caps);
85     ebml->caps = NULL;
86   }
87 
88   G_OBJECT_CLASS (parent_class)->finalize (object);
89 }
90 
91 
92 /**
93  * gst_ebml_write_new:
94  * @srcpad: Source pad to which the output will be pushed.
95  *
96  * Creates a new #GstEbmlWrite.
97  *
98  * Returns: a new #GstEbmlWrite
99  */
100 GstEbmlWrite *
gst_ebml_write_new(GstPad * srcpad)101 gst_ebml_write_new (GstPad * srcpad)
102 {
103   GstEbmlWrite *ebml =
104       GST_EBML_WRITE (g_object_new (GST_TYPE_EBML_WRITE, NULL));
105 
106   ebml->srcpad = gst_object_ref (srcpad);
107   ebml->timestamp = GST_CLOCK_TIME_NONE;
108 
109   gst_ebml_write_reset (ebml);
110 
111   return ebml;
112 }
113 
114 
115 /**
116  * gst_ebml_write_reset:
117  * @ebml: a #GstEbmlWrite.
118  *
119  * Reset internal state of #GstEbmlWrite.
120  */
121 void
gst_ebml_write_reset(GstEbmlWrite * ebml)122 gst_ebml_write_reset (GstEbmlWrite * ebml)
123 {
124   ebml->pos = 0;
125   ebml->last_pos = G_MAXUINT64; /* force segment event */
126 
127   if (ebml->cache) {
128     gst_byte_writer_free (ebml->cache);
129     ebml->cache = NULL;
130   }
131 
132   if (ebml->caps) {
133     gst_caps_unref (ebml->caps);
134     ebml->caps = NULL;
135   }
136 
137   ebml->last_write_result = GST_FLOW_OK;
138   ebml->timestamp = GST_CLOCK_TIME_NONE;
139 }
140 
141 
142 /**
143  * gst_ebml_last_write_result:
144  * @ebml: a #GstEbmlWrite.
145  *
146  * Returns: GST_FLOW_OK if there was not write error since the last call of
147  *          gst_ebml_last_write_result or code of the error.
148  */
149 GstFlowReturn
gst_ebml_last_write_result(GstEbmlWrite * ebml)150 gst_ebml_last_write_result (GstEbmlWrite * ebml)
151 {
152   GstFlowReturn res = ebml->last_write_result;
153 
154   ebml->last_write_result = GST_FLOW_OK;
155 
156   return res;
157 }
158 
159 
160 void
gst_ebml_start_streamheader(GstEbmlWrite * ebml)161 gst_ebml_start_streamheader (GstEbmlWrite * ebml)
162 {
163   g_return_if_fail (ebml->streamheader == NULL);
164 
165   GST_DEBUG ("Starting streamheader at %" G_GUINT64_FORMAT, ebml->pos);
166   ebml->streamheader = gst_byte_writer_new_with_size (1000, FALSE);
167   ebml->streamheader_pos = ebml->pos;
168   ebml->writing_streamheader = TRUE;
169 }
170 
171 GstBuffer *
gst_ebml_stop_streamheader(GstEbmlWrite * ebml)172 gst_ebml_stop_streamheader (GstEbmlWrite * ebml)
173 {
174   GstBuffer *buffer;
175 
176   if (!ebml->streamheader)
177     return NULL;
178 
179   buffer = gst_byte_writer_free_and_get_buffer (ebml->streamheader);
180   ebml->streamheader = NULL;
181   GST_DEBUG ("Streamheader was size %" G_GSIZE_FORMAT,
182       gst_buffer_get_size (buffer));
183 
184   ebml->writing_streamheader = FALSE;
185   return buffer;
186 }
187 
188 /**
189  * gst_ebml_write_set_cache:
190  * @ebml: a #GstEbmlWrite.
191  * @size: size of the cache.
192  * Create a cache.
193  *
194  * The idea is that you use this for writing a lot
195  * of small elements. This will just "queue" all of
196  * them and they'll be pushed to the next element all
197  * at once. This saves memory and time for buffer
198  * allocation and init, and it looks better.
199  */
200 void
gst_ebml_write_set_cache(GstEbmlWrite * ebml,guint size)201 gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
202 {
203   g_return_if_fail (ebml->cache == NULL);
204 
205   GST_DEBUG ("Starting cache at %" G_GUINT64_FORMAT, ebml->pos);
206   ebml->cache = gst_byte_writer_new_with_size (size, FALSE);
207   ebml->cache_pos = ebml->pos;
208 }
209 
210 static gboolean
gst_ebml_writer_send_segment_event(GstEbmlWrite * ebml,guint64 new_pos)211 gst_ebml_writer_send_segment_event (GstEbmlWrite * ebml, guint64 new_pos)
212 {
213   GstSegment segment;
214   gboolean res;
215 
216   GST_INFO ("seeking to %" G_GUINT64_FORMAT, new_pos);
217 
218   gst_segment_init (&segment,
219       ebml->streamable ? GST_FORMAT_TIME : GST_FORMAT_BYTES);
220   segment.start = new_pos;
221   segment.stop = -1;
222   segment.position = 0;
223 
224   res = gst_pad_push_event (ebml->srcpad, gst_event_new_segment (&segment));
225 
226   if (!res)
227     GST_WARNING ("seek to %" G_GUINT64_FORMAT "failed", new_pos);
228 
229   return res;
230 }
231 
232 /**
233  * gst_ebml_write_flush_cache:
234  * @ebml:      a #GstEbmlWrite.
235  * @timestamp: timestamp of the buffer.
236  *
237  * Flush the cache.
238  */
239 void
gst_ebml_write_flush_cache(GstEbmlWrite * ebml,gboolean is_keyframe,GstClockTime timestamp)240 gst_ebml_write_flush_cache (GstEbmlWrite * ebml, gboolean is_keyframe,
241     GstClockTime timestamp)
242 {
243   GstBuffer *buffer;
244 
245   if (!ebml->cache)
246     return;
247 
248   buffer = gst_byte_writer_free_and_get_buffer (ebml->cache);
249   ebml->cache = NULL;
250   GST_DEBUG ("Flushing cache of size %" G_GSIZE_FORMAT,
251       gst_buffer_get_size (buffer));
252   GST_BUFFER_TIMESTAMP (buffer) = timestamp;
253   GST_BUFFER_OFFSET (buffer) = ebml->pos - gst_buffer_get_size (buffer);
254   GST_BUFFER_OFFSET_END (buffer) = ebml->pos;
255   if (ebml->last_write_result == GST_FLOW_OK) {
256     if (GST_BUFFER_OFFSET (buffer) != ebml->last_pos) {
257       gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buffer));
258       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
259     } else {
260       GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
261     }
262     if (ebml->writing_streamheader) {
263       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
264     } else {
265       GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_HEADER);
266     }
267     if (!is_keyframe) {
268       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
269     }
270     ebml->last_pos = ebml->pos;
271     ebml->last_write_result = gst_pad_push (ebml->srcpad, buffer);
272   } else {
273     gst_buffer_unref (buffer);
274   }
275 }
276 
277 
278 /**
279  * gst_ebml_write_element_new:
280  * @ebml: a #GstEbmlWrite.
281  * @size: size of the requested buffer.
282  *
283  * Create a buffer for one element. If there is
284  * a cache, use that instead.
285  *
286  * Returns: A new #GstBuffer.
287  */
288 static GstBuffer *
gst_ebml_write_element_new(GstEbmlWrite * ebml,GstMapInfo * map,guint size)289 gst_ebml_write_element_new (GstEbmlWrite * ebml, GstMapInfo * map, guint size)
290 {
291   /* Create new buffer of size + ID + length */
292   GstBuffer *buf;
293 
294   /* length, ID */
295   size += 12;
296 
297   buf = gst_buffer_new_and_alloc (size);
298   GST_BUFFER_TIMESTAMP (buf) = ebml->timestamp;
299 
300   /* FIXME unmap not possible */
301   gst_buffer_map (buf, map, GST_MAP_WRITE);
302 
303   return buf;
304 }
305 
306 
307 /**
308  * gst_ebml_write_element_id:
309  * @data_inout: Pointer to data pointer
310  * @id: Element ID that should be written.
311  *
312  * Write element ID into a buffer.
313  */
314 static void
gst_ebml_write_element_id(guint8 ** data_inout,guint32 id)315 gst_ebml_write_element_id (guint8 ** data_inout, guint32 id)
316 {
317   guint8 *data = *data_inout;
318   guint bytes = 4, mask = 0x10;
319 
320   /* get ID length */
321   while (!(id & (mask << ((bytes - 1) * 8))) && bytes > 0) {
322     mask <<= 1;
323     bytes--;
324   }
325 
326   /* if invalid ID, use dummy */
327   if (bytes == 0) {
328     GST_WARNING ("Invalid ID, voiding");
329     bytes = 1;
330     id = GST_EBML_ID_VOID;
331   }
332 
333   /* write out, BE */
334   *data_inout += bytes;
335   while (bytes--) {
336     data[bytes] = id & 0xff;
337     id >>= 8;
338   }
339 }
340 
341 
342 /**
343  * gst_ebml_write_element_size:
344  * @data_inout: Pointer to data pointer
345  * @size: Element length.
346  *
347  * Write element length into a buffer.
348  */
349 static void
gst_ebml_write_element_size(guint8 ** data_inout,guint64 size)350 gst_ebml_write_element_size (guint8 ** data_inout, guint64 size)
351 {
352   guint8 *data = *data_inout;
353   guint bytes = 1, mask = 0x80;
354 
355   if (size != GST_EBML_SIZE_UNKNOWN) {
356     /* how many bytes? - use mask-1 because an all-1 bitset is not allowed */
357     while (bytes <= 8 && (size >> ((bytes - 1) * 8)) >= (mask - 1)) {
358       mask >>= 1;
359       bytes++;
360     }
361 
362     /* if invalid size, use max. */
363     if (bytes > 8) {
364       GST_WARNING ("Invalid size, writing size unknown");
365       mask = 0x01;
366       bytes = 8;
367       /* Now here's a real FIXME: we cannot read those yet! */
368       size = GST_EBML_SIZE_UNKNOWN;
369     }
370   } else {
371     mask = 0x01;
372     bytes = 8;
373   }
374 
375   /* write out, BE, with length size marker */
376   *data_inout += bytes;
377   while (bytes-- > 0) {
378     data[bytes] = size & 0xff;
379     size >>= 8;
380     if (!bytes)
381       *data |= mask;
382   }
383 }
384 
385 
386 /**
387  * gst_ebml_write_element_data:
388  * @data_inout: Pointer to data pointer
389  * @write: Data that should be written.
390  * @length: Length of the data.
391  *
392  * Write element data into a buffer.
393  */
394 static void
gst_ebml_write_element_data(guint8 ** data_inout,guint8 * write,guint64 length)395 gst_ebml_write_element_data (guint8 ** data_inout, guint8 * write,
396     guint64 length)
397 {
398   memcpy (*data_inout, write, length);
399   *data_inout += length;
400 }
401 
402 
403 /**
404  * gst_ebml_write_element_push:
405  * @ebml: #GstEbmlWrite
406  * @buf: #GstBuffer to be written.
407  * @buf_data: Start of data to push from @buf (or NULL for whole buffer).
408  * @buf_data_end: Data pointer positioned after the last byte in @buf_data (or
409  * NULL for whole buffer).
410  *
411  * Write out buffer by moving it to the next element.
412  */
413 static void
gst_ebml_write_element_push(GstEbmlWrite * ebml,GstBuffer * buf,guint8 * buf_data,guint8 * buf_data_end)414 gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf,
415     guint8 * buf_data, guint8 * buf_data_end)
416 {
417   GstMapInfo map;
418   guint data_size;
419 
420   map.data = NULL;
421 
422   if (buf_data_end)
423     data_size = buf_data_end - buf_data;
424   else
425     data_size = gst_buffer_get_size (buf);
426 
427   ebml->pos += data_size;
428 
429   /* if there's no cache, then don't push it! */
430   if (ebml->writing_streamheader) {
431     if (!buf_data) {
432       gst_buffer_map (buf, &map, GST_MAP_READ);
433       buf_data = map.data;
434     }
435     if (!buf_data)
436       GST_WARNING ("Failed to map buffer");
437     else if (!gst_byte_writer_put_data (ebml->streamheader, buf_data,
438             data_size))
439       GST_WARNING ("Error writing data to streamheader");
440   }
441   if (ebml->cache) {
442     if (!buf_data) {
443       gst_buffer_map (buf, &map, GST_MAP_READ);
444       buf_data = map.data;
445     }
446     if (!buf_data)
447       GST_WARNING ("Failed to map buffer");
448     else if (!gst_byte_writer_put_data (ebml->cache, buf_data, data_size))
449       GST_WARNING ("Error writing data to cache");
450     if (map.data)
451       gst_buffer_unmap (buf, &map);
452     gst_buffer_unref (buf);
453     return;
454   }
455 
456   if (buf_data && map.data)
457     gst_buffer_unmap (buf, &map);
458 
459   if (ebml->last_write_result == GST_FLOW_OK) {
460     buf = gst_buffer_make_writable (buf);
461     GST_BUFFER_OFFSET (buf) = ebml->pos - data_size;
462     GST_BUFFER_OFFSET_END (buf) = ebml->pos;
463     if (ebml->writing_streamheader) {
464       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
465     } else {
466       GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_HEADER);
467     }
468     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
469 
470     if (GST_BUFFER_OFFSET (buf) != ebml->last_pos) {
471       gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buf));
472       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
473     } else {
474       GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
475     }
476     ebml->last_pos = ebml->pos;
477     ebml->last_write_result = gst_pad_push (ebml->srcpad, buf);
478   } else {
479     gst_buffer_unref (buf);
480   }
481 }
482 
483 
484 /**
485  * gst_ebml_write_seek:
486  * @ebml: #GstEbmlWrite
487  * @pos: Seek position.
488  *
489  * Seek.
490  */
491 void
gst_ebml_write_seek(GstEbmlWrite * ebml,guint64 pos)492 gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos)
493 {
494   if (ebml->writing_streamheader) {
495     GST_DEBUG ("wanting to seek to pos %" G_GUINT64_FORMAT, pos);
496     if (pos >= ebml->streamheader_pos &&
497         pos <= ebml->streamheader_pos + ebml->streamheader->parent.size) {
498       gst_byte_writer_set_pos (ebml->streamheader,
499           pos - ebml->streamheader_pos);
500       GST_DEBUG ("seeked in streamheader to position %" G_GUINT64_FORMAT,
501           pos - ebml->streamheader_pos);
502     } else {
503       GST_WARNING
504           ("we are writing streamheader still and seek is out of bounds");
505     }
506   }
507   /* Cache seeking. A bit dangerous, we assume the client writer
508    * knows what he's doing... */
509   if (ebml->cache) {
510     /* within bounds? */
511     if (pos >= ebml->cache_pos &&
512         pos <= ebml->cache_pos + ebml->cache->parent.size) {
513       GST_DEBUG ("seeking in cache to %" G_GUINT64_FORMAT, pos);
514       ebml->pos = pos;
515       gst_byte_writer_set_pos (ebml->cache, ebml->pos - ebml->cache_pos);
516       return;
517     } else {
518       GST_LOG ("Seek outside cache range. Clearing...");
519       gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
520     }
521   }
522 
523   GST_INFO ("scheduling seek to %" G_GUINT64_FORMAT, pos);
524   ebml->pos = pos;
525 }
526 
527 
528 /**
529  * gst_ebml_write_get_uint_size:
530  * @num: Number to be encoded.
531  *
532  * Get number of bytes needed to write a uint.
533  *
534  * Returns: Encoded uint length.
535  */
536 static guint
gst_ebml_write_get_uint_size(guint64 num)537 gst_ebml_write_get_uint_size (guint64 num)
538 {
539   guint size = 1;
540 
541   /* get size */
542   while (size < 8 && num >= (G_GINT64_CONSTANT (1) << (size * 8))) {
543     size++;
544   }
545 
546   return size;
547 }
548 
549 
550 /**
551  * gst_ebml_write_set_uint:
552  * @data_inout: Pointer to data pointer
553  * @num: Number to be written.
554  * @size: Encoded number length.
555  *
556  * Write an uint into a buffer.
557  */
558 static void
gst_ebml_write_set_uint(guint8 ** data_inout,guint64 num,guint size)559 gst_ebml_write_set_uint (guint8 ** data_inout, guint64 num, guint size)
560 {
561   guint8 *data = *data_inout;
562 
563   *data_inout += size;
564 
565   while (size-- > 0) {
566     data[size] = num & 0xff;
567     num >>= 8;
568   }
569 }
570 
571 
572 /**
573  * gst_ebml_write_uint:
574  * @ebml: #GstEbmlWrite
575  * @id: Element ID.
576  * @num: Number to be written.
577  *
578  * Write uint element.
579  */
580 void
gst_ebml_write_uint(GstEbmlWrite * ebml,guint32 id,guint64 num)581 gst_ebml_write_uint (GstEbmlWrite * ebml, guint32 id, guint64 num)
582 {
583   GstBuffer *buf;
584   guint8 *data_start, *data_end;
585   guint size = gst_ebml_write_get_uint_size (num);
586   GstMapInfo map;
587 
588   buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
589   data_end = data_start = map.data;
590 
591   /* write */
592   gst_ebml_write_element_id (&data_end, id);
593   gst_ebml_write_element_size (&data_end, size);
594   gst_ebml_write_set_uint (&data_end, num, size);
595   gst_buffer_unmap (buf, &map);
596   gst_buffer_set_size (buf, (data_end - data_start));
597 
598   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
599 }
600 
601 
602 /**
603  * gst_ebml_write_sint:
604  * @ebml: #GstEbmlWrite
605  * @id: Element ID.
606  * @num: Number to be written.
607  *
608  * Write sint element.
609  */
610 void
gst_ebml_write_sint(GstEbmlWrite * ebml,guint32 id,gint64 num)611 gst_ebml_write_sint (GstEbmlWrite * ebml, guint32 id, gint64 num)
612 {
613   GstBuffer *buf;
614   guint8 *data_start, *data_end;
615   GstMapInfo map;
616 
617   /* if the signed number is on the edge of a extra-byte,
618    * then we'll fall over when detecting it. Example: if I
619    * have a number (-)0x8000 (G_MINSHORT), then my abs()<<1
620    * will be 0x10000; this is G_MAXUSHORT+1! So: if (<0) -1. */
621   guint64 unum = (num < 0 ? (-num - 1) << 1 : num << 1);
622   guint size = gst_ebml_write_get_uint_size (unum);
623 
624   buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
625   data_end = data_start = map.data;
626 
627   /* make unsigned */
628   if (num >= 0) {
629     unum = num;
630   } else {
631     unum = ((guint64) 0x80) << ((size - 1) * 8);
632     unum += num;
633     unum |= ((guint64) 0x80) << ((size - 1) * 8);
634   }
635 
636   /* write */
637   gst_ebml_write_element_id (&data_end, id);
638   gst_ebml_write_element_size (&data_end, size);
639   gst_ebml_write_set_uint (&data_end, unum, size);
640   gst_buffer_unmap (buf, &map);
641   gst_buffer_set_size (buf, (data_end - data_start));
642 
643   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
644 }
645 
646 
647 /**
648  * gst_ebml_write_float:
649  * @ebml: #GstEbmlWrite
650  * @id: Element ID.
651  * @num: Number to be written.
652  *
653  * Write float element.
654  */
655 void
gst_ebml_write_float(GstEbmlWrite * ebml,guint32 id,gdouble num)656 gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num)
657 {
658   GstBuffer *buf;
659   GstMapInfo map;
660   guint8 *data_start, *data_end;
661 
662   buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
663   data_end = data_start = map.data;
664 
665   gst_ebml_write_element_id (&data_end, id);
666   gst_ebml_write_element_size (&data_end, 8);
667   num = GDOUBLE_TO_BE (num);
668   gst_ebml_write_element_data (&data_end, (guint8 *) & num, 8);
669   gst_buffer_unmap (buf, &map);
670   gst_buffer_set_size (buf, (data_end - data_start));
671 
672   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
673 }
674 
675 
676 /**
677  * gst_ebml_write_ascii:
678  * @ebml: #GstEbmlWrite
679  * @id: Element ID.
680  * @str: String to be written.
681  *
682  * Write string element.
683  */
684 void
gst_ebml_write_ascii(GstEbmlWrite * ebml,guint32 id,const gchar * str)685 gst_ebml_write_ascii (GstEbmlWrite * ebml, guint32 id, const gchar * str)
686 {
687   gint len = strlen (str) + 1;  /* add trailing '\0' */
688   GstBuffer *buf;
689   GstMapInfo map;
690   guint8 *data_start, *data_end;
691 
692   buf = gst_ebml_write_element_new (ebml, &map, len);
693   data_end = data_start = map.data;
694 
695   gst_ebml_write_element_id (&data_end, id);
696   gst_ebml_write_element_size (&data_end, len);
697   gst_ebml_write_element_data (&data_end, (guint8 *) str, len);
698   gst_buffer_unmap (buf, &map);
699   gst_buffer_set_size (buf, (data_end - data_start));
700 
701   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
702 }
703 
704 
705 /**
706  * gst_ebml_write_utf8:
707  * @ebml: #GstEbmlWrite
708  * @id: Element ID.
709  * @str: String to be written.
710  *
711  * Write utf8 encoded string element.
712  */
713 void
gst_ebml_write_utf8(GstEbmlWrite * ebml,guint32 id,const gchar * str)714 gst_ebml_write_utf8 (GstEbmlWrite * ebml, guint32 id, const gchar * str)
715 {
716   gst_ebml_write_ascii (ebml, id, str);
717 }
718 
719 
720 /**
721  * gst_ebml_write_date:
722  * @ebml: #GstEbmlWrite
723  * @id: Element ID.
724  * @date: Date in seconds since the unix epoch.
725  *
726  * Write date element.
727  */
728 void
gst_ebml_write_date(GstEbmlWrite * ebml,guint32 id,gint64 date)729 gst_ebml_write_date (GstEbmlWrite * ebml, guint32 id, gint64 date)
730 {
731   gst_ebml_write_sint (ebml, id, (date - GST_EBML_DATE_OFFSET) * GST_SECOND);
732 }
733 
734 /**
735  * gst_ebml_write_master_start:
736  * @ebml: #GstEbmlWrite
737  * @id: Element ID.
738  *
739  * Start wiriting mater element.
740  *
741  * Master writing is annoying. We use a size marker of
742  * the max. allowed length, so that we can later fill it
743  * in validly.
744  *
745  * Returns: Master starting position.
746  */
747 guint64
gst_ebml_write_master_start(GstEbmlWrite * ebml,guint32 id)748 gst_ebml_write_master_start (GstEbmlWrite * ebml, guint32 id)
749 {
750   guint64 pos = ebml->pos;
751   GstBuffer *buf;
752   GstMapInfo map;
753   guint8 *data_start, *data_end;
754 
755   buf = gst_ebml_write_element_new (ebml, &map, 0);
756   data_end = data_start = map.data;
757 
758   gst_ebml_write_element_id (&data_end, id);
759   pos += data_end - data_start;
760   gst_ebml_write_element_size (&data_end, GST_EBML_SIZE_UNKNOWN);
761   gst_buffer_unmap (buf, &map);
762   gst_buffer_set_size (buf, (data_end - data_start));
763 
764   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
765 
766   return pos;
767 }
768 
769 
770 /**
771  * gst_ebml_write_master_finish_full:
772  * @ebml: #GstEbmlWrite
773  * @startpos: Master starting position.
774  *
775  * Finish writing master element.  Size of master element is difference between
776  * current position and the element start, and @extra_size added to this.
777  */
778 void
gst_ebml_write_master_finish_full(GstEbmlWrite * ebml,guint64 startpos,guint64 extra_size)779 gst_ebml_write_master_finish_full (GstEbmlWrite * ebml, guint64 startpos,
780     guint64 extra_size)
781 {
782   guint64 pos = ebml->pos;
783   guint8 *data = g_malloc (8);
784   GstBuffer *buf = gst_buffer_new_wrapped (data, 8);
785 
786   gst_ebml_write_seek (ebml, startpos);
787 
788   GST_WRITE_UINT64_BE (data,
789       (G_GINT64_CONSTANT (1) << 56) | (pos - startpos - 8 + extra_size));
790 
791   gst_ebml_write_element_push (ebml, buf, NULL, NULL);
792   gst_ebml_write_seek (ebml, pos);
793 }
794 
795 void
gst_ebml_write_master_finish(GstEbmlWrite * ebml,guint64 startpos)796 gst_ebml_write_master_finish (GstEbmlWrite * ebml, guint64 startpos)
797 {
798   gst_ebml_write_master_finish_full (ebml, startpos, 0);
799 }
800 
801 /**
802  * gst_ebml_write_binary:
803  * @ebml: #GstEbmlWrite
804  * @id: Element ID.
805  * @binary: Data to be written.
806  * @length: Length of the data
807  *
808  * Write an element with binary data.
809  */
810 void
gst_ebml_write_binary(GstEbmlWrite * ebml,guint32 id,guint8 * binary,guint64 length)811 gst_ebml_write_binary (GstEbmlWrite * ebml,
812     guint32 id, guint8 * binary, guint64 length)
813 {
814   GstBuffer *buf;
815   GstMapInfo map;
816   guint8 *data_start, *data_end;
817 
818   buf = gst_ebml_write_element_new (ebml, &map, length);
819   data_end = data_start = map.data;
820 
821   gst_ebml_write_element_id (&data_end, id);
822   gst_ebml_write_element_size (&data_end, length);
823   gst_ebml_write_element_data (&data_end, binary, length);
824   gst_buffer_unmap (buf, &map);
825   gst_buffer_set_size (buf, (data_end - data_start));
826 
827   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
828 }
829 
830 
831 /**
832  * gst_ebml_write_buffer_header:
833  * @ebml: #GstEbmlWrite
834  * @id: Element ID.
835  * @length: Length of the data
836  *
837  * Write header of the binary element (use with gst_ebml_write_buffer function).
838  *
839  * For things like video frames and audio samples,
840  * you want to use this function, as it doesn't have
841  * the overhead of memcpy() that other functions
842  * such as write_binary() do have.
843  */
844 void
gst_ebml_write_buffer_header(GstEbmlWrite * ebml,guint32 id,guint64 length)845 gst_ebml_write_buffer_header (GstEbmlWrite * ebml, guint32 id, guint64 length)
846 {
847   GstBuffer *buf;
848   GstMapInfo map;
849   guint8 *data_start, *data_end;
850 
851   buf = gst_ebml_write_element_new (ebml, &map, 0);
852   data_end = data_start = map.data;
853 
854   gst_ebml_write_element_id (&data_end, id);
855   gst_ebml_write_element_size (&data_end, length);
856   gst_buffer_unmap (buf, &map);
857   gst_buffer_set_size (buf, (data_end - data_start));
858 
859   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
860 }
861 
862 
863 /**
864  * gst_ebml_write_buffer:
865  * @ebml: #GstEbmlWrite
866  * @buf: #GstBuffer cointaining the data.
867  *
868  * Write  binary element (see gst_ebml_write_buffer_header).
869  */
870 void
gst_ebml_write_buffer(GstEbmlWrite * ebml,GstBuffer * buf)871 gst_ebml_write_buffer (GstEbmlWrite * ebml, GstBuffer * buf)
872 {
873   gst_ebml_write_element_push (ebml, buf, NULL, NULL);
874 }
875 
876 
877 /**
878  * gst_ebml_replace_uint:
879  * @ebml: #GstEbmlWrite
880  * @pos: Position of the uint that should be replaced.
881  * @num: New value.
882  *
883  * Replace uint with a new value.
884  *
885  * When replacing a uint, we assume that it is *always*
886  * 8-byte, since that's the safest guess we can do. This
887  * is just for simplicity.
888  *
889  * FIXME: this function needs to be replaced with something
890  * proper. This is a crude hack.
891  */
892 void
gst_ebml_replace_uint(GstEbmlWrite * ebml,guint64 pos,guint64 num)893 gst_ebml_replace_uint (GstEbmlWrite * ebml, guint64 pos, guint64 num)
894 {
895   guint64 oldpos = ebml->pos;
896   guint8 *data_start, *data_end;
897   GstBuffer *buf;
898 
899   data_start = g_malloc (8);
900   data_end = data_start;
901   buf = gst_buffer_new_wrapped (data_start, 8);
902 
903   gst_ebml_write_seek (ebml, pos);
904   gst_ebml_write_set_uint (&data_end, num, 8);
905 
906   gst_ebml_write_element_push (ebml, buf, data_start, data_end);
907   gst_ebml_write_seek (ebml, oldpos);
908 }
909 
910 /**
911  * gst_ebml_write_header:
912  * @ebml: #GstEbmlWrite
913  * @doctype: Document type.
914  * @version: Document type version.
915  *
916  * Write EBML header.
917  */
918 void
gst_ebml_write_header(GstEbmlWrite * ebml,const gchar * doctype,guint version)919 gst_ebml_write_header (GstEbmlWrite * ebml, const gchar * doctype,
920     guint version)
921 {
922   guint64 pos;
923 
924   /* write the basic EBML header */
925   gst_ebml_write_set_cache (ebml, 0x40);
926   pos = gst_ebml_write_master_start (ebml, GST_EBML_ID_HEADER);
927 #if (GST_EBML_VERSION != 1)
928   gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLVERSION, GST_EBML_VERSION);
929   gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLREADVERSION, GST_EBML_VERSION);
930 #endif
931 #if 0
932   /* we don't write these until they're "non-default" (never!) */
933   gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLMAXIDLENGTH, sizeof (guint32));
934   gst_ebml_write_uint (ebml, GST_EBML_ID_EBMLMAXSIZELENGTH, sizeof (guint64));
935 #endif
936   gst_ebml_write_ascii (ebml, GST_EBML_ID_DOCTYPE, doctype);
937   gst_ebml_write_uint (ebml, GST_EBML_ID_DOCTYPEVERSION, version);
938   gst_ebml_write_uint (ebml, GST_EBML_ID_DOCTYPEREADVERSION, version);
939   gst_ebml_write_master_finish (ebml, pos);
940   gst_ebml_write_flush_cache (ebml, FALSE, 0);
941 }
942