Browse code

matroskaenc: add an option to put the index at the start of the file

Anton Khirnov authored on 2013/04/30 15:36:20
Showing 4 changed files
... ...
@@ -16,6 +16,7 @@ version 10:
16 16
 - new trim and atrim filters
17 17
 - avconv -t and -ss (output-only) options are now sample-accurate when
18 18
   transcoding audio
19
+- Matroska muxer can now put the index at the beginning of the file.
19 20
 
20 21
 
21 22
 version 9:
... ...
@@ -373,6 +373,27 @@ For example a 3D WebM clip can be created using the following command line:
373 373
 avconv -i sample_left_right_clip.mpg -an -c:v libvpx -metadata STEREO_MODE=left_right -y stereo_clip.webm
374 374
 @end example
375 375
 
376
+This muxer supports the following options:
377
+
378
+@table @option
379
+
380
+@item reserve_index_space
381
+By default, this muxer writes the index for seeking (called cues in Matroska
382
+terms) at the end of the file, because it cannot know in advance how much space
383
+to leave for the index at the beginning of the file. However for some use cases
384
+-- e.g.  streaming where seeking is possible but slow -- it is useful to put the
385
+index at the beginning of the file.
386
+
387
+If this option is set to a non-zero value, the muxer will reserve a given amount
388
+of space in the file header and then try to write the cues there when the muxing
389
+finishes. If the available space does not suffice, muxing will fail. A safe size
390
+for most use cases should be about 50kB per hour of video.
391
+
392
+Note that cues are only written if the output is seekable and this option will
393
+have no effect if it is not.
394
+
395
+@end table
396
+
376 397
 @section segment
377 398
 
378 399
 Basic stream segmenter.
... ...
@@ -34,6 +34,7 @@
34 34
 #include "libavutil/intreadwrite.h"
35 35
 #include "libavutil/lfg.h"
36 36
 #include "libavutil/mathematics.h"
37
+#include "libavutil/opt.h"
37 38
 #include "libavutil/random_seed.h"
38 39
 #include "libavutil/samplefmt.h"
39 40
 
