From 6b1968e939472125f97947fe62a534364212f573 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt Date: Fri, 14 Jan 2022 01:18:24 +0100 Subject: [PATCH] avformat/matroskaenc: Avoid seeks when writing EBML header Using start/end_ebml_master() to write an EBML Master element uses seeks under the hood. This does not work if the output is unseekable with the AVIOContext's buffer being very small (the size of the currently written Matroska EBML header is 40) or with the AVIOContext being in direct mode, because then this seek can't be performed in the AVIOContext's buffer. So using an approach that does not rely on seeking at all is preferable; this is achieved by switching to EbmlWriter. Also factor writing the EBML header out into a function of its own. Signed-off-by: Andreas Rheinhardt --- libavformat/matroskaenc.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 53016d34ec..f3c0dde5c2 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -214,10 +214,6 @@ typedef struct MatroskaMuxContext { uint32_t segment_uid[4]; } MatroskaMuxContext; -/** 2 bytes * 7 for EBML IDs, 7 1-byte EBML lengths, 6 1-byte uint, - * 8 byte for "matroska" doctype string */ -#define MAX_EBML_HEADER_SIZE 35 - /** 2 bytes * 3 for EBML IDs, 3 1-byte EBML lengths, 8 bytes for 64 bit * offset, 4 bytes for target EBML ID */ #define MAX_SEEKENTRY_SIZE 21 @@ -464,6 +460,13 @@ static void ebml_writer_add_uid(EbmlWriter *writer, uint32_t id, elem->priv.uint = val; } +static void ebml_writer_add_uint(EbmlWriter *writer, uint32_t id, + uint64_t val) +{ + EbmlElement *elem = ebml_writer_add(writer, id, EBML_UINT); + elem->priv.uint = val; +} + static int ebml_writer_str_len(EbmlElement *elem) { size_t len = strlen(elem->priv.str); @@ -2115,11 +2118,26 @@ static int64_t get_metadata_duration(AVFormatContext *s) return max; } +static void ebml_write_header(AVIOContext *pb, + const char *doctype, int version) +{ + EBML_WRITER(8); + ebml_writer_open_master(&writer, EBML_ID_HEADER); + ebml_writer_add_uint (&writer, EBML_ID_EBMLVERSION, 1); + ebml_writer_add_uint (&writer, EBML_ID_EBMLREADVERSION, 1); + ebml_writer_add_uint (&writer, EBML_ID_EBMLMAXIDLENGTH, 4); + ebml_writer_add_uint (&writer, EBML_ID_EBMLMAXSIZELENGTH, 8); + ebml_writer_add_string(&writer, EBML_ID_DOCTYPE, doctype); + ebml_writer_add_uint (&writer, EBML_ID_DOCTYPEVERSION, version); + ebml_writer_add_uint (&writer, EBML_ID_DOCTYPEREADVERSION, 2); + /* The size is bounded, so no need to check this. */ + ebml_writer_write(&writer, pb); +} + static int mkv_write_header(AVFormatContext *s) { MatroskaMuxContext *mkv = s->priv_data; AVIOContext *pb = s->pb; - ebml_master ebml_header; const AVDictionaryEntry *tag; int ret, i, version = 2; int64_t creation_time; @@ -2136,16 +2154,7 @@ static int mkv_write_header(AVFormatContext *s) version = 4; } - ebml_header = start_ebml_master(pb, EBML_ID_HEADER, MAX_EBML_HEADER_SIZE); - put_ebml_uint (pb, EBML_ID_EBMLVERSION , 1); - put_ebml_uint (pb, EBML_ID_EBMLREADVERSION , 1); - put_ebml_uint (pb, EBML_ID_EBMLMAXIDLENGTH , 4); - put_ebml_uint (pb, EBML_ID_EBMLMAXSIZELENGTH , 8); - put_ebml_string(pb, EBML_ID_DOCTYPE , s->oformat->name); - put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , version); - put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION, 2); - end_ebml_master(pb, ebml_header); - + ebml_write_header(pb, s->oformat->name, version); put_ebml_id(pb, MATROSKA_ID_SEGMENT); put_ebml_size_unknown(pb, 8); mkv->segment_offset = avio_tell(pb);