]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/dashenc.c
lavfi/nlmeans: add SIMD-friendly assumptions for compute_safe_ssd_integral_image
[ffmpeg] / libavformat / dashenc.c
index 9c29c057769a15220ce40e22f91e1c279fe20f81..1dd633365da61908466533460559620f3d4f0107 100644 (file)
@@ -45,6 +45,7 @@
 #include "isom.h"
 #include "os_support.h"
 #include "url.h"
+#include "vpcc.h"
 #include "dash.h"
 
 typedef struct Segment {
@@ -76,9 +77,8 @@ typedef struct OutputStream {
     int nb_segments, segments_size, segment_index;
     Segment **segments;
     int64_t first_pts, start_pts, max_pts;
-    int64_t last_dts;
+    int64_t last_dts, last_pts;
     int bit_rate;
-    char bandwidth_str[64];
 
     char codec_str[100];
     int written_len;
@@ -124,6 +124,7 @@ typedef struct DASHContext {
     AVIOContext *m3u8_out;
     int streaming;
     int64_t timeout;
+    int index_correction;
 } DASHContext;
 
 static struct codec_string {
@@ -170,8 +171,23 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam
     }
 }
 
+static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par,
+                              AVRational *frame_rate, char *str, int size) {
+    VPCC vpcc;
+    int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc);
+    if (ret == 0) {
+        av_strlcatf(str, size, "vp09.%02x.%02x.%02x",
+                    vpcc.profile, vpcc.level, vpcc.bitdepth);
+    } else {
+        // Default to just vp9 in case of error while finding out profile or level
+        av_log(s, AV_LOG_WARNING, "Could not find VP9 profile and/or level\n");
+        av_strlcpy(str, "vp9", size);
+    }
+    return;
+}
+
 static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
-                          char *str, int size)
+                          AVRational *frame_rate, char *str, int size)
 {
     const AVCodecTag *tags[2] = { NULL, NULL };
     uint32_t tag;
@@ -180,7 +196,11 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par,
     // common Webm codecs are not part of RFC 6381
     for (i = 0; codecs[i].id; i++)
         if (codecs[i].id == par->codec_id) {
-            av_strlcpy(str, codecs[i].str, size);
+            if (codecs[i].id == AV_CODEC_ID_VP9) {
+                set_vp9_codec_str(s, par, frame_rate, str, size);
+            } else {
+                av_strlcpy(str, codecs[i].str, size);
+            }
             return;
         }
 
@@ -549,20 +569,25 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
 
     for (i = 0; i < s->nb_streams; i++) {
         OutputStream *os = &c->streams[i];
+        char bandwidth_str[64] = {'\0'};
 
         if (os->as_idx - 1 != as_index)
             continue;
 
+        if (os->bit_rate > 0)
+            snprintf(bandwidth_str, sizeof(bandwidth_str), " bandwidth=\"%d\"",
+                     os->bit_rate + os->muxer_overhead);
+
         if (as->media_type == AVMEDIA_TYPE_VIDEO) {
             AVStream *st = s->streams[i];
             avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
-                i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
+                i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
             if (st->avg_frame_rate.num)
                 avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
             avio_printf(out, ">\n");
         } else {
             avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
-                i, os->format_name, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate);
+                i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate);
             avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
                 s->streams[i]->codecpar->channels);
         }
@@ -822,25 +847,28 @@ static int write_manifest(AVFormatContext *s, int final)
         }
         av_dict_free(&opts);
 
-        ff_hls_write_playlist_version(out, 6);
+        ff_hls_write_playlist_version(out, 7);
 
         for (i = 0; i < s->nb_streams; i++) {
             char playlist_file[64];
             AVStream *st = s->streams[i];
+            OutputStream *os = &c->streams[i];
             if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
                 continue;
             get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i);
             ff_hls_write_audio_rendition(out, (char *)audio_group,
                                          playlist_file, i, is_default);
