mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-02 14:20:06 +00:00
flvmux: Preallocate index space and fill it after finishing output
Make the index appear at the beginning of the file, which is what most players are expecting. Fixes #601236.
This commit is contained in:
parent
7c74f7d525
commit
7deee29d2c
1 changed files with 106 additions and 71 deletions
|
@ -50,6 +50,7 @@ enum
|
|||
};
|
||||
|
||||
#define DEFAULT_IS_LIVE FALSE
|
||||
#define MAX_INDEX_ENTRIES 128
|
||||
|
||||
static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
|
@ -566,6 +567,36 @@ gst_flv_mux_push (GstFlvMux * mux, GstBuffer * buffer)
|
|||
return gst_pad_push (mux->srcpad, buffer);
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_flv_mux_preallocate_index (GstFlvMux * mux)
|
||||
{
|
||||
GstBuffer *tmp;
|
||||
guint8 *data;
|
||||
gint preallocate_size;
|
||||
|
||||
/* preallocate index of size:
|
||||
* - 'keyframes' ECMA array key: 2 + 9 = 11 bytes
|
||||
* - nested ECMA array header, length and end marker: 8 bytes
|
||||
* - 'times' and 'filepositions' keys: 22 bytes
|
||||
* - two strict arrays headers and lengths: 10 bytes
|
||||
* - each index entry: 18 bytes
|
||||
*/
|
||||
preallocate_size = 11 + 8 + 22 + 10 + MAX_INDEX_ENTRIES * 18;
|
||||
GST_DEBUG_OBJECT (mux, "preallocating %d bytes for the index",
|
||||
preallocate_size);
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (preallocate_size);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
|
||||
/* prefill the space with a gstfiller: <spaces> script tag variable */
|
||||
GST_WRITE_UINT16_BE (data, 9); /* 9 characters */
|
||||
memcpy (data + 2, "gstfiller", 9);
|
||||
GST_WRITE_UINT8 (data + 11, 2); /* a string value */
|
||||
GST_WRITE_UINT16_BE (data + 12, preallocate_size - 14);
|
||||
memset (data + 14, ' ', preallocate_size - 14); /* the rest is spaces */
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_flv_mux_write_metadata (GstFlvMux * mux)
|
||||
{
|
||||
|
@ -612,6 +643,13 @@ gst_flv_mux_write_metadata (GstFlvMux * mux)
|
|||
GST_WRITE_UINT32_BE (data + 1, n_tags);
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
|
||||
if (!mux->is_live) {
|
||||
tmp = gst_flv_mux_preallocate_index (mux);
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (mux, "not preallocating index, stream is live");
|
||||
}
|
||||
|
||||
for (i = 0; tags && i < n_tags; i++) {
|
||||
const gchar *tag_name =
|
||||
gst_structure_nth_field_name ((const GstStructure *) tags, i);
|
||||
|
@ -1010,113 +1048,110 @@ static GstFlowReturn
|
|||
gst_flv_mux_write_index (GstFlvMux * mux)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
GstBuffer *script_tag, *tmp;
|
||||
GstBuffer *index;
|
||||
GstEvent *event;
|
||||
guint8 *data;
|
||||
GList *l;
|
||||
guint32 index_len;
|
||||
guint32 index_len, allocate_size;
|
||||
guint32 i, index_skip;
|
||||
|
||||
if (!mux->index)
|
||||
return GST_FLOW_OK;
|
||||
|
||||
script_tag = gst_buffer_new_and_alloc (11);
|
||||
data = GST_BUFFER_DATA (script_tag);
|
||||
if (mux->is_live)
|
||||
return GST_FLOW_OK;
|
||||
|
||||
data[0] = 18;
|
||||
|
||||
/* Data size, unknown for now */
|
||||
data[1] = 0;
|
||||
data[2] = 0;
|
||||
data[3] = 0;
|
||||
|
||||
/* Timestamp */
|
||||
data[4] = data[5] = data[6] = data[7] = 0;
|
||||
|
||||
/* Stream ID */
|
||||
data[8] = data[9] = data[10] = 0;
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (13);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
data[0] = 2; /* string */
|
||||
data[1] = 0;
|
||||
data[2] = 0x0a; /* length 10 */
|
||||
memcpy (&data[3], "onMetaData", 10);
|
||||
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (5);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
data[0] = 8; /* ECMA array */
|
||||
GST_WRITE_UINT32_BE (data + 1, 2);
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
/* seek back to the preallocated index space */
|
||||
event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
|
||||
42, GST_CLOCK_TIME_NONE, 42);
|
||||
if (!gst_pad_push_event (mux->srcpad, event)) {
|
||||
GST_WARNING_OBJECT (mux, "Seek to rewrite index failed");
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
mux->index = g_list_reverse (mux->index);
|
||||
index_len = g_list_length (mux->index);
|
||||
|
||||
/* We write at most 128 elements */
|
||||
index_skip = (index_len > 128) ? 1 + index_len / 128 : 1;
|
||||
index_len =
|
||||
(index_len <=
|
||||
128) ? index_len : (index_len + index_skip - 1) / index_skip;
|
||||
/* We write at most MAX_INDEX_ENTRIES elements */
|
||||
if (index_len > MAX_INDEX_ENTRIES) {
|
||||
index_skip = 1 + index_len / MAX_INDEX_ENTRIES;
|
||||
index_len = (index_len + index_skip - 1) / index_skip;
|
||||
} else {
|
||||
index_skip = 1;
|
||||
}
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (2 + 5 + 1 + 4 + index_len * (1 + 8));
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
data[0] = 0; /* 5 bytes name */
|
||||
data[1] = 5;
|
||||
memcpy (&data[2], "times", 5);
|
||||
data[7] = 10; /* array */
|
||||
GST_WRITE_UINT32_BE (&data[8], index_len);
|
||||
data += 12;
|
||||
GST_DEBUG_OBJECT (mux, "Index length is %d", index_len);
|
||||
/* see size calculation in gst_flv_mux_preallocate_index */
|
||||
allocate_size = 11 + 8 + 22 + 10 + index_len * 18;
|
||||
GST_DEBUG_OBJECT (mux, "Allocating %d bytes for index", allocate_size);
|
||||
index = gst_buffer_new_and_alloc (allocate_size);
|
||||
data = GST_BUFFER_DATA (index);
|
||||
|
||||
GST_WRITE_UINT16_BE (data, 9); /* the 'keyframes' key */
|
||||
memcpy (data + 2, "keyframes", 9);
|
||||
GST_WRITE_UINT8 (data + 11, 8); /* nested ECMA array */
|
||||
GST_WRITE_UINT32_BE (data + 12, 2); /* two elements */
|
||||
GST_WRITE_UINT16_BE (data + 16, 5); /* first string key: 'times' */
|
||||
memcpy (data + 18, "times", 5);
|
||||
GST_WRITE_UINT8 (data + 23, 10); /* strict array */
|
||||
GST_WRITE_UINT32_BE (data + 24, index_len);
|
||||
data += 28;
|
||||
|
||||
/* the keyframes' times */
|
||||
for (i = 0, l = mux->index; l; l = l->next, i++) {
|
||||
GstFlvMuxIndexEntry *entry = l->data;
|
||||
|
||||
if (i % index_skip != 0)
|
||||
continue;
|
||||
|
||||
data[0] = 0;
|
||||
GST_WRITE_DOUBLE_BE (&data[1], entry->time);
|
||||
GST_WRITE_UINT8 (data, 0); /* numeric (aka double) */
|
||||
GST_WRITE_DOUBLE_BE (data + 1, entry->time);
|
||||
data += 9;
|
||||
}
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (2 + 13 + 1 + 4 + index_len * (1 + 8));
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
data[0] = 0; /* 13 bytes name */
|
||||
data[1] = 13;
|
||||
memcpy (&data[2], "filepositions", 13);
|
||||
data[15] = 10; /* array */
|
||||
GST_WRITE_UINT32_BE (&data[16], index_len);
|
||||
GST_WRITE_UINT16_BE (data, 13); /* second string key: 'filepositions' */
|
||||
memcpy (data + 2, "filepositions", 13);
|
||||
GST_WRITE_UINT8 (data + 15, 10); /* strict array */
|
||||
GST_WRITE_UINT32_BE (data + 16, index_len);
|
||||
data += 20;
|
||||
|
||||
/* the keyframes' file positions */
|
||||
for (i = 0, l = mux->index; l; l = l->next, i++) {
|
||||
GstFlvMuxIndexEntry *entry = l->data;
|
||||
|
||||
if (i % index_skip != 0)
|
||||
continue;
|
||||
data[0] = 0;
|
||||
GST_WRITE_DOUBLE_BE (&data[1], entry->position);
|
||||
GST_WRITE_UINT8 (data, 0);
|
||||
GST_WRITE_DOUBLE_BE (data + 1, entry->position);
|
||||
data += 9;
|
||||
}
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (3);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
GST_WRITE_UINT24_BE (data, 9); /* finish the ECMA array */
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
|
||||
tmp = gst_buffer_new_and_alloc (4);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
GST_WRITE_UINT32_BE (data, GST_BUFFER_SIZE (script_tag));
|
||||
script_tag = gst_buffer_join (script_tag, tmp);
|
||||
/* If there is space left in the prefilled area, reinsert the filler.
|
||||
There is at least 18 bytes free, so it will always fit. */
|
||||
if (index_len < MAX_INDEX_ENTRIES) {
|
||||
GstBuffer *tmp;
|
||||
guint8 *data;
|
||||
guint32 remaining_filler_size;
|
||||
|
||||
data = GST_BUFFER_DATA (script_tag);
|
||||
data[1] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 16) & 0xff;
|
||||
data[2] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 8) & 0xff;
|
||||
data[3] = ((GST_BUFFER_SIZE (script_tag) - 11 - 4) >> 0) & 0xff;
|
||||
tmp = gst_buffer_new_and_alloc (14);
|
||||
data = GST_BUFFER_DATA (tmp);
|
||||
GST_WRITE_UINT16_BE (data, 9);
|
||||
memcpy (data + 2, "gstfiller", 9);
|
||||
GST_WRITE_UINT8 (data + 11, 2); /* string */
|
||||
|
||||
gst_buffer_set_caps (script_tag, GST_PAD_CAPS (mux->srcpad));
|
||||
ret = gst_flv_mux_push (mux, script_tag);
|
||||
/* There is 18 bytes per remaining index entry minus what is used for
|
||||
* the'gstfiller' key. The rest is already filled with spaces, so just need
|
||||
* to update length. */
|
||||
remaining_filler_size = (MAX_INDEX_ENTRIES - index_len) * 18 - 14;
|
||||
GST_DEBUG_OBJECT (mux, "Remaining filler size is %d bytes",
|
||||
remaining_filler_size);
|
||||
GST_WRITE_UINT16_BE (data + 12, remaining_filler_size);
|
||||
index = gst_buffer_join (index, tmp);
|
||||
}
|
||||
|
||||
gst_buffer_set_caps (index, GST_PAD_CAPS (mux->srcpad));
|
||||
ret = gst_flv_mux_push (mux, index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue