]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/dashenc.c
bump micro after recent gif changes
[ffmpeg] / libavformat / dashenc.c
index 2c872f93a17412ba06db7896b5f75003510572da..585b34cb97af138b416b865074eefa99553615ce 100644 (file)
@@ -60,7 +60,7 @@ typedef struct Segment {
     int64_t start_pos;
     int range_length, index_length;
     int64_t time;
-    int duration;
+    int64_t duration;
     int n;
 } Segment;
 
@@ -138,6 +138,7 @@ typedef struct DASHContext {
     int index_correction;
     char *format_options_str;
     SegmentType segment_type_option;  /* segment type as specified in options */
+    int ignore_io_errors;
 } DASHContext;
 
 static struct codec_string {
@@ -174,6 +175,8 @@ static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename,
         URLContext *http_url_context = ffio_geturlcontext(*pb);
         av_assert0(http_url_context);
         err = ff_http_do_new_request(http_url_context, filename);
+        if (err < 0)
+            ff_format_io_close(s, pb);
 #endif
     }
     return err;
@@ -183,6 +186,9 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam
     DASHContext *c = s->priv_data;
     int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
 
+    if (!*pb)
+        return;
+
     if (!http_base_proto || !c->http_persistent) {
         ff_format_io_close(s, pb);
 #if CONFIG_HTTP_PROTOCOL
@@ -203,6 +209,15 @@ static const char *get_format_str(SegmentType segment_type) {
     return NULL;
 }
 
+static int handle_io_open_error(AVFormatContext *s, int err, char *url) {
+    DASHContext *c = s->priv_data;
+    char errbuf[AV_ERROR_MAX_STRING_SIZE];
+    av_strerror(err, errbuf, sizeof(errbuf));
+    av_log(s, c->ignore_io_errors ? AV_LOG_WARNING : AV_LOG_ERROR,
+           "Unable to open %s for writing: %s\n", url, errbuf);
+    return c->ignore_io_errors ? 0 : err;
+}
+
 static inline SegmentType select_segment_type(SegmentType segment_type, enum AVCodecID codec_id)
 {
     if (segment_type == SEGMENT_TYPE_AUTO) {
@@ -220,6 +235,7 @@ static inline SegmentType select_segment_type(SegmentType segment_type, enum AVC
 static int init_segment_types(AVFormatContext *s)
 {
     DASHContext *c = s->priv_data;
+    int has_mp4_streams = 0;
     for (int i = 0; i < s->nb_streams; ++i) {
         OutputStream *os = &c->streams[i];
         SegmentType segment_type = select_segment_type(
@@ -230,6 +246,12 @@ static int init_segment_types(AVFormatContext *s)
             av_log(s, AV_LOG_ERROR, "Could not select DASH segment type for stream %d\n", i);
             return AVERROR_MUXER_NOT_FOUND;
         }
+        has_mp4_streams |= segment_type == SEGMENT_TYPE_MP4;
+    }
+
+    if (c->hls_playlist && !has_mp4_streams) {
+         av_log(s, AV_LOG_WARNING, "No mp4 streams, disabling HLS manifest generation\n");
+         c->hls_playlist = 0;
     }
 
     return 0;
@@ -356,7 +378,8 @@ static int flush_dynbuf(OutputStream *os, int *range_length)
     // write out to file
     *range_length = avio_close_dyn_buf(os->ctx->pb, &buffer);
     os->ctx->pb = NULL;
-    avio_write(os->out, buffer + os->written_len, *range_length - os->written_len);
+    if (os->out)
+        avio_write(os->out, buffer + os->written_len, *range_length - os->written_len);
     os->written_len = 0;
     av_free(buffer);
 
@@ -418,8 +441,6 @@ static void dash_free(AVFormatContext *s)
         return;
     for (i = 0; i < s->nb_streams; i++) {
         OutputStream *os = &c->streams[i];
-        if (os->ctx && os->ctx_inited)
-            av_write_trailer(os->ctx);
         if (os->ctx && os->ctx->pb)
             ffio_free_dyn_buf(&os->ctx->pb);
         ff_format_io_close(s, &os->out);
@@ -469,7 +490,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont
                     cur_time = seg->time;
                     avio_printf(out, "t=\"%"PRId64"\" ", seg->time);
                 }
-                avio_printf(out, "d=\"%d\" ", seg->duration);
+                avio_printf(out, "d=\"%"PRId64"\" ", seg->duration);
                 while (i + repeat + 1 < os->nb_segments &&
                        os->segments[i + repeat + 1]->duration == seg->duration &&
                        os->segments[i + repeat + 1]->time == os->segments[i + repeat]->time + os->segments[i + repeat]->duration)
@@ -504,7 +525,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont
         }
         avio_printf(out, "\t\t\t\t</SegmentList>\n");
     }
-    if (c->hls_playlist && start_index < os->nb_segments)
+    if (c->hls_playlist && start_index < os->nb_segments && os->segment_type == SEGMENT_TYPE_MP4)
     {
         int timescale = os->ctx->streams[0]->time_base.den;
         char temp_filename_hls[1024];
@@ -521,8 +542,12 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont
         snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls);
 
         set_http_options(&http_opts, c);
-        dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts);
+        ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts);
         av_dict_free(&http_opts);
+        if (ret < 0) {
+            handle_io_open_error(s, ret, temp_filename_hls);
+            return;
+        }
         for (i = start_index; i < os->nb_segments; i++) {
             Segment *seg = os->segments[i];
             double duration = (double) seg->duration / timescale;
@@ -835,8 +860,7 @@ static int write_manifest(AVFormatContext *s, int final)
     ret = dashenc_io_open(s, &c->mpd_out, temp_filename, &opts);
     av_dict_free(&opts);
     if (ret < 0) {
-        av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
-        return ret;
+        return handle_io_open_error(s, ret, temp_filename);
     }
     out = c->mpd_out;
     avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
@@ -926,8 +950,7 @@ static int write_manifest(AVFormatContext *s, int final)
         ret = dashenc_io_open(s, &c->m3u8_out, temp_filename, &opts);
         av_dict_free(&opts);
         if (ret < 0) {
-            av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
-            return ret;
+            return handle_io_open_error(s, ret, temp_filename);
         }
 
         ff_hls_write_playlist_version(c->m3u8_out, 7);
@@ -938,6 +961,8 @@ static int write_manifest(AVFormatContext *s, int final)
             OutputStream *os = &c->streams[i];
             if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
                 continue;
+            if (os->segment_type != SEGMENT_TYPE_MP4)
+                continue;
             get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i);
             ff_hls_write_audio_rendition(c->m3u8_out, (char *)audio_group,
                                          playlist_file, i, is_default);
@@ -961,6 +986,8 @@ static int write_manifest(AVFormatContext *s, int final)
             int stream_bitrate = st->codecpar->bit_rate + os->muxer_overhead;
             if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
                 continue;
+            if (os->segment_type != SEGMENT_TYPE_MP4)
+                continue;
             av_strlcpy(codec_str, os->codec_str, sizeof(codec_str));
             if (max_audio_bitrate) {
                 agroup = (char *)audio_group;
@@ -1135,7 +1162,7 @@ static int dash_init(AVFormatContext *s)
 
         if (os->segment_type == SEGMENT_TYPE_MP4) {
             if (c->streaming)
-                av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0);
+                av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx", 0);
             else
                 av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
         } else {
@@ -1207,7 +1234,7 @@ static int dash_write_header(AVFormatContext *s)
 }
 
 static int add_segment(OutputStream *os, const char *file,
-                       int64_t time, int duration,
+                       int64_t time, int64_t duration,
                        int64_t start_pos, int64_t range_length,
                        int64_t index_length, int next_exp_index)
 {
@@ -1320,9 +1347,55 @@ static void dashenc_delete_file(AVFormatContext *s, char *filename) {
 
         av_dict_free(&http_opts);
         ff_format_io_close(s, &out);
-    } else if (unlink(filename) < 0) {
-        av_log(s, AV_LOG_ERROR, "failed to delete %s: %s\n", filename, strerror(errno));
+    } else {
+        int res = avpriv_io_delete(filename);
+        if (res < 0) {
+            char errbuf[AV_ERROR_MAX_STRING_SIZE];
+            av_strerror(res, errbuf, sizeof(errbuf));
+            av_log(s, (res == AVERROR(ENOENT) ? AV_LOG_WARNING : AV_LOG_ERROR), "failed to delete %s: %s\n", filename, errbuf);
+        }
+    }
+}
+
+static int dashenc_delete_segment_file(AVFormatContext *s, const char* file)
+{
+    DASHContext *c = s->priv_data;
+    size_t dirname_len, file_len;
+    char filename[1024];
+
+    dirname_len = strlen(c->dirname);
+    if (dirname_len >= sizeof(filename)) {
+        av_log(s, AV_LOG_WARNING, "Cannot delete segments as the directory path is too long: %"PRIu64" characters: %s\n",
+            (uint64_t)dirname_len, c->dirname);
+        return AVERROR(ENAMETOOLONG);
+    }
+
+    memcpy(filename, c->dirname, dirname_len);
+
+    file_len = strlen(file);
+    if ((dirname_len + file_len) >= sizeof(filename)) {
+        av_log(s, AV_LOG_WARNING, "Cannot delete segments as the path is too long: %"PRIu64" characters: %s%s\n",
+            (uint64_t)(dirname_len + file_len), c->dirname, file);
+        return AVERROR(ENAMETOOLONG);
+    }
+
+    memcpy(filename + dirname_len, file, file_len + 1); // include the terminating zero
+    dashenc_delete_file(s, filename);
+
+    return 0;
+}
+
+static inline void dashenc_delete_media_segments(AVFormatContext *s, OutputStream *os, int remove_count)
+{
+    for (int i = 0; i < remove_count; ++i) {
+        dashenc_delete_segment_file(s, os->segments[i]->file);
+
+        // Delete the segment regardless of whether the file was successfully deleted
+        av_free(os->segments[i]);
     }
+
+    os->nb_segments -= remove_count;
+    memmove(os->segments, os->segments + remove_count, os->nb_segments * sizeof(*os->segments));
 }
 
 static int dash_flush(AVFormatContext *s, int final, int stream)
@@ -1414,23 +1487,12 @@ static int dash_flush(AVFormatContext *s, int final, int stream)
         os->pos += range_length;
     }
 
