Browse code

Merge commit 'c94e2e85cb6af8a570d8542a830556243bd32873'

* commit 'c94e2e85cb6af8a570d8542a830556243bd32873':
nut: Support experimental NUT 4 features

Conflicts:
doc/nut.texi
libavformat/nut.h
libavformat/nutdec.c
libavformat/nutenc.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>

Michael Niedermayer authored on 2014/05/29 08:09:51
Showing 5 changed files
... ...
@@ -706,6 +706,23 @@ Alternatively you can write the command as:
706 706
 ffmpeg -benchmark -i INPUT -f null -
707 707
 @end example
708 708
 
709
+@section nut
710
+
711
+@table @option
712
+@item -syncpoints @var{flags}
713
+Change the syncpoint usage in nut:
714
+@table @option
715
+@item @var{default} use the normal low-overhead seeking aids.
716
+@item @var{none} do not use the syncpoints at all, reducing the overhead but making the stream non-seekable;
717
+@item @var{timestamped} extend the syncpoint with a wallclock field.
718
+@end table
719
+The @var{none} and @var{timestamped} flags are experimental.
720
+@end table
721
+
722
+@example
723
+ffmpeg -i INPUT -f_strict experimental -syncpoints none - | processor
724
+@end example
725
+
709 726
 @section ogg
710 727
 
711 728
 Ogg container muxer.
... ...
@@ -21,6 +21,27 @@ The official nut specification is at svn://svn.mplayerhq.hu/nut
21 21
 In case of any differences between this text and the official specification,
22 22
 the official specification shall prevail.
23 23
 
24
+@chapter Modes
25
+NUT has some variants signaled by using the flags field in its main header.
26
+
27
+@multitable @columnfractions .4 .4
28
+@item BROADCAST   @tab Extend the syncpoint to report the sender wallclock
29
+@item PIPE        @tab Omit completely the syncpoint
30
+@end multitable
31
+
32
+@section BROADCAST
33
+
34
+The BROADCAST variant provides a secondary time reference to facilitate
35
+detecting endpoint latency and network delays.
36
+It assumes all the endpoint clocks are syncronized.
37
+To be used in real-time scenarios.
38
+
39
+@section PIPE
40
+
41
+The PIPE variant assumes NUT is used as non-seekable intermediate container,
42
+by not using syncpoint removes unneeded overhead and reduces the overall
43
+memory usage.
44
+
24 45
 @chapter Container-specific codec tags
25 46
 
26 47
 @section Generic raw YUVA formats
... ...
@@ -36,7 +36,9 @@
36 36
 
37 37
 #define MAX_DISTANCE (1024*32-1)
38 38
 
39
-#define NUT_VERSION 3
39
+#define NUT_MAX_VERSION 4
40
+#define NUT_STABLE_VERSION 3
41
+#define NUT_MIN_VERSION 2
40 42
 
41 43
 typedef enum{
42 44
     FLAG_KEY        =   1, ///<if set, frame is keyframe
... ...
@@ -87,6 +89,7 @@ typedef struct ChapterContext {
87 87
 } ChapterContext;
88 88
 
89 89
 typedef struct NUTContext {
90
+    const AVClass *av_class;
90 91
     AVFormatContext *avf;
91 92
 //    int written_packet_size;
92 93
 //    int64_t packet_start;
... ...
@@ -105,7 +108,10 @@ typedef struct NUTContext {
105 105
     int sp_count;
106 106
     int64_t max_pts;
107 107
     AVRational *max_pts_tb;
108
-    int version;
108
+#define NUT_BROADCAST 1 // use extended syncpoints
109
+#define NUT_PIPE 2      // do not write syncpoints
110
+    int flags;
111
+    int version; // version currently in use
109 112
     int minor_version;
110 113
 } NUTContext;
111 114
 
... ...
@@ -229,7 +229,7 @@ static int decode_main_header(NUTContext *nut)
229 229
     end += avio_tell(bc);
230 230
 
231 231
     tmp = ffio_read_varlen(bc);
232
-    if (tmp < 2 && tmp > 4) {
232
+    if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) {
233 233
         av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n",
234 234
                tmp);
235 235
         return AVERROR(ENOSYS);
... ...
@@ -340,6 +340,11 @@ static int decode_main_header(NUTContext *nut)
340 340
         av_assert0(nut->header_len[0] == 0);
341 341
     }
342 342
 
343
+    // flags had been effectively introduced in version 4
344
+    if (nut->version > NUT_STABLE_VERSION) {
345
+        nut->flags = ffio_read_varlen(bc);
346
+    }
347
+
343 348
     if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
344 349
         av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n");
345 350
         return AVERROR_INVALIDDATA;
... ...
@@ -578,6 +583,14 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
578 578
     ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count],
