Commit d9c21ec6 authored by Andreas Rheinhardt's avatar Andreas Rheinhardt

avformat/matroskaenc: Write level 1 elements in one go

Up until now, writing level 1 elements proceeded as follows: First, the
element id was written to the ordinary output AVIOContext and a dynamic
buffer was opened for the content of the level 1 element in
start_ebml_master_crc32(). Then this buffer was actually used and after it
was closed (in end_ebml_master_crc32()), the size field corresponding to
the buffer's size was written, after which the actual data was written.

This commit changes this: Nothing is written to the main AVIOContext any
more in start_ebml_master_crc32(). end_ebml_master_crc32() now writes
both the id, the length field as well as the data. This implies that
one can start a level 1 element in memory without outputting anything.
This is done to enable to test whether enough space has been reserved
for the Cues (if space has been reserved for them) before writing them.
A large duration between outputting the header and outputting the rest
could also break certain streaming usecases like the one from #8578
(which this commit fixes).
Signed-off-by: 's avatarAndreas Rheinhardt <andreas.rheinhardt@gmail.com>
parent 5e3b7bd5
...@@ -325,26 +325,26 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master) ...@@ -325,26 +325,26 @@ static void end_ebml_master(AVIOContext *pb, ebml_master master)
avio_seek(pb, pos, SEEK_SET); avio_seek(pb, pos, SEEK_SET);
} }
static int start_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv, static int start_ebml_master_crc32(AVIOContext **dyn_cp, MatroskaMuxContext *mkv)
uint32_t elementid)
{ {
int ret; int ret;
if ((ret = avio_open_dyn_buf(dyn_cp)) < 0) if ((ret = avio_open_dyn_buf(dyn_cp)) < 0)
return ret; return ret;
put_ebml_id(pb, elementid);
if (mkv->write_crc) if (mkv->write_crc)
put_ebml_void(*dyn_cp, 6); /* Reserve space for CRC32 so position/size calculations using avio_tell() take it into account */ put_ebml_void(*dyn_cp, 6); /* Reserve space for CRC32 so position/size calculations using avio_tell() take it into account */
return 0; return 0;
} }
static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv) static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp,
MatroskaMuxContext *mkv, uint32_t id)
{ {
uint8_t *buf, crc[4]; uint8_t *buf, crc[4];
int size, skip = 0; int size, skip = 0;
put_ebml_id(pb, id);
size = avio_close_dyn_buf(*dyn_cp, &buf); size = avio_close_dyn_buf(*dyn_cp, &buf);
put_ebml_num(pb, size, 0); put_ebml_num(pb, size, 0);
if (mkv->write_crc) { if (mkv->write_crc) {
...@@ -362,13 +362,14 @@ static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, Matrosk ...@@ -362,13 +362,14 @@ static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp, Matrosk
* Complete ebml master without destroying the buffer, allowing for later updates * Complete ebml master without destroying the buffer, allowing for later updates
*/ */
static void end_ebml_master_crc32_preliminary(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv, static void end_ebml_master_crc32_preliminary(AVIOContext *pb, AVIOContext **dyn_cp, MatroskaMuxContext *mkv,
int64_t *pos) uint32_t id, int64_t *pos)
{ {
uint8_t *buf; uint8_t *buf;
int size = avio_get_dyn_buf(*dyn_cp, &buf); int size = avio_get_dyn_buf(*dyn_cp, &buf);
*pos = avio_tell(pb); *pos = avio_tell(pb);
put_ebml_id(pb, id);
put_ebml_num(pb, size, 0); put_ebml_num(pb, size, 0);
avio_write(pb, buf, size); avio_write(pb, buf, size);
} }
...@@ -448,7 +449,7 @@ static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv, ...@@ -448,7 +449,7 @@ static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv,
if ((ret64 = avio_seek(pb, seekhead->filepos, SEEK_SET)) < 0) if ((ret64 = avio_seek(pb, seekhead->filepos, SEEK_SET)) < 0)
return error_on_seek_failure ? ret64 : 0; return error_on_seek_failure ? ret64 : 0;
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD); ret = start_ebml_master_crc32(&dyn_cp, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -464,7 +465,7 @@ static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv, ...@@ -464,7 +465,7 @@ static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv,
put_ebml_uint(dyn_cp, MATROSKA_ID_SEEKPOSITION, entry->segmentpos); put_ebml_uint(dyn_cp, MATROSKA_ID_SEEKPOSITION, entry->segmentpos);
end_ebml_master(dyn_cp, seekentry); end_ebml_master(dyn_cp, seekentry);
} }
end_ebml_master_crc32(pb, &dyn_cp, mkv); end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD);
remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb); remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb);
put_ebml_void(pb, remaining); put_ebml_void(pb, remaining);
...@@ -507,7 +508,7 @@ static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tra ...@@ -507,7 +508,7 @@ static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tra
int ret; int ret;
currentpos = avio_tell(pb); currentpos = avio_tell(pb);
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CUES); ret = start_ebml_master_crc32(&dyn_cp, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -550,7 +551,7 @@ static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tra ...@@ -550,7 +551,7 @@ static int64_t mkv_write_cues(AVFormatContext *s, mkv_cues *cues, mkv_track *tra
ffio_reset_dyn_buf(cuepoint); ffio_reset_dyn_buf(cuepoint);
} }
ffio_free_dyn_buf(&cuepoint); ffio_free_dyn_buf(&cuepoint);
end_ebml_master_crc32(pb, &dyn_cp, mkv); end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CUES);
return currentpos; return currentpos;
} }
...@@ -1372,7 +1373,7 @@ static int mkv_write_tracks(AVFormatContext *s) ...@@ -1372,7 +1373,7 @@ static int mkv_write_tracks(AVFormatContext *s)
mkv_add_seekhead_entry(mkv, MATROSKA_ID_TRACKS, avio_tell(pb)); mkv_add_seekhead_entry(mkv, MATROSKA_ID_TRACKS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS); ret = start_ebml_master_crc32(&mkv->tracks_bc, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1387,9 +1388,10 @@ static int mkv_write_tracks(AVFormatContext *s) ...@@ -1387,9 +1388,10 @@ static int mkv_write_tracks(AVFormatContext *s)
} }
if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
end_ebml_master_crc32_preliminary(pb, &mkv->tracks_bc, mkv, &mkv->tracks_pos); end_ebml_master_crc32_preliminary(pb, &mkv->tracks_bc, mkv,
MATROSKA_ID_TRACKS, &mkv->tracks_pos);
else else
end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv); end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS);
return 0; return 0;
} }
...@@ -1407,7 +1409,7 @@ static int mkv_write_chapters(AVFormatContext *s) ...@@ -1407,7 +1409,7 @@ static int mkv_write_chapters(AVFormatContext *s)
mkv_add_seekhead_entry(mkv, MATROSKA_ID_CHAPTERS, avio_tell(pb)); mkv_add_seekhead_entry(mkv, MATROSKA_ID_CHAPTERS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS); ret = start_ebml_master_crc32(&dyn_cp, mkv);
if (ret < 0) return ret; if (ret < 0) return ret;
editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0); editionentry = start_ebml_master(dyn_cp, MATROSKA_ID_EDITIONENTRY, 0);
...@@ -1445,7 +1447,7 @@ static int mkv_write_chapters(AVFormatContext *s) ...@@ -1445,7 +1447,7 @@ static int mkv_write_chapters(AVFormatContext *s)
end_ebml_master(dyn_cp, chapteratom); end_ebml_master(dyn_cp, chapteratom);
} }
end_ebml_master(dyn_cp, editionentry); end_ebml_master(dyn_cp, editionentry);
end_ebml_master_crc32(pb, &dyn_cp, mkv); end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS);
mkv->wrote_chapters = 1; mkv->wrote_chapters = 1;
return 0; return 0;
...@@ -1496,7 +1498,7 @@ static int mkv_write_tag_targets(AVFormatContext *s, uint32_t elementid, ...@@ -1496,7 +1498,7 @@ static int mkv_write_tag_targets(AVFormatContext *s, uint32_t elementid,
if (!mkv->tags_bc) { if (!mkv->tags_bc) {
mkv_add_seekhead_entry(mkv, MATROSKA_ID_TAGS, avio_tell(s->pb)); 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); ret = start_ebml_master_crc32(&mkv->tags_bc, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -1641,9 +1643,10 @@ static int mkv_write_tags(AVFormatContext *s) ...@@ -1641,9 +1643,10 @@ static int mkv_write_tags(AVFormatContext *s)
if (mkv->tags_bc) { if (mkv->tags_bc) {
if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
end_ebml_master_crc32_preliminary(s->pb, &mkv->tags_bc, mkv, &mkv->tags_pos); end_ebml_master_crc32_preliminary(s->pb, &mkv->tags_bc, mkv,
MATROSKA_ID_TAGS, &mkv->tags_pos);
else else
end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv); end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS);
} }
return 0; return 0;
} }
...@@ -1666,7 +1669,7 @@ static int mkv_write_attachments(AVFormatContext *s) ...@@ -1666,7 +1669,7 @@ static int mkv_write_attachments(AVFormatContext *s)
mkv_add_seekhead_entry(mkv, MATROSKA_ID_ATTACHMENTS, avio_tell(pb)); mkv_add_seekhead_entry(mkv, MATROSKA_ID_ATTACHMENTS, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS); ret = start_ebml_master_crc32(&dyn_cp, mkv);
if (ret < 0) return ret; if (ret < 0) return ret;
for (i = 0; i < s->nb_streams; i++) { for (i = 0; i < s->nb_streams; i++) {
...@@ -1739,7 +1742,7 @@ static int mkv_write_attachments(AVFormatContext *s) ...@@ -1739,7 +1742,7 @@ static int mkv_write_attachments(AVFormatContext *s)
mkv->attachments->entries[mkv->attachments->num_entries].stream_idx = i; mkv->attachments->entries[mkv->attachments->num_entries].stream_idx = i;
mkv->attachments->entries[mkv->attachments->num_entries++].fileuid = fileuid; mkv->attachments->entries[mkv->attachments->num_entries++].fileuid = fileuid;
} }
end_ebml_master_crc32(pb, &dyn_cp, mkv); end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS);
return 0; return 0;
} }
...@@ -1818,7 +1821,7 @@ static int mkv_write_header(AVFormatContext *s) ...@@ -1818,7 +1821,7 @@ static int mkv_write_header(AVFormatContext *s)
mkv_add_seekhead_entry(mkv, MATROSKA_ID_INFO, avio_tell(pb)); mkv_add_seekhead_entry(mkv, MATROSKA_ID_INFO, avio_tell(pb));
ret = start_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO); ret = start_ebml_master_crc32(&mkv->info_bc, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
pb = mkv->info_bc; pb = mkv->info_bc;
...@@ -1877,9 +1880,10 @@ static int mkv_write_header(AVFormatContext *s) ...@@ -1877,9 +1880,10 @@ static int mkv_write_header(AVFormatContext *s)
} }
} }
if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live) if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && !mkv->is_live)
end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv, &mkv->info_pos); end_ebml_master_crc32_preliminary(s->pb, &mkv->info_bc, mkv,
MATROSKA_ID_INFO, &mkv->info_pos);
else else
end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv); end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO);
pb = s->pb; pb = s->pb;
ret = mkv_write_tracks(s); ret = mkv_write_tracks(s);
...@@ -2167,7 +2171,7 @@ static void mkv_end_cluster(AVFormatContext *s) ...@@ -2167,7 +2171,7 @@ static void mkv_end_cluster(AVFormatContext *s)
{ {
MatroskaMuxContext *mkv = s->priv_data; MatroskaMuxContext *mkv = s->priv_data;
end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv); end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER);
mkv->cluster_pos = -1; mkv->cluster_pos = -1;
avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT);
} }
...@@ -2308,7 +2312,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_ ...@@ -2308,7 +2312,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, AVPacket *pkt, int add_
if (mkv->cluster_pos == -1) { if (mkv->cluster_pos == -1) {
mkv->cluster_pos = avio_tell(s->pb); mkv->cluster_pos = avio_tell(s->pb);
ret = start_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER); ret = start_ebml_master_crc32(&mkv->cluster_bc, mkv);
if (ret < 0) if (ret < 0)
return ret; return ret;
put_ebml_uint(mkv->cluster_bc, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts)); put_ebml_uint(mkv->cluster_bc, MATROSKA_ID_CLUSTERTIMECODE, FFMAX(0, ts));
...@@ -2474,7 +2478,7 @@ static int mkv_write_trailer(AVFormatContext *s) ...@@ -2474,7 +2478,7 @@ static int mkv_write_trailer(AVFormatContext *s)
} }
if (mkv->cluster_bc) { if (mkv->cluster_bc) {
end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv); end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER);
} }
ret = mkv_write_chapters(s); ret = mkv_write_chapters(s);
...@@ -2522,11 +2526,11 @@ static int mkv_write_trailer(AVFormatContext *s) ...@@ -2522,11 +2526,11 @@ static int mkv_write_trailer(AVFormatContext *s)
av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration); av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
avio_seek(mkv->info_bc, mkv->duration_offset, SEEK_SET); avio_seek(mkv->info_bc, mkv->duration_offset, SEEK_SET);
put_ebml_float(mkv->info_bc, MATROSKA_ID_DURATION, mkv->duration); put_ebml_float(mkv->info_bc, MATROSKA_ID_DURATION, mkv->duration);
end_ebml_master_crc32(pb, &mkv->info_bc, mkv); end_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO);
// write tracks master // write tracks master
avio_seek(pb, mkv->tracks_pos, SEEK_SET); avio_seek(pb, mkv->tracks_pos, SEEK_SET);
end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv); end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS);
// update stream durations // update stream durations
if (!mkv->is_live) { if (!mkv->is_live) {
...@@ -2556,7 +2560,7 @@ static int mkv_write_trailer(AVFormatContext *s) ...@@ -2556,7 +2560,7 @@ static int mkv_write_trailer(AVFormatContext *s)
} }
if (mkv->tags_bc && !mkv->is_live) { if (mkv->tags_bc && !mkv->is_live) {
avio_seek(pb, mkv->tags_pos, SEEK_SET); avio_seek(pb, mkv->tags_pos, SEEK_SET);
end_ebml_master_crc32(pb, &mkv->tags_bc, mkv); end_ebml_master_crc32(pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS);
} }
avio_seek(pb, currentpos, SEEK_SET); avio_seek(pb, currentpos, SEEK_SET);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment