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 nanoseconds 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);
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 containing 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