579 579
                     tmp / nut->time_base_count);
580 580
 
581
+    if (nut->flags & NUT_BROADCAST) {
582
+        tmp = ffio_read_varlen(bc);
583
+        av_log(s, AV_LOG_VERBOSE, "Syncpoint wallclock %"PRId64"\n",
584
+               av_rescale_q(tmp / nut->time_base_count,
585
+                            nut->time_base[tmp % nut->time_base_count],
586
+                            AV_TIME_BASE_Q));
587
+    }
588
+
581 589
     if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
582 590
         av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n");
583 591
         return AVERROR_INVALIDDATA;
... ...
@@ -898,7 +911,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
898 898
     int size, flags, size_mul, pts_delta, i, reserved_count;
899 899
     uint64_t tmp;
900 900
 
901
-    if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) {
901
+    if (!(nut->flags & NUT_PIPE) &&
902
+        avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) {
902 903
         av_log(s, AV_LOG_ERROR,
903 904
                "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n",
904 905
                avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance);
... ...
@@ -951,8 +965,9 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
951 951
 
952 952
     if (flags & FLAG_CHECKSUM) {
953 953
         avio_rb32(bc); // FIXME check this
954
-    } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) >
955
-               stc->max_pts_distance) {
954
+    } else if (!(nut->flags & NUT_PIPE) &&
955
+               size > 2 * nut->max_distance ||
956
+               FFABS(stc->last_pts - *pts) > stc->max_pts_distance) {
956 957
         av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n");
957 958
         return AVERROR_INVALIDDATA;
958 959
     }
... ...
@@ -1118,6 +1133,10 @@ static int read_seek(AVFormatContext *s, int stream_index,
1118 1118
     int64_t pos, pos2, ts;
1119 1119
     int i;
1120 1120
 
1121
+    if (nut->flags & NUT_PIPE) {
1122
+        return AVERROR(ENOSYS);
1123
+    }
1124
+
1121 1125
     if (st->index_entries) {
1122 1126
         int index = av_index_search_timestamp(st, pts, flags);
1123 1127
         if (index < 0)
... ...
@@ -26,6 +26,8 @@
26 26
 #include "libavutil/tree.h"
27 27
 #include "libavutil/dict.h"
28 28
 #include "libavutil/avassert.h"
29
+#include "libavutil/time.h"
30
+#include "libavutil/opt.h"
29 31
 #include "libavcodec/bytestream.h"
30 32
 #include "libavcodec/mpegaudiodata.h"
31 33
 #include "nut.h"
... ...
@@ -338,7 +340,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
338 338
         tmp_head_idx;
339 339
     int64_t tmp_match;
340 340
 
341
-    ff_put_v(bc, nut->version = NUT_VERSION);
341
+    ff_put_v(bc, nut->version);
342 342
     if (nut->version > 3)
343 343
         ff_put_v(bc, nut->minor_version);
344 344
     ff_put_v(bc, nut->avf->nb_streams);
... ...
@@ -407,6 +409,9 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
407 407
         ff_put_v(bc, nut->header_len[i]);
408 408
         avio_write(bc, nut->header[i], nut->header_len[i]);
409 409
     }
410
+    // flags had been effectively introduced in version 4
411
+    if (nut->version > NUT_STABLE_VERSION)
412
+        ff_put_v(bc, nut->flags);
410 413
 }