-    if (c->window_size || (final && c->remove_at_exit)) {
+    if (c->window_size) {
         for (i = 0; i < s->nb_streams; i++) {
             OutputStream *os = &c->streams[i];
-            int j;
-            int remove = os->nb_segments - c->window_size - c->extra_window_size;
-            if (final && c->remove_at_exit)
-                remove = os->nb_segments;
-            if (remove > 0) {
-                for (j = 0; j < remove; j++) {
-                    char filename[1024];
-                    snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->segments[j]->file);
-                    dashenc_delete_file(s, filename);
-                    av_free(os->segments[j]);
-                }
-                os->nb_segments -= remove;
-                memmove(os->segments, os->segments + remove, os->nb_segments * sizeof(*os->segments));
-            }
+            int remove_count = os->nb_segments - c->window_size - c->extra_window_size;
+            if (remove_count > 0)
+                dashenc_delete_media_segments(s, os, remove_count);
         }
     }
 
@@ -1555,8 +1617,9 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
         set_http_options(&opts, c);
         ret = dashenc_io_open(s, &os->out, os->temp_path, &opts);
         av_dict_free(&opts);
-        if (ret < 0)
-            return ret;
+        if (ret < 0) {
+            return handle_io_open_error(s, ret, os->temp_path);
+        }
     }
 
     //write out the data immediately in streaming mode