... ...
@@ -79,6 +80,7 @@ typedef struct {
79 79
 #define MODE_WEBM       0x02
80 80
 
81 81
 typedef struct MatroskaMuxContext {
82
+    const AVClass  *class;
82 83
     int             mode;
83 84
     AVIOContext   *dyn_bc;
84 85
     ebml_master     segment;
... ...
@@ -95,6 +97,9 @@ typedef struct MatroskaMuxContext {
95 95
     AVPacket        cur_audio_pkt;
96 96
 
97 97
     int have_attachments;
98
+
99
+    int reserve_cues_space;
100
+    int64_t cues_pos;
98 101
 } MatroskaMuxContext;
99 102
 
100 103
 
... ...
@@ -968,6 +973,11 @@ static int mkv_write_header(AVFormatContext *s)
968 968
     if (mkv->cues == NULL)
969 969
         return AVERROR(ENOMEM);
970 970
 
971
+    if (pb->seekable && mkv->reserve_cues_space) {
972
+        mkv->cues_pos = avio_tell(pb);
973
+        put_ebml_void(pb, mkv->reserve_cues_space);
974
+    }
975
+
971 976
     av_init_packet(&mkv->cur_audio_pkt);
972 977
     mkv->cur_audio_pkt.size = 0;
973 978
 
... ...
@@ -1250,7 +1260,28 @@ static int mkv_write_trailer(AVFormatContext *s)
1250 1250
 
1251 1251
     if (pb->seekable) {
1252 1252
         if (mkv->cues->num_entries) {
1253
-            cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
1253
+            if (mkv->reserve_cues_space) {
1254
+                int64_t cues_end;
1255
+
1256
+                currentpos = avio_tell(pb);
1257
+                avio_seek(pb, mkv->cues_pos, SEEK_SET);
1258
+
1259
+                cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
1260
+                cues_end = avio_tell(pb);
1261
+                if (cues_end > cuespos + mkv->reserve_cues_space) {
1262
+                    av_log(s, AV_LOG_ERROR, "Insufficient space reserved for cues: %d "
1263
+                           "(needed: %"PRId64").\n", mkv->reserve_cues_space,
1264
+                           cues_end - cuespos);
1265
+                    return AVERROR(EINVAL);
1266
+                }
1267
+
1268
+                if (cues_end < cuespos + mkv->reserve_cues_space)
1269
+                    put_ebml_void(pb, mkv->reserve_cues_space - (cues_end - cuespos));
1270
+
1271
+                avio_seek(pb, currentpos, SEEK_SET);
1272
+            } else {
1273
+                cuespos = mkv_write_cues(pb, mkv->cues, s->nb_streams);
1274
+            }
1254 1275
 
1255 1276
             ret = mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_CUES, cuespos);
1256 1277
             if (ret < 0) return ret;
... ...
@@ -1291,7 +1322,22 @@ static int mkv_query_codec(enum AVCodecID codec_id, int std_compliance)
1291 1291
     return 0;
1292 1292
 }
1293 1293
 
1294
+#define OFFSET(x) offsetof(MatroskaMuxContext, x)
1295
+#define FLAGS AV_OPT_FLAG_ENCODING_PARAM
1296
+static const AVOption options[] = {
1297
+    { "reserve_index_space", "Reserve a given amount of space (in bytes) at the beginning "
1298
+        "of the file for the index (cues).", OFFSET(reserve_cues_space), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
1299
+    { NULL },
1300
+};
1301
+
1294 1302
 #if CONFIG_MATROSKA_MUXER
1303
+static const AVClass matroska_class = {
1304
+    .class_name = "matroska muxer",
1305
+    .item_name  = av_default_item_name,
1306
+    .option     = options,
1307
+    .version    = LIBAVUTIL_VERSION_INT,
1308
+};
1309
+
1295 1310
 AVOutputFormat ff_matroska_muxer = {
1296 1311
     .name              = "matroska",
1297 1312
     .long_name         = NULL_IF_CONFIG_SMALL("Matroska"),
... ...
@@ -1312,10 +1358,18 @@ AVOutputFormat ff_matroska_muxer = {
1312 1312
     },
1313 1313
     .subtitle_codec    = AV_CODEC_ID_SSA,
1314 1314
     .query_codec       = mkv_query_codec,
1315
+    .priv_class        = &matroska_class,
1315 1316
 };
1316 1317
 #endif
1317 1318
 
1318 1319
 #if CONFIG_WEBM_MUXER
1320
+static const AVClass webm_class = {
1321
+    .class_name = "webm muxer",
1322
+    .item_name  = av_default_item_name,
1323
+    .option     = options,
1324
+    .version    = LIBAVUTIL_VERSION_INT,
1325
+};
1326
+
1319 1327
 AVOutputFormat ff_webm_muxer = {
1320 1328
     .name              = "webm",
1321 1329
     .long_name         = NULL_IF_CONFIG_SMALL("WebM"),
... ...
@@ -1329,10 +1383,17 @@ AVOutputFormat ff_webm_muxer = {
1329 1329
     .write_trailer     = mkv_write_trailer,
1330 1330
     .flags             = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
1331 1331
                          AVFMT_TS_NONSTRICT,
1332
+    .priv_class        = &webm_class,
1332 1333
 };
1333 1334
 #endif
1334 1335
 
1335 1336
 #if CONFIG_MATROSKA_AUDIO_MUXER
1337
+static const AVClass mka_class = {
1338
+    .class_name = "matroska audio muxer",
1339
+    .item_name  = av_default_item_name,
1340
+    .option     = options,
1341
+    .version    = LIBAVUTIL_VERSION_INT,
1342
+};
1336 1343
 AVOutputFormat ff_matroska_audio_muxer = {
1337 1344
     .name              = "matroska",
1338 1345
     .long_name         = NULL_IF_CONFIG_SMALL("Matroska"),
... ...
@@ -1347,5 +1408,6 @@ AVOutputFormat ff_matroska_audio_muxer = {
1347 1347
     .write_trailer     = mkv_write_trailer,
1348 1348
     .flags             = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT,
1349 1349
     .codec_tag         = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 },
1350
+    .priv_class        = &mka_class,
1350 1351
 };
1351 1352
 #endif
... ...
@@ -31,7 +31,7 @@
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 55
33 33
 #define LIBAVFORMAT_VERSION_MINOR  0
34
-#define LIBAVFORMAT_VERSION_MICRO  0
34
+#define LIBAVFORMAT_VERSION_MICRO  1
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \