GitLab will be down for maintenance this Sunday 13th June, from approx 7-11am UTC. This is for a PostgreSQL migration. See the tracker issue for more informations.

ebml-write.c 22.8 KB
Newer Older
1 2
/* GStreamer EBML I/O
 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 * (c) 2005 Michal Benes <michal.benes@xeris.cz>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
19 20
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
21 22 23 24 25 26 27 28 29 30 31 32
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>

#include "ebml-write.h"
#include "ebml-ids.h"


33 34
GST_DEBUG_CATEGORY_STATIC (gst_ebml_write_debug);
#define GST_CAT_DEFAULT gst_ebml_write_debug
35

René Stadler's avatar
René Stadler committed
36
#define _do_init \
37
      GST_DEBUG_CATEGORY_INIT (gst_ebml_write_debug, "ebmlwrite", 0, "Write EBML structured data")
René Stadler's avatar
René Stadler committed
38 39
#define parent_class gst_ebml_write_parent_class
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
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
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
René Stadler's avatar
René Stadler committed
53
gst_ebml_write_init (GstEbmlWrite * ebml)
54 55 56
{
  ebml->srcpad = NULL;
  ebml->pos = 0;
René Stadler's avatar
René Stadler committed
57
  ebml->last_pos = G_MAXUINT64; /* force segment event */
58 59

  ebml->cache = NULL;
60 61 62
  ebml->streamheader = NULL;
  ebml->streamheader_pos = 0;
  ebml->writing_streamheader = FALSE;
63
  ebml->caps = NULL;
64 65 66 67 68 69 70 71 72
}

static void
gst_ebml_write_finalize (GObject * object)
{
  GstEbmlWrite *ebml = GST_EBML_WRITE (object);

  gst_object_unref (ebml->srcpad);

73
  if (ebml->cache) {
74
    gst_byte_writer_free (ebml->cache);
75 76 77
    ebml->cache = NULL;
  }

78 79 80 81 82
  if (ebml->streamheader) {
    gst_byte_writer_free (ebml->streamheader);
    ebml->streamheader = NULL;
  }

83 84 85 86
  if (ebml->caps) {
    gst_caps_unref (ebml->caps);
    ebml->caps = NULL;
  }
87

René Stadler's avatar
René Stadler committed
88
  G_OBJECT_CLASS (parent_class)->finalize (object);
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
}


/**
 * 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);
107
  ebml->timestamp = GST_CLOCK_TIME_NONE;
108 109 110 111

  gst_ebml_write_reset (ebml);

  return ebml;
112 113
}

114 115 116 117 118 119 120 121 122

/**
 * gst_ebml_write_reset:
 * @ebml: a #GstEbmlWrite.
 *
 * Reset internal state of #GstEbmlWrite.
 */
void
gst_ebml_write_reset (GstEbmlWrite * ebml)
123
{
124
  ebml->pos = 0;
René Stadler's avatar
René Stadler committed
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 133 134 135 136

  if (ebml->caps) {
    gst_caps_unref (ebml->caps);
    ebml->caps = NULL;
  }

137
  ebml->last_write_result = GST_FLOW_OK;
138
  ebml->timestamp = GST_CLOCK_TIME_NONE;
139
}
140 141


142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/**
 * 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;
157 158
}

159

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
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;
181 182
  GST_DEBUG ("Streamheader was size %" G_GSIZE_FORMAT,
      gst_buffer_get_size (buffer));
183 184 185 186 187

  ebml->writing_streamheader = FALSE;
  return buffer;
}

188 189 190 191 192
/**
 * gst_ebml_write_set_cache:
 * @ebml: a #GstEbmlWrite.
 * @size: size of the cache.
 * Create a cache.
193 194 195 196 197 198 199 200
 *
 * 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
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
201
gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
202 203 204
{
  g_return_if_fail (ebml->cache == NULL);

205 206 207
  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;
208 209
}

210
static gboolean
René Stadler's avatar
René Stadler committed
211
gst_ebml_writer_send_segment_event (GstEbmlWrite * ebml, guint64 new_pos)
212
{
René Stadler's avatar
René Stadler committed
213
  GstSegment segment;
214 215 216 217
  gboolean res;

  GST_INFO ("seeking to %" G_GUINT64_FORMAT, new_pos);

René Stadler's avatar
René Stadler committed
218 219 220 221 222 223
  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));
224 225 226 227 228 229 230

  if (!res)
    GST_WARNING ("seek to %" G_GUINT64_FORMAT "failed", new_pos);

  return res;
}

231 232
/**
 * gst_ebml_write_flush_cache:
233 234
 * @ebml:      a #GstEbmlWrite.
 * @timestamp: timestamp of the buffer.
235 236 237
 *
 * Flush the cache.
 */
238
void
239 240
gst_ebml_write_flush_cache (GstEbmlWrite * ebml, gboolean is_keyframe,
    GstClockTime timestamp)
241
{
242 243
  GstBuffer *buffer;

244 245 246
  if (!ebml->cache)
    return;

247 248
  buffer = gst_byte_writer_free_and_get_buffer (ebml->cache);
  ebml->cache = NULL;
249 250
  GST_DEBUG ("Flushing cache of size %" G_GSIZE_FORMAT,
      gst_buffer_get_size (buffer));
251
  GST_BUFFER_TIMESTAMP (buffer) = timestamp;
René Stadler's avatar
René Stadler committed
252
  GST_BUFFER_OFFSET (buffer) = ebml->pos - gst_buffer_get_size (buffer);
253
  GST_BUFFER_OFFSET_END (buffer) = ebml->pos;
254
  if (ebml->last_write_result == GST_FLOW_OK) {
255
    if (GST_BUFFER_OFFSET (buffer) != ebml->last_pos) {
René Stadler's avatar
René Stadler committed
256
      gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buffer));
257
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
258
    }
259
    if (ebml->writing_streamheader) {
Wim Taymans's avatar
Wim Taymans committed
260
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
261
    }
262 263 264
    if (!is_keyframe) {
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
    }
265
    ebml->last_pos = ebml->pos;
266 267 268
    ebml->last_write_result = gst_pad_push (ebml->srcpad, buffer);
  } else {
    gst_buffer_unref (buffer);
269
  }
270 271
}

272 273 274 275 276 277 278

/**
 * gst_ebml_write_element_new:
 * @ebml: a #GstEbmlWrite.
 * @size: size of the requested buffer.
 *
 * Create a buffer for one element. If there is
279
 * a cache, use that instead.
280 281
 *
 * Returns: A new #GstBuffer.
282 283
 */
static GstBuffer *
284
gst_ebml_write_element_new (GstEbmlWrite * ebml, GstMapInfo * map, guint size)
285 286 287 288 289 290 291 292
{
  /* Create new buffer of size + ID + length */
  GstBuffer *buf;

  /* length, ID */
  size += 12;

  buf = gst_buffer_new_and_alloc (size);
293
  GST_BUFFER_TIMESTAMP (buf) = ebml->timestamp;
294

Wim Taymans's avatar
Wim Taymans committed
295
  /* FIXME unmap not possible */
296
  gst_buffer_map (buf, map, GST_MAP_WRITE);
297

298 299 300
  return buf;
}

301 302 303

/**
 * gst_ebml_write_element_id:
304
 * @data_inout: Pointer to data pointer
305 306
 * @id: Element ID that should be written.
 * 
307 308 309
 * Write element ID into a buffer.
 */
static void
310
gst_ebml_write_element_id (guint8 ** data_inout, guint32 id)
311
{
312
  guint8 *data = *data_inout;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
  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 */
329
  *data_inout += bytes;
330 331 332 333 334 335
  while (bytes--) {
    data[bytes] = id & 0xff;
    id >>= 8;
  }
}

336 337 338

/**
 * gst_ebml_write_element_size:
339
 * @data_inout: Pointer to data pointer
340
 * @size: Element length.
341
 *
342 343 344
 * Write element length into a buffer.
 */
static void
345
gst_ebml_write_element_size (guint8 ** data_inout, guint64 size)
346
{
347
  guint8 *data = *data_inout;
348 349
  guint bytes = 1, mask = 0x80;

350 351 352 353 354 355
  if (size != GST_EBML_SIZE_UNKNOWN) {
    /* how many bytes? - use mask-1 because an all-1 bitset is not allowed */
    while ((size >> ((bytes - 1) * 8)) >= (mask - 1) && bytes <= 8) {
      mask >>= 1;
      bytes++;
    }
356

357 358 359 360 361 362 363 364 365
    /* 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 {
366 367 368 369 370
    mask = 0x01;
    bytes = 8;
  }

  /* write out, BE, with length size marker */
371
  *data_inout += bytes;
372 373 374 375 376 377 378 379
  while (bytes-- > 0) {
    data[bytes] = size & 0xff;
    size >>= 8;
    if (!bytes)
      *data |= mask;
  }
}

380 381 382

/**
 * gst_ebml_write_element_data:
383
 * @data_inout: Pointer to data pointer
384 385 386
 * @write: Data that should be written.
 * @length: Length of the data.
 *
387 388 389
 * Write element data into a buffer.
 */
static void
390 391
gst_ebml_write_element_data (guint8 ** data_inout, guint8 * write,
    guint64 length)
392
{
393 394
  memcpy (*data_inout, write, length);
  *data_inout += length;
395 396
}

397 398 399 400 401

/**
 * gst_ebml_write_element_push:
 * @ebml: #GstEbmlWrite
 * @buf: #GstBuffer to be written.
402 403 404
 * @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).
405
 * 
406 407 408
 * Write out buffer by moving it to the next element.
 */
static void
409 410
gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf,
    guint8 * buf_data, guint8 * buf_data_end)
411
{
Wim Taymans's avatar
Wim Taymans committed
412
  GstMapInfo map;
413 414
  guint data_size;

415 416
  map.data = NULL;

René Stadler's avatar
René Stadler committed
417
  if (buf_data_end)
418
    data_size = buf_data_end - buf_data;
René Stadler's avatar
René Stadler committed
419 420
  else
    data_size = gst_buffer_get_size (buf);
421 422 423 424

  ebml->pos += data_size;

  /* if there's no cache, then don't push it! */
425
  if (ebml->writing_streamheader) {
Wim Taymans's avatar
Wim Taymans committed
426
    if (!buf_data) {
427
      gst_buffer_map (buf, &map, GST_MAP_READ);
Wim Taymans's avatar
Wim Taymans committed
428 429
      buf_data = map.data;
    }
430 431 432 433
    if (!buf_data)
      GST_WARNING ("Failed to map buffer");
    else if (!gst_byte_writer_put_data (ebml->streamheader, buf_data,
            data_size))
434
      GST_WARNING ("Error writing data to streamheader");
435
  }
436
  if (ebml->cache) {
Wim Taymans's avatar
Wim Taymans committed
437
    if (!buf_data) {
438
      gst_buffer_map (buf, &map, GST_MAP_READ);
Wim Taymans's avatar
Wim Taymans committed
439 440
      buf_data = map.data;
    }
441 442 443
    if (!buf_data)
      GST_WARNING ("Failed to map buffer");
    else if (!gst_byte_writer_put_data (ebml->cache, buf_data, data_size))
444
      GST_WARNING ("Error writing data to cache");
445 446
    if (map.data)
      gst_buffer_unmap (buf, &map);
447
    gst_buffer_unref (buf);
448 449 450
    return;
  }

451
  if (buf_data && map.data)
Wim Taymans's avatar
Wim Taymans committed
452
    gst_buffer_unmap (buf, &map);
René Stadler's avatar
René Stadler committed
453

454
  if (ebml->last_write_result == GST_FLOW_OK) {
René Stadler's avatar
René Stadler committed
455
    buf = gst_buffer_make_writable (buf);
456
    GST_BUFFER_OFFSET (buf) = ebml->pos - data_size;
457
    GST_BUFFER_OFFSET_END (buf) = ebml->pos;
458
    if (ebml->writing_streamheader) {
Wim Taymans's avatar
Wim Taymans committed
459
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
460
    }
461
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
462 463

    if (GST_BUFFER_OFFSET (buf) != ebml->last_pos) {
René Stadler's avatar
René Stadler committed
464
      gst_ebml_writer_send_segment_event (ebml, GST_BUFFER_OFFSET (buf));
465 466 467
      GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    }
    ebml->last_pos = ebml->pos;
468
    ebml->last_write_result = gst_pad_push (ebml->srcpad, buf);
469
  } else {
470
    gst_buffer_unref (buf);
471
  }
472 473
}