411 414
 
412 415
 static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
... ...
@@ -692,6 +697,16 @@ static int nut_write_header(AVFormatContext *s)
692 692
 
693 693
     nut->avf = s;
694 694
 
695
+    nut->version = NUT_STABLE_VERSION + !!nut->flags;
696
+    if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
697
+        av_log(s, AV_LOG_ERROR,
698
+               "The additional syncpoint modes require version %d, "
699
+               "that is currently not finalized, "
700
+               "please set -f_strict experimental in order to enable it.\n",
701
+               nut->version);
702
+        return AVERROR_EXPERIMENTAL;
703
+    }
704
+
695 705
     nut->stream   = av_calloc(s->nb_streams,  sizeof(*nut->stream ));
696 706
     nut->chapter  = av_calloc(s->nb_chapters, sizeof(*nut->chapter));
697 707
     nut->time_base= av_calloc(s->nb_streams +
... ...
@@ -974,7 +989,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
974 974
 
975 975
 //FIXME: Ensure store_sp is 1 in the first place.
976 976
 
977
-    if (store_sp) {
977
+    if (store_sp &&
978
+        (!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) {
978 979
         Syncpoint *sp, dummy = { .pos = INT64_MAX };
979 980
 
980 981
         ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
... ...
@@ -1000,6 +1016,11 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
1000 1000
             return ret;
1001 1001
         put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
1002 1002
         ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0);
1003
+
1004
+        if (nut->flags & NUT_BROADCAST) {
1005
+            put_tt(nut, nus->time_base, dyn_bc,
1006
+                   av_rescale_q(av_gettime(), AV_TIME_BASE_Q, *nus->time_base));
1007
+        }
1003 1008
         put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE);
1004 1009
 
1005 1010
         if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0)
... ...
@@ -1110,7 +1131,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
1110 1110
     nus->last_pts   = pkt->pts;
1111 1111
 
1112 1112
     //FIXME just store one per syncpoint
1113
-    if (flags & FLAG_KEY) {
1113
+    if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE)) {
1114 1114
         av_add_index_entry(
1115 1115
             s->streams[pkt->stream_index],
1116 1116
             nut->last_syncpoint_pos,
... ...
@@ -1156,6 +1177,23 @@ static int nut_write_trailer(AVFormatContext *s)
1156 1156
     return 0;
1157 1157
 }
1158 1158
 
1159
+#define OFFSET(x) offsetof(NUTContext, x)
1160
+#define E AV_OPT_FLAG_ENCODING_PARAM
1161
+static const AVOption options[] = {
1162
+    { "syncpoints",  "NUT syncpoint behaviour",                         OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0},             INT_MIN, INT_MAX, E, "syncpoints" },
1163
+    { "default",     "",                                                0,             AV_OPT_TYPE_CONST, {.i64 = 0},             INT_MIN, INT_MAX, E, "syncpoints" },
1164
+    { "none",        "Disable syncpoints, low overhead and unseekable", 0,             AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE},      INT_MIN, INT_MAX, E, "syncpoints" },
1165
+    { "timestamped", "Extend syncpoints with a wallclock timestamp",    0,             AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" },
1166
+    { NULL },
1167
+};
1168
+
1169
+static const AVClass class = {
1170
+    .class_name = "nutenc",
1171
+    .item_name  = av_default_item_name,
1172
+    .option     = options,
1173
+    .version    = LIBAVUTIL_VERSION_INT,
1174
+};
1175
+
1159 1176
 AVOutputFormat ff_nut_muxer = {
1160 1177
     .name           = "nut",
1161 1178
     .long_name      = NULL_IF_CONFIG_SMALL("NUT"),
... ...
@@ -1170,4 +1208,5 @@ AVOutputFormat ff_nut_muxer = {
1170 1170
     .write_trailer  = nut_write_trailer,
1171 1171
     .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
1172 1172
     .codec_tag      = ff_nut_codec_tags,
1173
+    .priv_class     = &class,
1173 1174
 };