-            max_audio_bitrate = FFMAX(st->codecpar->bit_rate, max_audio_bitrate);
+            max_audio_bitrate = FFMAX(st->codecpar->bit_rate +
+                                      os->muxer_overhead, max_audio_bitrate);
             is_default = 0;
         }
 
         for (i = 0; i < s->nb_streams; i++) {
             char playlist_file[64];
             AVStream *st = s->streams[i];
+            OutputStream *os = &c->streams[i];
             char *agroup = NULL;
-            int stream_bitrate = st->codecpar->bit_rate;
+            int stream_bitrate = st->codecpar->bit_rate + os->muxer_overhead;
             if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && max_audio_bitrate) {
                 agroup = (char *)audio_group;
                 stream_bitrate += max_audio_bitrate;
@@ -915,10 +943,7 @@ static int dash_init(AVFormatContext *s)
         char filename[1024];
 
         os->bit_rate = s->streams[i]->codecpar->bit_rate;
-        if (os->bit_rate) {
-            snprintf(os->bandwidth_str, sizeof(os->bandwidth_str),
-                     " bandwidth=\"%d\"", os->bit_rate);
-        } else {
+        if (!os->bit_rate) {
             int level = s->strict_std_compliance >= FF_COMPLIANCE_STRICT ?
                         AV_LOG_ERROR : AV_LOG_WARNING;
             av_log(s, level, "No bit rate set for stream %d\n", i);
@@ -934,14 +959,23 @@ static int dash_init(AVFormatContext *s)
         if (!ctx)
             return AVERROR(ENOMEM);
 
-        // choose muxer based on codec: webm for VP8/9 and opus, mp4 otherwise
+        // choose muxer based on codec: webm for VP8 and opus, mp4 otherwise
         // note: os->format_name is also used as part of the mimetype of the
         //       representation, e.g. video/<format_name>
         if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 ||
-            s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP9 ||
             s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS ||
             s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) {
             snprintf(os->format_name, sizeof(os->format_name), "webm");
+
+            if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) {
+                av_log(s, AV_LOG_ERROR,
+                       "WebM support in dashenc is experimental and has not "
+                       "been validated. For testing purposes, make sure "
+                       "to add -strict experimental and override "
+                       "-init_seg_name and -media_seg_name to end with "
+                       "the extension 'webm'.\n");
+                return AVERROR(EINVAL);
+            }
         } else {
             snprintf(os->format_name, sizeof(os->format_name), "mp4");
         }
@@ -949,10 +983,11 @@ static int dash_init(AVFormatContext *s)
         if (!ctx->oformat)
             return AVERROR_MUXER_NOT_FOUND;
         os->ctx = ctx;
-        ctx->interrupt_callback = s->interrupt_callback;
-        ctx->opaque             = s->opaque;
-        ctx->io_close           = s->io_close;
-        ctx->io_open            = s->io_open;
+        ctx->interrupt_callback    = s->interrupt_callback;
+        ctx->opaque                = s->opaque;
+        ctx->io_close              = s->io_close;
+        ctx->io_open               = s->io_open;
+        ctx->strict_std_compliance = s->strict_std_compliance;
 
         if (!(st = avformat_new_stream(ctx, NULL)))
             return AVERROR(ENOMEM);
@@ -1002,13 +1037,6 @@ static int dash_init(AVFormatContext *s)
 
         av_log(s, AV_LOG_VERBOSE, "Representation %d init segment will be written to: %s\n", i, filename);
 
-        // Flush init segment
-        // except for mp4, since delay_moov is set and the init segment
-        // is then flushed after the first packets
-        if (strcmp(os->format_name, "mp4")) {
-            flush_init_segment(s, os);
-        }
-
         s->streams[i]->time_base = st->time_base;
         // If the muxer wants to shift timestamps, request to have them shifted
         // already before being handed to this muxer, so we don't have mismatches
@@ -1027,7 +1055,8 @@ static int dash_init(AVFormatContext *s)
             c->has_video = 1;
         }
 
-        set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str));
+        set_codec_str(s, st->codecpar, &st->avg_frame_rate, os->codec_str,
+                      sizeof(os->codec_str));
         os->first_pts = AV_NOPTS_VALUE;
         os->max_pts = AV_NOPTS_VALUE;
         os->last_dts = AV_NOPTS_VALUE;
@@ -1049,6 +1078,13 @@ static int dash_write_header(AVFormatContext *s)
         OutputStream *os = &c->streams[i];
         if ((ret = avformat_write_header(os->ctx, NULL)) < 0)
             return ret;
+
+        // Flush init segment
+        // Only for WebM segment, since for mp4 delay_moov is set and
+        // the init segment is thus flushed after the first packets.
+        if (strcmp(os->format_name, "mp4") &&
+            (ret = flush_init_segment(s, os)) < 0)
+            return ret;
     }
     return ret;
 }
