Browse code

nut: Support experimental NUT 4 features

Add the low overhead pipe mode and the extended broadcast mode.
Export the options as 'syncponts' since it impacts only that.

Signed-off-by: Luca Barbato <lu_zero@gentoo.org>

Luca Barbato authored on 2014/03/07 01:58:34
Showing 5 changed files
... ...
@@ -444,6 +444,23 @@ Alternatively you can write the command as:
444 444
 avconv -benchmark -i INPUT -f null -
445 445
 @end example
446 446
 
447
+@section nut
448
+
449
+@table @option
450
+@item -syncpoints @var{flags}
451
+Change the syncpoint usage in nut:
452
+@table @option
453
+@item @var{default} use the normal low-overhead seeking aids.
454
+@item @var{none} do not use the syncpoints at all, reducing the overhead but making the stream non-seekable;
455
+@item @var{timestamped} extend the syncpoint with a wallclock field.
456
+@end table
457
+The @var{none} and @var{timestamped} flags are experimental.
458
+@end table
459
+
460
+@example
461
+avconv -i INPUT -f_strict experimental -syncpoints none - | processor
462
+@end example
463
+
447 464
 @section ogg
448 465
 
449 466
 Ogg container muxer.
... ...
@@ -17,6 +17,27 @@ subtitle and user-defined streams in a simple, yet efficient, way.
17 17
 It was created by a group of FFmpeg and MPlayer developers in 2003
18 18
 and was finalized in 2008.
19 19
 
20
+@chapter Modes
21
+NUT has some variants signaled by using the flags field in its main header.
22
+
23
+@multitable @columnfractions .4 .4
24
+@item BROADCAST   @tab Extend the syncpoint to report the sender wallclock
25
+@item PIPE        @tab Omit completely the syncpoint
26
+@end multitable
27
+
28
+@section BROADCAST
29
+
30
+The BROADCAST variant provides a secondary time reference to facilitate
31
+detecting endpoint latency and network delays.
32
+It assumes all the endpoint clocks are syncronized.
33
+To be used in real-time scenarios.
34
+
35
+@section PIPE
36
+
37
+The PIPE variant assumes NUT is used as non-seekable intermediate container,
38
+by not using syncpoint removes unneeded overhead and reduces the overall
39
+memory usage.
40
+
20 41
 @chapter Container-specific codec tags
21 42
 
22 43
 @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
... ...
@@ -85,6 +87,7 @@ typedef struct ChapterContext {
85 85
 } ChapterContext;
86 86
 
87 87
 typedef struct NUTContext {
88
+    const AVClass *av_class;
88 89
     AVFormatContext *avf;
89 90
 //    int written_packet_size;
90 91
 //    int64_t packet_start;
... ...
@@ -100,6 +103,10 @@ typedef struct NUTContext {
100 100
     int header_count;
101 101
     AVRational *time_base;
102 102
     struct AVTreeNode *syncpoints;
103
+#define NUT_BROADCAST 1 // use extended syncpoints
104
+#define NUT_PIPE 2      // do not write syncpoints
105
+    int flags;
106
+    int version; // version currently in use
103 107
 } NUTContext;
104 108
 
105 109
 extern const AVCodecTag ff_nut_subtitle_tags[];
... ...
@@ -213,8 +213,8 @@ static int decode_main_header(NUTContext *nut)
213 213
     end  = get_packetheader(nut, bc, 1, MAIN_STARTCODE);
214 214
     end += avio_tell(bc);
215 215
 
216
-    tmp = ffio_read_varlen(bc);
217
-    if (tmp < 2 && tmp > NUT_VERSION) {
216
+    nut->version = ffio_read_varlen(bc);
217
+    if (tmp < NUT_MIN_VERSION && tmp > NUT_MAX_VERSION) {
218 218
         av_log(s, AV_LOG_ERROR, "Version %"PRId64" not supported.\n",
219 219
                tmp);
220 220
         return AVERROR(ENOSYS);
... ...
@@ -320,6 +320,11 @@ static int decode_main_header(NUTContext *nut)
320 320
         assert(nut->header_len[0] == 0);
321 321
     }
322 322
 
323
+    // flags had been effectively introduced in version 4
324
+    if (nut->version > NUT_STABLE_VERSION) {
325
+        nut->flags = ffio_read_varlen(bc);
326
+    }
327
+
323 328
     if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
324 329
         av_log(s, AV_LOG_ERROR, "main header checksum mismatch\n");
325 330
         return AVERROR_INVALIDDATA;
... ...
@@ -547,6 +552,14 @@ static int decode_syncpoint(NUTContext *nut, int64_t *ts, int64_t *back_ptr)
547 547
     ff_nut_reset_ts(nut, nut->time_base[tmp % nut->time_base_count],
548 548
                     tmp / nut->time_base_count);
549 549
 
550
+    if (nut->flags & NUT_BROADCAST) {
551
+        tmp = ffio_read_varlen(bc);
552
+        av_log(s, AV_LOG_VERBOSE, "Syncpoint wallclock %"PRId64"\n",
553
+               av_rescale_q(tmp / nut->time_base_count,
554
+                            nut->time_base[tmp % nut->time_base_count],
555
+                            AV_TIME_BASE_Q));
556
+    }
557
+
550 558
     if (skip_reserved(bc, end) || ffio_get_checksum(bc)) {
551 559
         av_log(s, AV_LOG_ERROR, "sync point checksum mismatch\n");
552 560
         return AVERROR_INVALIDDATA;
... ...
@@ -728,7 +741,8 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
728 728
     int size, flags, size_mul, pts_delta, i, reserved_count;
729 729
     uint64_t tmp;
730 730
 
731
-    if (avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) {
731
+    if (!(nut->flags & NUT_PIPE) &&
732
+        avio_tell(bc) > nut->last_syncpoint_pos + nut->max_distance) {
732 733
         av_log(s, AV_LOG_ERROR,
733 734
                "Last frame must have been damaged %"PRId64" > %"PRId64" + %d\n",
734 735
                avio_tell(bc), nut->last_syncpoint_pos, nut->max_distance);
... ...
@@ -781,8 +795,9 @@ static int decode_frame_header(NUTContext *nut, int64_t *pts, int *stream_id,
781 781
 
782 782
     if (flags & FLAG_CHECKSUM) {
783 783
         avio_rb32(bc); // FIXME check this
784
-    } else if (size > 2 * nut->max_distance || FFABS(stc->last_pts - *pts) >
785
-               stc->max_pts_distance) {
784
+    } else if (!(nut->flags & NUT_PIPE) &&
785
+               size > 2 * nut->max_distance ||
786
+               FFABS(stc->last_pts - *pts) > stc->max_pts_distance) {
786 787
         av_log(s, AV_LOG_ERROR, "frame size > 2max_distance and no checksum\n");
787 788
         return AVERROR_INVALIDDATA;
788 789
     }
... ...
@@ -933,6 +948,10 @@ static int read_seek(AVFormatContext *s, int stream_index,
933 933
     int64_t pos, pos2, ts;
934 934
     int i;
935 935
 
936
+    if (nut->flags & NUT_PIPE) {
937
+        return AVERROR(ENOSYS);
938
+    }
939
+
936 940
     if (st->index_entries) {
937 941
         int index = av_index_search_timestamp(st, pts, flags);
938 942
         if (index < 0)
... ...
@@ -25,6 +25,8 @@
25 25
 #include "libavutil/mathematics.h"
26 26
 #include "libavutil/tree.h"
27 27
 #include "libavutil/dict.h"
28
+#include "libavutil/time.h"
29
+#include "libavutil/opt.h"
28 30
 #include "libavcodec/mpegaudiodata.h"
29 31
 #include "nut.h"
30 32
 #include "internal.h"
... ...
@@ -325,7 +327,7 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
325 325
         tmp_head_idx;
326 326
     int64_t tmp_match;
327 327
 
328
-    ff_put_v(bc, NUT_VERSION);
328
+    ff_put_v(bc, nut->version);
329 329
     ff_put_v(bc, nut->avf->nb_streams);
330 330
     ff_put_v(bc, nut->max_distance);
331 331
     ff_put_v(bc, nut->time_base_count);
... ...
@@ -405,6 +407,9 @@ static void write_mainheader(NUTContext *nut, AVIOContext *bc)
405 405
         ff_put_v(bc, nut->header_len[i]);
406 406
         avio_write(bc, nut->header[i], nut->header_len[i]);
407 407
     }
408
+    // flags had been effectively introduced in version 4
409
+    if (nut->version > NUT_STABLE_VERSION)
410
+        ff_put_v(bc, nut->flags);
408 411
 }
409 412
 
410 413
 static int write_streamheader(AVFormatContext *avctx, AVIOContext *bc,
... ...
@@ -643,6 +648,16 @@ static int nut_write_header(AVFormatContext *s)
643 643
 
644 644
     nut->avf = s;
645 645
 
646
+    nut->version = NUT_STABLE_VERSION + !!nut->flags;
647
+    if (nut->flags && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
648
+        av_log(s, AV_LOG_ERROR,
649
+               "The additional syncpoint modes require version %d, "
650
+               "that is currently not finalized, "
651
+               "please set -f_strict experimental in order to enable it.\n",
652
+               nut->version);
653
+        return AVERROR_EXPERIMENTAL;
654
+    }
655
+
646 656
     nut->stream = av_mallocz(sizeof(StreamContext) * s->nb_streams);
647 657
     if (s->nb_chapters)
648 658
         nut->chapter = av_mallocz(sizeof(ChapterContext) * s->nb_chapters);
... ...
@@ -789,7 +804,8 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
789 789
 
790 790
 //FIXME: Ensure store_sp is 1 in the first place.
791 791
 
792
-    if (store_sp) {
792
+    if (store_sp &&
793
+        (!(nut->flags & NUT_PIPE) || nut->last_syncpoint_pos == INT_MIN)) {
793 794
         Syncpoint *sp, dummy = { .pos = INT64_MAX };
794 795
 
795 796
         ff_nut_reset_ts(nut, *nus->time_base, pkt->dts);
... ...
@@ -815,6 +831,11 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
815 815
             return ret;
816 816
         put_tt(nut, nus->time_base, dyn_bc, pkt->dts);
817 817
         ff_put_v(dyn_bc, sp ? (nut->last_syncpoint_pos - sp->pos) >> 4 : 0);
818
+
819
+        if (nut->flags & NUT_BROADCAST) {
820
+            put_tt(nut, nus->time_base, dyn_bc,
821
+                   av_rescale_q(av_gettime(), AV_TIME_BASE_Q, *nus->time_base));
822
+        }
818 823
         put_packet(nut, bc, dyn_bc, 1, SYNCPOINT_STARTCODE);
819 824
 
820 825
         if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0)
... ...
@@ -917,7 +938,7 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt)
917 917
     nus->last_pts   = pkt->pts;
918 918
 
919 919
     //FIXME just store one per syncpoint
920
-    if (flags & FLAG_KEY)
920
+    if (flags & FLAG_KEY && !(nut->flags & NUT_PIPE))
921 921
         av_add_index_entry(
922 922
             s->streams[pkt->stream_index],
923 923
             nut->last_syncpoint_pos,
... ...
@@ -945,6 +966,23 @@ static int nut_write_trailer(AVFormatContext *s)
945 945
     return 0;
946 946
 }
947 947
 
948
+#define OFFSET(x) offsetof(NUTContext, x)
949
+#define E AV_OPT_FLAG_ENCODING_PARAM
950
+static const AVOption options[] = {
951
+    { "syncpoints",  "NUT syncpoint behaviour",                         OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0},             INT_MIN, INT_MAX, E, "syncpoints" },
952
+    { "default",     "",                                                0,             AV_OPT_TYPE_CONST, {.i64 = 0},             INT_MIN, INT_MAX, E, "syncpoints" },
953
+    { "none",        "Disable syncpoints, low overhead and unseekable", 0,             AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE},      INT_MIN, INT_MAX, E, "syncpoints" },
954
+    { "timestamped", "Extend syncpoints with a wallclock timestamp",    0,             AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" },
955
+    { NULL },
956
+};
957
+
958
+static const AVClass class = {
959
+    .class_name = "nutenc",
960
+    .item_name  = av_default_item_name,
961
+    .option     = options,
962
+    .version    = LIBAVUTIL_VERSION_INT,
963
+};
964
+
948 965
 AVOutputFormat ff_nut_muxer = {
949 966
     .name           = "nut",
950 967
     .long_name      = NULL_IF_CONFIG_SMALL("NUT"),
... ...
@@ -959,4 +997,5 @@ AVOutputFormat ff_nut_muxer = {
959 959
     .write_trailer  = nut_write_trailer,
960 960
     .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS,
961 961
     .codec_tag      = ff_nut_codec_tags,
962
+    .priv_class     = &class,
962 963
 };