474 475 476 477 478 479

/**
 * gst_ebml_write_seek:
 * @ebml: #GstEbmlWrite
 * @pos: Seek position.
 * 
480 481 482
 * Seek.
 */
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos)
484
{
485 486 487 488 489 490 491 492 493 494 495 496 497
  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");
    }
  }
498 499 500 501
  /* Cache seeking. A bit dangerous, we assume the client writer
   * knows what he's doing... */
  if (ebml->cache) {
    /* within bounds? */
502 503
    if (pos >= ebml->cache_pos &&
        pos <= ebml->cache_pos + ebml->cache->parent.size) {
504
      GST_DEBUG ("seeking in cache to %" G_GUINT64_FORMAT, pos);
505
      ebml->pos = pos;
506 507
      gst_byte_writer_set_pos (ebml->cache, ebml->pos - ebml->cache_pos);
      return;
508 509
    } else {
      GST_LOG ("Seek outside cache range. Clearing...");
510
      gst_ebml_write_flush_cache (ebml, FALSE, GST_CLOCK_TIME_NONE);
511 512 513
    }
  }

514
  GST_INFO ("scheduling seek to %" G_GUINT64_FORMAT, pos);
515 516 517 518
  ebml->pos = pos;
}


519 520 521 522 523 524 525 526
/**
 * gst_ebml_write_get_uint_size:
 * @num: Number to be encoded.
 * 
 * Get number of bytes needed to write a uint.
 *
 * Returns: Encoded uint length.
 */
527 528 529 530 531 532
static guint
gst_ebml_write_get_uint_size (guint64 num)
{
  guint size = 1;

  /* get size */
533
  while (num >= (G_GINT64_CONSTANT (1) << (size * 8)) && size < 8) {
534 535 536 537 538 539 540
    size++;
  }

  return size;
}


541 542
/**
 * gst_ebml_write_set_uint:
543
 * @data_inout: Pointer to data pointer
544 545 546
 * @num: Number to be written.
 * @size: Encoded number length.
 *
547 548 549
 * Write an uint into a buffer.
 */
static void
550
gst_ebml_write_set_uint (guint8 ** data_inout, guint64 num, guint size)
551
{
552 553 554
  guint8 *data = *data_inout;

  *data_inout += size;
555 556 557 558 559 560 561 562

  while (size-- > 0) {
    data[size] = num & 0xff;
    num >>= 8;
  }
}


563 564 565 566 567 568 569 570
/**
 * gst_ebml_write_uint:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @num: Number to be written.
 *
 * Write uint element.
 */
571
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
572
gst_ebml_write_uint (GstEbmlWrite * ebml, guint32 id, guint64 num)
573
{
574 575
  GstBuffer *buf;
  guint8 *data_start, *data_end;
576
  guint size = gst_ebml_write_get_uint_size (num);
577
  GstMapInfo map;
578

579 580
  buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
  data_end = data_start = map.data;
581

582
  /* write */
583 584 585
  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);
586
  gst_buffer_unmap (buf, &map);
587
  gst_buffer_set_size (buf, (data_end - data_start));
588

589
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
590 591
}

592 593 594 595 596 597 598 599 600

/**
 * gst_ebml_write_sint:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @num: Number to be written.
 *
 * Write sint element.
 */
601
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
602
gst_ebml_write_sint (GstEbmlWrite * ebml, guint32 id, gint64 num)
603
{
604 605
  GstBuffer *buf;
  guint8 *data_start, *data_end;
606 607
  GstMapInfo map;

608 609 610 611 612 613 614
  /* 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);

615 616
  buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
  data_end = data_start = map.data;
617

618
  /* make unsigned */
619 620 621
  if (num >= 0) {
    unum = num;
  } else {
622
    unum = ((guint64) 0x80) << (size - 1);
623
    unum += num;
624
    unum |= ((guint64) 0x80) << (size - 1);
625
  }
626 627

  /* write */
628 629 630
  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);
