matroskamux: fix ebml write caching with bytewriter implementation

Also cache a bit more during header writing.

Fixes #619273.
This commit is contained in:
Mark Nauwelaerts 2010-05-20 14:23:07 +02:00
parent 7895ddbc38
commit 81bf657aa7
3 changed files with 27 additions and 55 deletions

View file

@ -62,7 +62,6 @@ gst_ebml_write_init (GstEbmlWrite * ebml, GstEbmlWriteClass * klass)
ebml->pos = 0; ebml->pos = 0;
ebml->cache = NULL; ebml->cache = NULL;
ebml->cache_size = 0;
} }
static void static void
@ -73,7 +72,7 @@ gst_ebml_write_finalize (GObject * object)
gst_object_unref (ebml->srcpad); gst_object_unref (ebml->srcpad);
if (ebml->cache) { if (ebml->cache) {
gst_buffer_unref (ebml->cache); gst_byte_writer_free (ebml->cache);
ebml->cache = NULL; ebml->cache = NULL;
} }
@ -116,10 +115,9 @@ gst_ebml_write_reset (GstEbmlWrite * ebml)
ebml->pos = 0; ebml->pos = 0;
if (ebml->cache) { if (ebml->cache) {
gst_buffer_unref (ebml->cache); gst_byte_writer_free (ebml->cache);
ebml->cache = NULL; ebml->cache = NULL;
} }
ebml->cache_size = 0;
ebml->last_write_result = GST_FLOW_OK; ebml->last_write_result = GST_FLOW_OK;
ebml->timestamp = GST_CLOCK_TIME_NONE; ebml->timestamp = GST_CLOCK_TIME_NONE;
ebml->need_newsegment = TRUE; ebml->need_newsegment = TRUE;
@ -159,16 +157,11 @@ gst_ebml_last_write_result (GstEbmlWrite * ebml)
void void
gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size) gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
{ {
/* FIXME: This is currently broken. I don't know why yet. */
return;
g_return_if_fail (ebml->cache == NULL); g_return_if_fail (ebml->cache == NULL);
ebml->cache = gst_buffer_new_and_alloc (size); GST_DEBUG ("Starting cache at %" G_GUINT64_FORMAT, ebml->pos);
ebml->cache_size = size; ebml->cache = gst_byte_writer_new_with_size (size, FALSE);
GST_BUFFER_SIZE (ebml->cache) = 0; ebml->cache_pos = ebml->pos;
GST_BUFFER_OFFSET (ebml->cache) = ebml->pos;
ebml->handled = 0;
} }
/** /**
@ -180,30 +173,27 @@ gst_ebml_write_set_cache (GstEbmlWrite * ebml, guint size)
void void
gst_ebml_write_flush_cache (GstEbmlWrite * ebml) gst_ebml_write_flush_cache (GstEbmlWrite * ebml)
{ {
GstBuffer *buffer;
if (!ebml->cache) if (!ebml->cache)
return; return;
/* this is very important. It may fail, in which case the client buffer = gst_byte_writer_free_and_get_buffer (ebml->cache);
* programmer didn't use the cache somewhere. That's fatal. */ ebml->cache = NULL;
g_assert (ebml->handled == GST_BUFFER_SIZE (ebml->cache)); GST_DEBUG ("Flushing cache of size %d", GST_BUFFER_SIZE (buffer));
g_assert (GST_BUFFER_SIZE (ebml->cache) +
GST_BUFFER_OFFSET (ebml->cache) == ebml->pos);
if (ebml->last_write_result == GST_FLOW_OK) { if (ebml->last_write_result == GST_FLOW_OK) {
if (ebml->need_newsegment) { if (ebml->need_newsegment) {
GstEvent *ev; GstEvent *ev;
g_assert (ebml->handled == 0);
ev = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0); ev = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
if (gst_pad_push_event (ebml->srcpad, ev)) if (gst_pad_push_event (ebml->srcpad, ev))
ebml->need_newsegment = FALSE; ebml->need_newsegment = FALSE;
} }
ebml->last_write_result = gst_pad_push (ebml->srcpad, ebml->cache); ebml->last_write_result = gst_pad_push (ebml->srcpad, buffer);
} else {
gst_buffer_unref (buffer);
} }
ebml->cache = NULL;
ebml->cache_size = 0;
ebml->handled = 0;
} }
@ -226,17 +216,6 @@ gst_ebml_write_element_new (GstEbmlWrite * ebml, guint size)
/* length, ID */ /* length, ID */
size += 12; size += 12;
/* prefer cache */
if (ebml->cache) {
if (ebml->cache_size - GST_BUFFER_SIZE (ebml->cache) < size) {
GST_LOG ("Cache available, but too small. Clearing...");
gst_ebml_write_flush_cache (ebml);
} else {
return ebml->cache;
}
}
/* else, use a one-element buffer. This is slower */
buf = gst_buffer_new_and_alloc (size); buf = gst_buffer_new_and_alloc (size);
GST_BUFFER_SIZE (buf) = 0; GST_BUFFER_SIZE (buf) = 0;
GST_BUFFER_TIMESTAMP (buf) = ebml->timestamp; GST_BUFFER_TIMESTAMP (buf) = ebml->timestamp;
@ -352,16 +331,14 @@ gst_ebml_write_element_data (GstBuffer * buf, guint8 * write, guint64 length)
static void static void
gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf) gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf)
{ {
guint data_size = GST_BUFFER_SIZE (buf) - ebml->handled; guint data_size = GST_BUFFER_SIZE (buf);
ebml->pos += data_size; ebml->pos += data_size;
if (buf == ebml->cache) {
ebml->handled += data_size;
}
/* if there's no cache, then don't push it! */ /* if there's no cache, then don't push it! */
if (ebml->cache) { if (ebml->cache) {
g_assert (buf == ebml->cache); gst_byte_writer_put_data (ebml->cache, GST_BUFFER_DATA (buf), data_size);
gst_buffer_unref (buf);
return; return;
} }
@ -369,7 +346,6 @@ gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf)
if (ebml->need_newsegment) { if (ebml->need_newsegment) {
GstEvent *ev; GstEvent *ev;
g_assert (ebml->handled == 0);
ev = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0); ev = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0);
if (gst_pad_push_event (ebml->srcpad, ev)) if (gst_pad_push_event (ebml->srcpad, ev))
ebml->need_newsegment = FALSE; ebml->need_newsegment = FALSE;
@ -378,8 +354,7 @@ gst_ebml_write_element_push (GstEbmlWrite * ebml, GstBuffer * buf)
gst_buffer_set_caps (buf, GST_PAD_CAPS (ebml->srcpad)); gst_buffer_set_caps (buf, GST_PAD_CAPS (ebml->srcpad));
ebml->last_write_result = gst_pad_push (ebml->srcpad, buf); ebml->last_write_result = gst_pad_push (ebml->srcpad, buf);
} else { } else {
if (buf != ebml->cache) gst_buffer_unref (buf);
gst_buffer_unref (buf);
} }
} }
@ -400,14 +375,11 @@ gst_ebml_write_seek (GstEbmlWrite * ebml, guint64 pos)
* knows what he's doing... */ * knows what he's doing... */
if (ebml->cache) { if (ebml->cache) {
/* within bounds? */ /* within bounds? */
if (pos >= GST_BUFFER_OFFSET (ebml->cache) && if (pos >= ebml->cache_pos &&
pos < GST_BUFFER_OFFSET (ebml->cache) + ebml->cache_size) { pos <= ebml->cache_pos + ebml->cache->parent.size) {
GST_BUFFER_SIZE (ebml->cache) = pos - GST_BUFFER_OFFSET (ebml->cache);
if (ebml->pos > pos)
ebml->handled -= ebml->pos - pos;
else
ebml->handled += pos - ebml->pos;
ebml->pos = pos; ebml->pos = pos;
gst_byte_writer_set_pos (ebml->cache, ebml->pos - ebml->cache_pos);
return;
} else { } else {
GST_LOG ("Seek outside cache range. Clearing..."); GST_LOG ("Seek outside cache range. Clearing...");
gst_ebml_write_flush_cache (ebml); gst_ebml_write_flush_cache (ebml);

View file

@ -25,6 +25,7 @@
#include <glib.h> #include <glib.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstbytewriter.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -48,9 +49,8 @@ typedef struct _GstEbmlWrite {
guint64 pos; guint64 pos;
GstClockTime timestamp; GstClockTime timestamp;
GstBuffer *cache; GstByteWriter *cache;
guint cache_size; guint64 cache_pos;
guint handled;
GstFlowReturn last_write_result; GstFlowReturn last_write_result;

View file

@ -2005,14 +2005,14 @@ gst_matroska_mux_start (GstMatroskaMux * mux)
doctype, mux->doctype_version); doctype, mux->doctype_version);
gst_ebml_write_header (ebml, doctype, mux->doctype_version); gst_ebml_write_header (ebml, doctype, mux->doctype_version);
/* the rest of the header is cached */
gst_ebml_write_set_cache (ebml, 0x1000);
/* start a segment */ /* start a segment */
mux->segment_pos = mux->segment_pos =
gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT); gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEGMENT);
mux->segment_master = ebml->pos; mux->segment_master = ebml->pos;
/* the rest of the header is cached */
gst_ebml_write_set_cache (ebml, 0x1000);
/* seekhead (table of contents) - we set the positions later */ /* seekhead (table of contents) - we set the positions later */
mux->seekhead_pos = ebml->pos; mux->seekhead_pos = ebml->pos;
master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD); master = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_SEEKHEAD);