avformat/matroskaenc: Avoid allocations for SeekHead

Up until e7ddafd5, the Matroska muxer wrote two SeekHeads: One at the
beginning referencing the main level 1 elements (i.e. not the Clusters)
and one at the end, referencing the Clusters. This second SeekHead was
useless and has therefore been removed. Yet the SeekHead-related
functions and structures are still geared towards this usecase: They
are built around an allocated array of variable size that gets
reallocated every time an element is added to it although the maximum
number of Seek entries is a small compile-time constant, so that one should
rather include the array in the SeekHead structure itself; and said
structure should be contained in the MatroskaMuxContext instead of being
allocated separately.

The earlier code reserved space for a SeekHead with 10 entries, although
we currently write at most 6. Reducing said number implied that every
Matroska/Webm file will be 84 bytes smaller and required to adapt
several FATE tests; furthermore, the reserved amount overestimated the
amount needed for for the SeekHead's length field and how many bytes
need to be reserved to write a EBML Void element, bringing the total
reduction to 89 bytes.

This also fixes a potential segfault: If !mkv->is_live and if the
AVIOContext is initially unseekable when writing the header, the
SeekHead is already written when writing the header and this used to
free the SeekHead-related structures that have been allocated. But if
the AVIOContext happens to be seekable when writing the trailer, it will
be attempted to write the SeekHead again which will lead to segfaults
because the corresponding structures have already been freed.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
This commit is contained in:
Andreas Rheinhardt 2019-12-29 09:38:44 +01:00
parent 107783006c
commit dc2f6b54ac
11 changed files with 73 additions and 122 deletions

View File