631
  gst_buffer_unmap (buf, &map);
632
  gst_buffer_set_size (buf, (data_end - data_start));
633

634
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
635 636
}

637 638 639 640 641 642 643 644 645

/**
 * gst_ebml_write_float:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @num: Number to be written.
 *
 * Write float element.
 */
646
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
647
gst_ebml_write_float (GstEbmlWrite * ebml, guint32 id, gdouble num)
648
{
649
  GstBuffer *buf;
650
  GstMapInfo map;
651 652
  guint8 *data_start, *data_end;

653 654
  buf = gst_ebml_write_element_new (ebml, &map, sizeof (num));
  data_end = data_start = map.data;
655

656 657
  gst_ebml_write_element_id (&data_end, id);
  gst_ebml_write_element_size (&data_end, 8);
658
  num = GDOUBLE_TO_BE (num);
659
  gst_ebml_write_element_data (&data_end, (guint8 *) & num, 8);
660
  gst_buffer_unmap (buf, &map);
661
  gst_buffer_set_size (buf, (data_end - data_start));
662

663
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
664 665
}

666 667 668 669 670 671 672 673 674

/**
 * gst_ebml_write_ascii:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @str: String to be written.
 *
 * Write string element.
 */
675
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
676
gst_ebml_write_ascii (GstEbmlWrite * ebml, guint32 id, const gchar * str)
677
{
678
  gint len = strlen (str) + 1;  /* add trailing '\0' */
679
  GstBuffer *buf;
680
  GstMapInfo map;
681 682
  guint8 *data_start, *data_end;

683 684
  buf = gst_ebml_write_element_new (ebml, &map, len);
  data_end = data_start = map.data;
685

686 687 688
  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);
689
  gst_buffer_unmap (buf, &map);
690
  gst_buffer_set_size (buf, (data_end - data_start));
691

692
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
693 694
}

695 696 697 698 699 700 701 702 703

/**
 * gst_ebml_write_utf8:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @str: String to be written.
 *
 * Write utf8 encoded string element.
 */
704
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
705
gst_ebml_write_utf8 (GstEbmlWrite * ebml, guint32 id, const gchar * str)
706 707 708 709
{
  gst_ebml_write_ascii (ebml, id, str);
}

710 711 712 713 714 715 716 717 718

/**
 * gst_ebml_write_date:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 * @date: Date in seconds since the unix epoch.
 *
 * Write date element.
 */
719
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
720
gst_ebml_write_date (GstEbmlWrite * ebml, guint32 id, gint64 date)
721
{
722
  gst_ebml_write_sint (ebml, id, (date - GST_EBML_DATE_OFFSET) * GST_SECOND);
723 724
}

725 726 727 728 729 730 731
/**
 * gst_ebml_write_master_start:
 * @ebml: #GstEbmlWrite
 * @id: Element ID.
 *
 * Start wiriting mater element.
 *
732 733 734
 * Master writing is annoying. We use a size marker of
 * the max. allowed length, so that we can later fill it
 * in validly. 
735 736
 *
 * Returns: Master starting position.
737 738
 */
guint64
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
739
gst_ebml_write_master_start (GstEbmlWrite * ebml, guint32 id)
740
{
741 742
  guint64 pos = ebml->pos;
  GstBuffer *buf;
743
  GstMapInfo map;
744
  guint8 *data_start, *data_end;
745

746 747
  buf = gst_ebml_write_element_new (ebml, &map, 0);
  data_end = data_start = map.data;
748 749 750 751

  gst_ebml_write_element_id (&data_end, id);
  pos += data_end - data_start;
  gst_ebml_write_element_size (&data_end, GST_EBML_SIZE_UNKNOWN);
752
  gst_buffer_unmap (buf, &map);
753
  gst_buffer_set_size (buf, (data_end - data_start));
754

755
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
756 757 758 759

  return pos;
}

760 761

