[FFmpeg-devel] [PATCH 26/28] avformat/matroskaenc: Stop reallocating of Cluster buffer

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Wed Jan 22 21:27:15 EET 2020


The Matroska muxer uses a dynamic buffer to buffer the content of
Clusters before eventually writing them. Up until now, each time a
Cluster was written, the dynamic buffer was closed, i.e. freed; now it
is only reset, saving allocations of the AVIOContext itself, its opaque
as well as most of the reallocations of the buffer.

This is advantageous performance-wise, in particular on systems where
reallocations are slow (namely Windows). The following table shows the
decicyles for writing a frame on Linux (Ubuntu 19.10) and Windows (7)
on an x64 Haswell (to /dev/null on Linux, to stdout which is discarded
on Windows (the default values of the size and duration of clusters for
seekable output have been explicitly set in this case); in all tests,
writing CRC-32 values has been disabled in all tests; calls to the muxer's
write_packet function in write_packet() in libavformat/mux.c have been
timed; each of the following tests has been repeated 50 times):

    | Windows before | Windows after | Linux before | Linux after
_________________________________________________________________
 A  |     979437     |    192304     |    259500    |   183320
 B  |     715936     |    155648     |    152786    |   130879
 C  |     265115     |     56034     |     78496    |    53243
 D  |     386224     |     80307     |    128894    |    75354
 E  |      21732     |     10695     |     11320    |     9801

