lavf/dashenc: Add support for format-specific file extensions.
authorAndrey Semashev <andrey.semashev@gmail.com>
Sat, 17 Nov 2018 17:40:51 +0000 (20:40 +0300)
committerKarthick J <kjeyapal@akamai.com>
Wed, 21 Nov 2018 12:32:19 +0000 (18:02 +0530)
The file name template options now support a new "$ext$" placeholder,
which is replaced with a filename extension specific for the selected
file format. This is useful for the new "auto" format mode, when
different streams may use different file formats, and it is not
possible to specify the correct file name extension exactly.

Resolves warnings in the log about webm segments not having webm extensions.

doc/muxers.texi
libavformat/dashenc.c

index 2fed5cf..a02ac01 100644 (file)
@@ -245,11 +245,11 @@ Enable (1) or disable (0) use of SegmentTimeline in SegmentTemplate.
 @item -single_file @var{single_file}
 Enable (1) or disable (0) storing all segments in one file, accessed using byte ranges.
 @item -single_file_name @var{file_name}
-DASH-templated name to be used for baseURL. Implies @var{single_file} set to "1".
+DASH-templated name to be used for baseURL. Implies @var{single_file} set to "1". In the template, "$ext$" is replaced with the file name extension specific for the segment format.
 @item -init_seg_name @var{init_name}
-DASH-templated name to used for the initialization segment. Default is "init-stream$RepresentationID$.m4s"
+DASH-templated name to used for the initialization segment. Default is "init-stream$RepresentationID$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format.
 @item -media_seg_name @var{segment_name}
-DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.m4s"
+DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format.
 @item -utc_timing_url @var{utc_url}
 URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"
 @item method @var{method}
index 0af7b85..f552503 100644 (file)
@@ -87,6 +87,9 @@ typedef struct OutputStream {
     int bit_rate;
     SegmentType segment_type;  /* segment type selected for this particular stream */
     const char *format_name;
+    const char *single_file_name;  /* file names selected for this particular stream */
+    const char *init_seg_name;
+    const char *media_seg_name;
 
     char codec_str[100];
     int written_len;
@@ -119,7 +122,7 @@ typedef struct DASHContext {
     int64_t total_duration;
     char availability_start_time[100];
     char dirname[1024];
-    const char *single_file_name;
+    const char *single_file_name;  /* file names as specified in options */
     const char *init_seg_name;
     const char *media_seg_name;
     const char *utc_timing_url;
@@ -200,7 +203,7 @@ static const char *get_format_str(SegmentType segment_type) {
     return NULL;
 }
 
-static inline SegmentType select_segment_type(SegmentType segment_type, AVCodecID codec_id)
+static inline SegmentType select_segment_type(SegmentType segment_type, enum AVCodecID codec_id)
 {
     if (segment_type == SEGMENT_TYPE_AUTO) {
         if (codec_id == AV_CODEC_ID_OPUS || codec_id == AV_CODEC_ID_VORBIS ||
@@ -425,6 +428,9 @@ static void dash_free(AVFormatContext *s)
         for (j = 0; j < os->nb_segments; j++)
             av_free(os->segments[j]);
         av_free(os->segments);
+        av_freep(&os->single_file_name);
+        av_freep(&os->init_seg_name);
+        av_freep(&os->media_seg_name);
     }
     av_freep(&c->streams);
 
@@ -451,7 +457,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont
                 avio_printf(out, "availabilityTimeOffset=\"%.3f\" ",
                             os->availability_time_offset);
         }
-        avio_printf(out, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\">\n", c->init_seg_name, c->media_seg_name, c->use_timeline ? start_number : 1);
+        avio_printf(out, "initialization=\"%s\" media=\"%s\" startNumber=\"%d\">\n", os->init_seg_name, os->media_seg_name, c->use_timeline ? start_number : 1);
         if (c->use_timeline) {
             int64_t cur_time = 0;
             avio_printf(out, "\t\t\t\t\t<SegmentTimeline>\n");
@@ -1056,10 +1062,26 @@ static int dash_init(AVFormatContext *s)
         if (!ctx)
             return AVERROR(ENOMEM);
 
+        if (c->init_seg_name) {
+            os->init_seg_name = av_strireplace(c->init_seg_name, "$ext$", os->format_name);
+            if (!os->init_seg_name)
+                return AVERROR(ENOMEM);
+        }
+        if (c->media_seg_name) {
+            os->media_seg_name = av_strireplace(c->media_seg_name, "$ext$", os->format_name);
+            if (!os->media_seg_name)
+                return AVERROR(ENOMEM);
+        }
+        if (c->single_file_name) {
+            os->single_file_name = av_strireplace(c->single_file_name, "$ext$", os->format_name);
+            if (!os->single_file_name)
+                return AVERROR(ENOMEM);
+        }
+
         if (os->segment_type == SEGMENT_TYPE_WEBM) {
-            if ((!c->single_file && check_file_extension(c->init_seg_name, os->format_name) != 0) ||
-                (!c->single_file && check_file_extension(c->media_seg_name, os->format_name) != 0) ||
-                (c->single_file && check_file_extension(c->single_file_name, os->format_name) != 0)) {
+            if ((!c->single_file && check_file_extension(os->init_seg_name, os->format_name) != 0) ||
+                (!c->single_file && check_file_extension(os->media_seg_name, os->format_name) != 0) ||
+                (c->single_file && check_file_extension(os->single_file_name, os->format_name) != 0)) {
                 av_log(s, AV_LOG_WARNING,
                        "One or many segment file names doesn't end with .webm. "
                        "Override -init_seg_name and/or -media_seg_name and/or "
@@ -1090,12 +1112,12 @@ static int dash_init(AVFormatContext *s)
             return ret;
 
         if (c->single_file) {
-            if (c->single_file_name)
-                ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->single_file_name, i, 0, os->bit_rate, 0);
+            if (os->single_file_name)
+                ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), os->single_file_name, i, 0, os->bit_rate, 0);
             else
-                snprintf(os->initfile, sizeof(os->initfile), "%s-stream%d.m4s", basename, i);
+                snprintf(os->initfile, sizeof(os->initfile), "%s-stream%d.%s", basename, i, os->format_name);
         } else {
-            ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->init_seg_name, i, 0, os->bit_rate, 0);
+            ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), os->init_seg_name, i, 0, os->bit_rate, 0);
         }
         snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile);
         set_http_options(&opts, c);
@@ -1523,7 +1545,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
         int use_rename = proto && !strcmp(proto, "file");
         os->filename[0] = os->full_path[0] = os->temp_path[0] = '\0';
         ff_dash_fill_tmpl_params(os->filename, sizeof(os->filename),
-                                 c->media_seg_name, pkt->stream_index,
+                                 os->media_seg_name, pkt->stream_index,
                                  os->segment_index, os->bit_rate, os->start_pts);
         snprintf(os->full_path, sizeof(os->full_path), "%s%s", c->dirname,
                  os->filename);
@@ -1622,8 +1644,8 @@ static const AVOption options[] = {
     { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E },
     { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { "single_file_name", "DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges", OFFSET(single_file_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
-    { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.m4s"}, 0, 0, E },
-    { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E },
+    { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.$ext$"}, 0, 0, E },
+    { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.$ext$"}, 0, 0, E },
     { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E },
     { "method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
     { "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},