/**
762
 * gst_ebml_write_master_finish_full:
763 764 765
 * @ebml: #GstEbmlWrite
 * @startpos: Master starting position.
 *
766 767
 * Finish writing master element.  Size of master element is difference between
 * current position and the element start, and @extra_size added to this.
768
 */
769
void
770 771
gst_ebml_write_master_finish_full (GstEbmlWrite * ebml, guint64 startpos,
    guint64 extra_size)
772 773
{
  guint64 pos = ebml->pos;
René Stadler's avatar
René Stadler committed
774 775
  guint8 *data = g_malloc (8);
  GstBuffer *buf = gst_buffer_new_wrapped (data, 8);
776 777

  gst_ebml_write_seek (ebml, startpos);
778

René Stadler's avatar
René Stadler committed
779
  GST_WRITE_UINT64_BE (data,
780
      (G_GINT64_CONSTANT (1) << 56) | (pos - startpos - 8 + extra_size));
781 782

  gst_ebml_write_element_push (ebml, buf, NULL, NULL);
783 784 785
  gst_ebml_write_seek (ebml, pos);
}

786 787 788 789 790
void
gst_ebml_write_master_finish (GstEbmlWrite * ebml, guint64 startpos)
{
  gst_ebml_write_master_finish_full (ebml, startpos, 0);
}
791 792 793 794 795 796 797 798 799 800

/**
 * 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.
 */
801
void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
802 803
gst_ebml_write_binary (GstEbmlWrite * ebml,
    guint32 id, guint8 * binary, guint64 length)
804
{
805
  GstBuffer *buf;
806
  GstMapInfo map;
807 808
  guint8 *data_start, *data_end;

809 810
  buf = gst_ebml_write_element_new (ebml, &map, length);
  data_end = data_start = map.data;
811

812 813 814
  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);
815
  gst_buffer_unmap (buf, &map);
816
  gst_buffer_set_size (buf, (data_end - data_start));
817

818
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
819 820
}

821 822 823 824 825 826 827 828 829

/**
 * 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).
 * 
830 831 832 833 834 835
 * 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
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836
gst_ebml_write_buffer_header (GstEbmlWrite * ebml, guint32 id, guint64 length)
837
{
838
  GstBuffer *buf;
839
  GstMapInfo map;
840
  guint8 *data_start, *data_end;
841

842 843
  buf = gst_ebml_write_element_new (ebml, &map, 0);
  data_end = data_start = map.data;
844 845 846

  gst_ebml_write_element_id (&data_end, id);
  gst_ebml_write_element_size (&data_end, length);
847
  gst_buffer_unmap (buf, &map);
848
  gst_buffer_set_size (buf, (data_end - data_start));
849

850
  gst_ebml_write_element_push (ebml, buf, data_start, data_end);
851 852
}

853 854 855 856

/**
 * gst_ebml_write_buffer:
 * @ebml: #GstEbmlWrite
857
 * @buf: #GstBuffer cointaining the data.
858 859 860
 *
 * Write  binary element (see gst_ebml_write_buffer_header).
 */
861
void
862
gst_ebml_write_buffer (GstEbmlWrite * ebml, GstBuffer * buf)
863
{
864
  gst_ebml_write_element_push (ebml, buf, NULL, NULL);
865 866
}

867 868 869 870 871 872 873 874 875

/**
 * gst_ebml_replace_uint:
 * @ebml: #GstEbmlWrite
 * @pos: Position of the uint that should be replaced.
 * @num: New value.
 *
 * Replace uint with a new value.
 * 
876 877 878 879 880 881 882 883
 * 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
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
884
gst_ebml_replace_uint (GstEbmlWrite * ebml, guint64 pos, guint64 num)
885 886
{
  guint64 oldpos = ebml->pos;
887
  guint8 *data_start, *data_end;
René Stadler's avatar
René Stadler committed
888
  GstBuffer *buf;
889

René Stadler's avatar
René Stadler committed
890
  data_start = g_malloc (8);
891
  data_end = data_start;
René Stadler's avatar
René Stadler committed
892
  buf = gst_buffer_new_wrapped (data_start, 8);
893