@@ -1567,9 +1630,11 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
             write_styp(os->ctx->pb);
         avio_flush(os->ctx->pb);
         len = avio_get_dyn_buf (os->ctx->pb, &buf);
-        avio_write(os->out, buf + os->written_len, len - os->written_len);
+        if (os->out) {
+            avio_write(os->out, buf + os->written_len, len - os->written_len);
+            avio_flush(os->out);
+        }
         os->written_len = len;
-        avio_flush(os->out);
     }
 
     return ret;
@@ -1578,6 +1643,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
 static int dash_write_trailer(AVFormatContext *s)
 {
     DASHContext *c = s->priv_data;
+    int i;
 
     if (s->nb_streams > 0) {
         OutputStream *os = &c->streams[0];
@@ -1593,15 +1659,33 @@ static int dash_write_trailer(AVFormatContext *s)
     }
     dash_flush(s, 1, -1);
 
+    for (i = 0; i < s->nb_streams; ++i) {
+        OutputStream *os = &c->streams[i];
+        if (os->ctx && os->ctx_inited) {
+            av_write_trailer(os->ctx);
+        }
+
+        if (c->remove_at_exit) {
+            dashenc_delete_media_segments(s, os, os->nb_segments);
+            dashenc_delete_segment_file(s, os->initfile);
+        }
+    }
+
     if (c->remove_at_exit) {
-        char filename[1024];
-        int i;
-        for (i = 0; i < s->nb_streams; i++) {
-            OutputStream *os = &c->streams[i];
-            snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile);
+        dashenc_delete_file(s, s->url);
+
+        if (c->hls_playlist && c->master_playlist_created) {
+            char filename[1024];
+            for (i = 0; i < s->nb_streams; ++i) {
+                OutputStream *os = &c->streams[i];
+                if (os->segment_type == SEGMENT_TYPE_MP4) {
+                    get_hls_playlist_name(filename, sizeof(filename), c->dirname, i);
+                    dashenc_delete_file(s, filename);
+                }
+            }
+            snprintf(filename, sizeof(filename), "%smaster.m3u8", c->dirname);
             dashenc_delete_file(s, filename);
         }
-        dashenc_delete_file(s, s->url);
     }
 
     return 0;
@@ -1660,6 +1744,7 @@ static const AVOption options[] = {
     { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX,   E, "segment_type"},
     { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX,   E, "segment_type"},
     { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX,   E, "segment_type"},
+    { "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
     { NULL },
 };