@@ -1056,7 +1092,7 @@ static int dash_write_header(AVFormatContext *s)
 static int add_segment(OutputStream *os, const char *file,
                        int64_t time, int duration,
                        int64_t start_pos, int64_t range_length,
-                       int64_t index_length)
+                       int64_t index_length, int next_exp_index)
 {
     int err;
     Segment *seg;
@@ -1084,6 +1120,12 @@ static int add_segment(OutputStream *os, const char *file,
     seg->index_length = index_length;
     os->segments[os->nb_segments++] = seg;
     os->segment_index++;
+    //correcting the segment index if it has fallen behind the expected value
+    if (os->segment_index < next_exp_index) {
+        av_log(NULL, AV_LOG_WARNING, "Correcting the segment index after file %s: current=%d corrected=%d\n",
+               file, os->segment_index, next_exp_index);
+        os->segment_index = next_exp_index;
+    }
     return 0;
 }
 
@@ -1121,7 +1163,8 @@ static void find_index_range(AVFormatContext *s, const char *full_path,
 }
 
 static int update_stream_extradata(AVFormatContext *s, OutputStream *os,
-                                   AVCodecParameters *par)
+                                   AVCodecParameters *par,
+                                   AVRational *frame_rate)
 {
     uint8_t *extradata;
 
@@ -1138,7 +1181,7 @@ static int update_stream_extradata(AVFormatContext *s, OutputStream *os,
     os->ctx->streams[0]->codecpar->extradata = extradata;
     os->ctx->streams[0]->codecpar->extradata_size = par->extradata_size;
 
-    set_codec_str(s, par, os->codec_str, sizeof(os->codec_str));
+    set_codec_str(s, par, frame_rate, os->codec_str, sizeof(os->codec_str));
 
     return 0;
 }
@@ -1173,10 +1216,22 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
     const char *proto = avio_find_protocol_name(s->url);
     int use_rename = proto && !strcmp(proto, "file");
 
-    int cur_flush_segment_index = 0;
-    if (stream >= 0)
+    int cur_flush_segment_index = 0, next_exp_index = -1;
+    if (stream >= 0) {
         cur_flush_segment_index = c->streams[stream].segment_index;
 
+        //finding the next segment's expected index, based on the current pts value
+        if (c->use_template && !c->use_timeline && c->index_correction &&
+            c->streams[stream].last_pts != AV_NOPTS_VALUE &&
+            c->streams[stream].first_pts != AV_NOPTS_VALUE) {
+            int64_t pts_diff = av_rescale_q(c->streams[stream].last_pts -
+                                            c->streams[stream].first_pts,
+                                            s->streams[stream]->time_base,
+                                            AV_TIME_BASE_Q);
+            next_exp_index = (pts_diff / c->seg_duration) + 1;
+        }
+    }
+
     for (i = 0; i < s->nb_streams; i++) {
         OutputStream *os = &c->streams[i];
         AVStream *st = s->streams[i];
@@ -1233,13 +1288,10 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
             int64_t bitrate = (int64_t) range_length * 8 * AV_TIME_BASE / av_rescale_q(os->max_pts - os->start_pts,
                                                                                        st->time_base,
                                                                                        AV_TIME_BASE_Q);
-            if (bitrate >= 0) {
+            if (bitrate >= 0)
                 os->bit_rate = bitrate;
-                snprintf(os->bandwidth_str, sizeof(os->bandwidth_str),
-                     " bandwidth=\"%d\"", os->bit_rate);
-            }
         }
-        add_segment(os, os->filename, os->start_pts, os->max_pts - os->start_pts, os->pos, range_length, index_length);
+        add_segment(os, os->filename, os->start_pts, os->max_pts - os->start_pts, os->pos, range_length, index_length, next_exp_index);
         av_log(s, AV_LOG_VERBOSE, "Representation %d media segment %d written to: %s\n", i, os->segment_index, os->full_path);
 
         os->pos += range_length;
@@ -1278,7 +1330,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
     int64_t seg_end_duration, elapsed_duration;
     int ret;
 
-    ret = update_stream_extradata(s, os, st->codecpar);
+    ret = update_stream_extradata(s, os, st->codecpar, &st->avg_frame_rate);
     if (ret < 0)
         return ret;
 
@@ -1302,6 +1354,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
 
     if (os->first_pts == AV_NOPTS_VALUE)
         os->first_pts = pkt->pts;
+    os->last_pts = pkt->pts;
 
     if (!c->availability_start_time[0])
         format_date_now(c->availability_start_time,
@@ -1484,6 +1537,7 @@ static const AVOption options[] = {
     { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { "streaming", "Enable/Disable streaming mode of output. Each frame will be moof fragment", OFFSET(streaming), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { "timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E },
+    { "index_correction", "Enable/Disable segment index correction logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { NULL },
 };