@ -57,6 +57,10 @@
#include "libavcodec/mpeg4audio.h"
#include "libavcodec/internal.h"
/* Level 1 elements we create a SeekHead entry for:
* Info, Tracks, Chapters, Attachments, Tags and Cues */
#define MAX_SEEKHEAD_ENTRIES 6
typedef struct ebml_master {
int64_t pos; ///< absolute offset in the containing AVIOContext where the master's elements start
int sizebytes; ///< how many bytes were reserved for the size
@ -69,11 +73,9 @@ typedef struct mkv_seekhead_entry {
typedef struct mkv_seekhead {
int64_t filepos;
int64_t segment_offset; ///< the file offset to the beginning of the segment
int reserved_size; ///< -1 if appending to file
int max_entries;
mkv_seekhead_entry *entries;
mkv_seekhead_entry entries[MAX_SEEKHEAD_ENTRIES];
int num_entries;
int reserved_size;
} mkv_seekhead;
typedef struct mkv_cuepoint {
@ -136,7 +138,7 @@ typedef struct MatroskaMuxContext {
int64_t cluster_pts;
int64_t duration_offset;
int64_t duration;
mkv_seekhead *seekhead;
mkv_seekhead seekhead;
mkv_cues *cues;
mkv_track *tracks;
mkv_attachments *attachments;
@ -396,10 +398,6 @@ static void mkv_deinit(AVFormatContext *s)
ffio_free_dyn_buf(&mkv->tracks_bc);
ffio_free_dyn_buf(&mkv->tags_bc);
if (mkv->seekhead) {
av_freep(&mkv->seekhead->entries);
av_freep(&mkv->seekhead);
}
if (mkv->cues) {
av_freep(&mkv->cues->entries);
av_freep(&mkv->cues);
@ -412,61 +410,33 @@ static void mkv_deinit(AVFormatContext *s)
}
/**
* Initialize a mkv_seekhead element to be ready to index level 1 Matroska
* elements. If a maximum number of elements is specified, enough space
* will be reserved at the current file location to write a seek head of
* that size.
*
* @param segment_offset The absolute offset to the position in the file
* where the segment begins.
* @param numelements The maximum number of elements that will be indexed
* by this seek head, 0 if unlimited.
* Initialize the SeekHead element to be ready to index level 1 Matroska
* elements. Enough space to write MAX_SEEKHEAD_ENTRIES SeekHead entries
* will be reserved at the current file location.
*/
static mkv_seekhead *mkv_start_seekhead(AVIOContext *pb, int64_t segment_offset,
int numelements)
static void mkv_start_seekhead(MatroskaMuxContext *mkv, AVIOContext *pb)
{
mkv_seekhead *new_seekhead = av_mallocz(sizeof(mkv_seekhead));
if (!new_seekhead)
return NULL;
new_seekhead->segment_offset = segment_offset;
if (numelements > 0) {
new_seekhead->filepos = avio_tell(pb);
// 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID
// and size, 6 bytes for a CRC32 element, and 3 bytes to guarantee
// that an EBML void element will fit afterwards
new_seekhead->reserved_size = numelements * MAX_SEEKENTRY_SIZE + 19;
new_seekhead->max_entries = numelements;
put_ebml_void(pb, new_seekhead->reserved_size);
}
return new_seekhead;
mkv->seekhead.filepos = avio_tell(pb);
// 21 bytes max for a Seek entry, 6 bytes max for the SeekHead ID
// and size, 6 bytes for a CRC32 element, and 2 bytes to guarantee
// that an EBML void element will fit afterwards
mkv->seekhead.reserved_size = MAX_SEEKHEAD_ENTRIES * MAX_SEEKENTRY_SIZE + 14;
put_ebml_void(pb, mkv->seekhead.reserved_size);
}
static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, uint32_t elementid, uint64_t filepos)
static void mkv_add_seekhead_entry(MatroskaMuxContext *mkv, uint32_t elementid,
uint64_t filepos)
{
mkv_seekhead_entry *entries = seekhead->entries;
mkv_seekhead *seekhead = &mkv->seekhead;
// don't store more elements than we reserved space for
if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries)
return -1;
entries = av_realloc_array(entries, seekhead->num_entries + 1, sizeof(mkv_seekhead_entry));
if (!entries)
return AVERROR(ENOMEM);
seekhead->entries = entries;
av_assert1(seekhead->num_entries < MAX_SEEKHEAD_ENTRIES);
seekhead->entries[seekhead->num_entries].elementid = elementid;
seekhead->entries[seekhead->num_entries++].segmentpos = filepos - seekhead->segment_offset;
return 0;
seekhead->entries[seekhead->num_entries++].segmentpos = filepos - mkv->segment_offset;
}
/**
* Write the seek head to the file and free it. If a maximum number of
* elements was specified to mkv_start_seekhead(), the seek head will
* be written at the location reserved for it. Otherwise, it is written
* at the current location in the file.
* Write the SeekHead to the file at the location reserved for it.
*
* @return The file offset where the seekhead was written,
* -1 if an error occurred.
@ -474,19 +444,17 @@ static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, uint32_t elementid, ui
static int64_t mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv)
{
AVIOContext *dyn_cp;
mkv_seekhead *seekhead = mkv->seekhead;
mkv_seekhead *seekhead = &mkv->seekhead;
ebml_master seekentry;
int64_t currentpos;
int64_t currentpos, remaining;
int i;
currentpos = avio_tell(pb);
if (seekhead->reserved_size > 0) {
if (avio_seek(pb, seekhead->filepos, SEEK_SET) < 0) {
currentpos = -1;
goto fail;
}
}
if (start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD) < 0) {
currentpos = -1;
@ -507,16 +475,13 @@ static int64_t mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv)
}
end_ebml_master_crc32(pb, &dyn_cp, mkv);
if (seekhead->reserved_size > 0) {
uint64_t remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb);
remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb);
put_ebml_void(pb, remaining);
avio_seek(pb, currentpos, SEEK_SET);
currentpos = seekhead->filepos;
}
fail:
av_freep(&mkv->seekhead->entries);
av_freep(&mkv->seekhead);
return currentpos;
}
@ -1429,9 +1394,7 @@ static int mkv_write_tracks(AVFormatContext *s)
AVIOContext *pb = s->pb;
int i, ret, default_stream_exists = 0;
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_TRACKS, avio_tell(pb));
if (ret < 0)
return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_TRACKS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS);
if (ret < 0)
@ -1466,8 +1429,7 @@ static int mkv_write_chapters(AVFormatContext *s)
if (!s->nb_chapters || mkv->wrote_chapters)
return 0;
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_CHAPTERS, avio_tell(pb));
if (ret < 0) return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_CHAPTERS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS);
if (ret < 0) return ret;
@ -1556,8 +1518,7 @@ static int mkv_write_tag_targets(AVFormatContext *s, uint32_t elementid,
int ret;
if (!mkv->tags_bc) {
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_TAGS, avio_tell(s->pb));
if (ret < 0) return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_TAGS, avio_tell(s->pb));
ret = start_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS);
if (ret < 0)
@ -1727,8 +1688,7 @@ static int mkv_write_attachments(AVFormatContext *s)
av_lfg_init(&c, av_get_random_seed());
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_ATTACHMENTS, avio_tell(pb));
if (ret < 0) return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_ATTACHMENTS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS);
if (ret < 0) return ret;
@ -1877,16 +1837,10 @@ static int mkv_write_header(AVFormatContext *s)
mkv->segment_offset = avio_tell(pb);
// we write a seek head at the beginning to point to all other level
// one elements, which aren't more than 10 elements as we write only one
// of every other currently defined level 1 element
mkv->seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10);
if (!mkv->seekhead) {
return AVERROR(ENOMEM);
}
// one elements (except Clusters).
mkv_start_seekhead(mkv, pb);
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_INFO, avio_tell(pb));
if (ret < 0)
return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_INFO, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO);
if (ret < 0)
@ -2580,10 +2534,7 @@ static int mkv_write_trailer(AVFormatContext *s)
cuespos = mkv_write_cues(s, mkv->cues, mkv->tracks, s->nb_streams);
}
ret = mkv_add_seekhead_entry(mkv->seekhead, MATROSKA_ID_CUES,
cuespos);
if (ret < 0)
return ret;
mkv_add_seekhead_entry(mkv, MATROSKA_ID_CUES, cuespos);
}
mkv_write_seekhead(pb, mkv);

View File

@ -12,7 +12,7 @@ fate-matroska-prores-header-insertion-bz2: CMD = framecrc -i $(TARGET_SAMPLES)/m
FATE_MATROSKA-$(call DEMMUX, MATROSKA, MATROSKA) += fate-matroska-remux
fate-matroska-remux: CMD = md5pipe -i $(TARGET_SAMPLES)/vp9-test-vectors/vp90-2-2pass-akiyo.webm -color_trc 4 -c:v copy -fflags +bitexact -strict -2 -f matroska
fate-matroska-remux: CMP = oneline
fate-matroska-remux: REF = e5457e5fa606d564a54914bd12f426c8
fate-matroska-remux: REF = 49a60ef91cf7302ab7276f9373f8a429
FATE_MATROSKA-$(call ALLYES, MATROSKA_DEMUXER VORBIS_PARSER) += fate-matroska-xiph-lacing
fate-matroska-xiph-lacing: CMD = framecrc -i $(TARGET_SAMPLES)/mkv/xiph_lacing.mka -c:a copy

View File

@ -91,12 +91,12 @@ fate-wavpack-matroskamode: CMD = md5 -i $(TARGET_SAMPLES)/wavpack/special/matros
FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-mono
fate-wavpack-matroska_mux-mono: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/num_channels/mono_16bit_int.wv -c copy -fflags +bitexact -f matroska
fate-wavpack-matroska_mux-mono: CMP = oneline
fate-wavpack-matroska_mux-mono: REF = c5a2b46d8b31c9c2e7bfb436f9d56f02
fate-wavpack-matroska_mux-mono: REF = 0a1a7c6d3b413925f8261c92d1d53363
FATE_WAVPACK-$(call DEMMUX, WV, MATROSKA) += fate-wavpack-matroska_mux-61
fate-wavpack-matroska_mux-61: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/num_channels/eva_2.22_6.1_16bit-partial.wv -c copy -fflags +bitexact -f matroska
fate-wavpack-matroska_mux-61: CMP = oneline
fate-wavpack-matroska_mux-61: REF = 22fc2a591be22657d19cab89ad6eef55
fate-wavpack-matroska_mux-61: REF = 1f012f7c237f46f2f63089ba84c589c5
FATE_SAMPLES_AVCONV += $(FATE_WAVPACK-yes)
fate-wavpack: $(FATE_WAVPACK-yes)

View File

@ -1,5 +1,5 @@
b09fc2f554712adbf84fe7899eb679d4 *tests/data/fate/aac-autobsf-adtstoasc.matroska
6695 tests/data/fate/aac-autobsf-adtstoasc.matroska
d10b73732b32f6b2f08885b40c41c77f *tests/data/fate/aac-autobsf-adtstoasc.matroska
6606 tests/data/fate/aac-autobsf-adtstoasc.matroska
#extradata 0: 2, 0x0030001c
#tb 0: 1/1000
#media_type 0: audio

View File

@ -1 +1 @@
a5811caa0caba3a3f9a449b91569745c
03d376cc2e8622e7af540d4d9809dbf7

View File

@ -1,5 +1,5 @@
ffe2c21083d56764aaae7945fd146326 *tests/data/fate/rgb24-mkv.matroska
58305 tests/data/fate/rgb24-mkv.matroska
92861b36a8220a3dae10d91cba614892 *tests/data/fate/rgb24-mkv.matroska
58216 tests/data/fate/rgb24-mkv.matroska
#tb 0: 1/10
#media_type 0: video
#codec_id 0: rawvideo

View File

@ -1,3 +1,3 @@
6604d339d44864be58b8580fe2fb52cc *tests/data/lavf-fate/lavf.av1.mkv
55725 tests/data/lavf-fate/lavf.av1.mkv
be4903a6a6710043383c8f38a535a8d3 *tests/data/lavf-fate/lavf.av1.mkv
55636 tests/data/lavf-fate/lavf.av1.mkv
tests/data/lavf-fate/lavf.av1.mkv CRC=0x7c27cc15

View File

@ -1,3 +1,3 @@
a91bfdb08ff5a1e62b039748d16b07f7 *tests/data/lavf/lavf.mka
43652 tests/data/lavf/lavf.mka
58936d3ad4eb1372bd2e5845963fe17c *tests/data/lavf/lavf.mka
43563 tests/data/lavf/lavf.mka
tests/data/lavf/lavf.mka CRC=0x3a1da17e

View File

@ -1,3 +1,3 @@
3cddd26ed41f34925d3f34acabab174e *tests/data/lavf/lavf.mkv
320555 tests/data/lavf/lavf.mkv
6ec6d6a058a85bf616e0b981bb8e383f *tests/data/lavf/lavf.mkv
320466 tests/data/lavf/lavf.mkv
tests/data/lavf/lavf.mkv CRC=0xec6c3c68

View File

@ -1,3 +1,3 @@
4a25c53150c09537cd4fcbff8f8f14ff *tests/data/lavf/lavf.mkv_attachment
472706 tests/data/lavf/lavf.mkv_attachment
41b4f132ada6e0faedf92e909526a899 *tests/data/lavf/lavf.mkv_attachment
472617 tests/data/lavf/lavf.mkv_attachment
tests/data/lavf/lavf.mkv_attachment CRC=0xec6c3c68

View File

@ -1,48 +1,48 @@
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 714 size: 208
ret: 0 st:-1 flags:0 ts:-1.000000
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret: 0 st:-1 flags:1 ts: 1.894167
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret: 0 st: 0 flags:0 ts: 0.788000
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret: 0 st: 0 flags:1 ts:-0.317000
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret:-1 st: 1 flags:0 ts: 2.577000
ret: 0 st: 1 flags:1 ts: 1.471000
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320187 size: 209
ret: 0 st:-1 flags:0 ts: 0.365002
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146898 size: 27925
ret: 0 st:-1 flags:1 ts:-0.740831
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret:-1 st: 0 flags:0 ts: 2.153000
ret: 0 st: 0 flags:1 ts: 1.048000
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret: 0 st: 1 flags:0 ts:-0.058000
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 714 size: 208
ret: 0 st: 1 flags:1 ts: 2.836000
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320187 size: 209
ret:-1 st:-1 flags:0 ts: 1.730004
ret: 0 st:-1 flags:1 ts: 0.624171
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146898 size: 27925
ret: 0 st: 0 flags:0 ts:-0.482000
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret: 0 st: 0 flags:1 ts: 2.413000
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret:-1 st: 1 flags:0 ts: 1.307000
ret: 0 st: 1 flags:1 ts: 0.201000
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 803 size: 208
ret: 0 st: 1 flags:1 dts: 0.000000 pts: 0.000000 pos: 714 size: 208
ret: 0 st:-1 flags:0 ts:-0.904994
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret: 0 st:-1 flags:1 ts: 1.989173
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret: 0 st: 0 flags:0 ts: 0.883000
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292435 size: 27834
ret: 0 st: 0 flags:1 dts: 0.971000 pts: 0.971000 pos: 292346 size: 27834
ret: 0 st: 0 flags:1 ts:-0.222000
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837
ret:-1 st: 1 flags:0 ts: 2.672000
ret: 0 st: 1 flags:1 ts: 1.566000
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320276 size: 209
ret: 0 st: 1 flags:1 dts: 0.993000 pts: 0.993000 pos: 320187 size: 209
ret: 0 st:-1 flags:0 ts: 0.460008
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146987 size: 27925
ret: 0 st: 0 flags:1 dts: 0.491000 pts: 0.491000 pos: 146898 size: 27925
ret: 0 st:-1 flags:1 ts:-0.645825
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 1019 size: 27837
ret: 0 st: 0 flags:1 dts: 0.011000 pts: 0.011000 pos: 930 size: 27837