(A is a 10.2 mb/s file with a GOP length of 2s, amounting to an average
Cluster size of about 2.5 MiB; the average Cluster size of B is 1.1 MiB;
for C it is 2.35 MiB, for D it is 0.46 MiB; for E - a file with just a
single audio track of 158kb/s resulting in a Cluster size of about 100
kB, the relative gains were the smallest, probably because of the small
Cluster size.)

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt at gmail.com>
---
 libavformat/matroskaenc.c | 47 ++++++++++++++++++++++-----------------
 1 file changed, 26 insertions(+), 21 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 5065b2adbd..8bbacf5cd3 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -337,7 +337,7 @@ static int start_ebml_master_crc32(AVIOContext **dyn_cp, MatroskaMuxContext *mkv
 {
     int ret;
 
-    if ((ret = avio_open_dyn_buf(dyn_cp)) < 0)
+    if (!*dyn_cp && (ret = avio_open_dyn_buf(dyn_cp)) < 0)
         return ret;
 
     if (mkv->write_crc)
@@ -348,13 +348,13 @@ static int start_ebml_master_crc32(AVIOContext **dyn_cp, MatroskaMuxContext *mkv
 
 static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp,
                                   MatroskaMuxContext *mkv, uint32_t id,
-                                  int length_size)
+                                  int length_size, int keep_buffer)
 {
     uint8_t *buf, crc[4];
     int size, skip = 0;
 
     put_ebml_id(pb, id);
-    size = avio_close_dyn_buf(*dyn_cp, &buf);
+    size = avio_get_dyn_buf(*dyn_cp, &buf);
     put_ebml_num(pb, size, length_size);
     if (mkv->write_crc) {
         skip = 6; /* Skip reserved 6-byte long void element from the dynamic buffer. */
@@ -363,8 +363,11 @@ static void end_ebml_master_crc32(AVIOContext *pb, AVIOContext **dyn_cp,
     }
     avio_write(pb, buf + skip, size - skip);
 
-    av_free(buf);
-    *dyn_cp = NULL;
+    if (keep_buffer) {
+        ffio_reset_dyn_buf(*dyn_cp);
+    } else {
+        ffio_free_dyn_buf(dyn_cp);
+    }
 }
 
 /**
@@ -449,7 +452,7 @@ static void mkv_add_seekhead_entry(MatroskaMuxContext *mkv, uint32_t elementid,
 static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv,
                               int error_on_seek_failure, int64_t destpos)
 {
-    AVIOContext *dyn_cp;
+    AVIOContext *dyn_cp = NULL;
     mkv_seekhead *seekhead = &mkv->seekhead;
     int64_t remaining, ret64;
     int i, ret;
@@ -476,7 +479,7 @@ static int mkv_write_seekhead(AVIOContext *pb, MatroskaMuxContext *mkv,
         put_ebml_uint(dyn_cp, MATROSKA_ID_SEEKPOSITION, entry->segmentpos);
         end_ebml_master(dyn_cp, seekentry);
     }
-    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD, 0);
+    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_SEEKHEAD, 0, 0);
 
     remaining = seekhead->filepos + seekhead->reserved_size - avio_tell(pb);
     put_ebml_void(pb, remaining);
@@ -1416,7 +1419,7 @@ static int mkv_write_tracks(AVFormatContext *s)
         end_ebml_master_crc32_preliminary(pb, mkv->tracks_bc,
                                           MATROSKA_ID_TRACKS, &mkv->tracks_pos);
     else
-        end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS, 0);
+        end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS, 0, 0);
 
     return 0;
 }
@@ -1424,7 +1427,7 @@ static int mkv_write_tracks(AVFormatContext *s)
 static int mkv_write_chapters(AVFormatContext *s)
 {
     MatroskaMuxContext *mkv = s->priv_data;
-    AVIOContext *dyn_cp, *pb = s->pb;
+    AVIOContext *dyn_cp = NULL, *pb = s->pb;
     ebml_master editionentry;
     AVRational scale = {1, 1E9};
     int i, ret;
@@ -1472,7 +1475,7 @@ static int mkv_write_chapters(AVFormatContext *s)
         end_ebml_master(dyn_cp, chapteratom);
     }
     end_ebml_master(dyn_cp, editionentry);
-    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS, 0);
+    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_CHAPTERS, 0, 0);
 
     mkv->wrote_chapters = 1;
     return 0;
@@ -1671,7 +1674,7 @@ static int mkv_write_tags(AVFormatContext *s)
             end_ebml_master_crc32_preliminary(s->pb, mkv->tags_bc,
                                               MATROSKA_ID_TAGS, &mkv->tags_pos);
         else
-            end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS, 0);
+            end_ebml_master_crc32(s->pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS, 0, 0);
     }
     return 0;
 }
@@ -1679,7 +1682,7 @@ static int mkv_write_tags(AVFormatContext *s)
 static int mkv_write_attachments(AVFormatContext *s)
 {
     MatroskaMuxContext *mkv = s->priv_data;
-    AVIOContext *dyn_cp, *pb = s->pb;
+    AVIOContext *dyn_cp = NULL, *pb = s->pb;
     AVLFG c;
     int i, ret;
 
@@ -1767,7 +1770,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++].fileuid  = fileuid;
     }
-    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS, 0);
+    end_ebml_master_crc32(pb, &dyn_cp, mkv, MATROSKA_ID_ATTACHMENTS, 0, 0);
 
     return 0;
 }
@@ -1908,7 +1911,7 @@ static int mkv_write_header(AVFormatContext *s)
         end_ebml_master_crc32_preliminary(s->pb, mkv->info_bc,
                                           MATROSKA_ID_INFO, &mkv->info_pos);
     else
-        end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO, 0);
+        end_ebml_master_crc32(s->pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO, 0, 0);
     pb = s->pb;
 
     ret = mkv_write_tracks(s);
@@ -2188,7 +2191,7 @@ static void mkv_end_cluster(AVFormatContext *s)
 {
     MatroskaMuxContext *mkv = s->priv_data;
 
-    end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER, 0);
+    end_ebml_master_crc32(s->pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER, 0, 1);
     mkv->cluster_pos = -1;
     avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT);
 }
@@ -2492,7 +2495,8 @@ static int mkv_write_trailer(AVFormatContext *s)
     }
 
     if (mkv->cluster_bc) {
-        end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv, MATROSKA_ID_CLUSTER, 0);
+        end_ebml_master_crc32(pb, &mkv->cluster_bc, mkv,
+                              MATROSKA_ID_CLUSTER, 0, 0);
     }
 
     ret = mkv_write_chapters(s);
@@ -2506,7 +2510,7 @@ static int mkv_write_trailer(AVFormatContext *s)
         endpos = avio_tell(pb);
 
         if (mkv->cues.num_entries) {
-            AVIOContext *cues;
+            AVIOContext *cues = NULL;
             uint64_t size;
             int64_t cuespos = endpos;
             int length_size = 0;
@@ -2548,7 +2552,8 @@ static int mkv_write_trailer(AVFormatContext *s)
                 }
             }
             mkv_add_seekhead_entry(mkv, MATROSKA_ID_CUES, cuespos);
-            end_ebml_master_crc32(pb, &cues, mkv, MATROSKA_ID_CUES, length_size);
+            end_ebml_master_crc32(pb, &cues, mkv, MATROSKA_ID_CUES,
+                                  length_size, 0);
             if (mkv->reserve_cues_space) {
                 if (size < mkv->reserve_cues_space)
                     put_ebml_void(pb, mkv->reserve_cues_space - size);
@@ -2564,11 +2569,11 @@ static int mkv_write_trailer(AVFormatContext *s)
         av_log(s, AV_LOG_DEBUG, "end duration = %" PRIu64 "\n", mkv->duration);
         avio_seek(mkv->info_bc, mkv->duration_offset, SEEK_SET);
         put_ebml_float(mkv->info_bc, MATROSKA_ID_DURATION, mkv->duration);
-        end_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO, 0);
+        end_ebml_master_crc32(pb, &mkv->info_bc, mkv, MATROSKA_ID_INFO, 0, 0);
 
         // write tracks master
         avio_seek(pb, mkv->tracks_pos, SEEK_SET);
-        end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS, 0);
+        end_ebml_master_crc32(pb, &mkv->tracks_bc, mkv, MATROSKA_ID_TRACKS, 0, 0);
 
         // update stream durations
         if (!mkv->is_live) {
@@ -2598,7 +2603,7 @@ static int mkv_write_trailer(AVFormatContext *s)
         }
         if (mkv->tags_bc && !mkv->is_live) {
             avio_seek(pb, mkv->tags_pos, SEEK_SET);
-            end_ebml_master_crc32(pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS, 0);
+            end_ebml_master_crc32(pb, &mkv->tags_bc, mkv, MATROSKA_ID_TAGS, 0, 0);
         }
 
         avio_seek(pb, endpos, SEEK_SET);
-- 
2.20.1



More information about the ffmpeg-devel mailing list