diff mbox

[FFmpeg-devel,1/2] avformat/segment: add delete segment_list flag

Message ID 20161008140242.11665-1-ffmpeg-dev@c-14.de
State New
Headers show

Commit Message

Simon Thelen Oct. 8, 2016, 2:02 p.m. UTC
Works in the same manner as the hls segment_delete flag.

Signed-off-by: Simon Thelen <ffmpeg-dev@c-14.de>
---
 doc/muxers.texi       |  5 +++
 libavformat/segment.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+)
diff mbox

Patch

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 9ec2e31..4002473 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -1228,6 +1228,11 @@  Allow caching (only affects M3U8 list files).
 
 @item live
 Allow live-friendly file generation.
+
+@item delete
+Segment files removed from the playlist are deleted after a period of time
+equal to the duration of the segment plus the duration of the playlist.
+
 @end table
 
 @item segment_list_size @var{size}
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 33a5cf0..82872fa 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -26,8 +26,12 @@ 
 
 /* #define DEBUG */
 
+#include "config.h"
 #include <float.h>
 #include <time.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include "avformat.h"
 #include "avio_internal.h"
@@ -67,6 +71,7 @@  typedef enum {
 
 #define SEGMENT_LIST_FLAG_CACHE 1
 #define SEGMENT_LIST_FLAG_LIVE  2
+#define SEGMENT_LIST_FLAG_DELETE 4
 
 typedef struct SegmentContext {
     const AVClass *class;  /**< Class for private options. */
@@ -126,6 +131,7 @@  typedef struct SegmentContext {
     SegmentListEntry cur_entry;
     SegmentListEntry *segment_list_entries;
     SegmentListEntry *segment_list_entries_end;
+    SegmentListEntry *segment_list_old;
 } SegmentContext;
 
 static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
@@ -144,6 +150,72 @@  static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
         avio_w8(ctx, '"');
 }
 
+static int delete_old_segments(SegmentContext *seg)
+{
+    SegmentListEntry *segment, *previous_segment = NULL;
+    float playlist_duration = 0.0f;
+    int ret = 0, path_size;
+    char *dirname = NULL, *p;
+    char *path = NULL;
+
+    segment = seg->segment_list_entries;
+    while (segment) {
+        playlist_duration += segment->end_time - segment->start_time;
+        segment = segment->next;
+    }
+
+    segment = seg->segment_list_old;
+    while (segment) {
+        playlist_duration -= segment->end_time - segment->start_time;
+        previous_segment = segment;
+        segment = previous_segment->next;
+        if (playlist_duration <= -(previous_segment->end_time - previous_segment->start_time)) {
+            previous_segment->next = NULL;
+            break;
+        }
+    }
+
+    if (segment) {
+        dirname = av_strdup(seg->avf->filename);
+        if (!dirname) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        p = (char *)av_basename(dirname);
+        *p = '\0';
+    }
+
+    while (segment) {
+        av_log(seg, AV_LOG_DEBUG, "deleting old segment %s\n",
+                                  segment->filename);
+        path_size = strlen(dirname) + strlen(segment->filename) + 1;
+        path = av_malloc(path_size);
+        if (!path) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        av_strlcpy(path, dirname, path_size);
+        av_strlcat(path, segment->filename, path_size);
+        if (unlink(path) < 0) {
+            av_log(segment, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
+                                          path, strerror(errno));
+        }
+        av_freep(&path);
+        previous_segment = segment;
+        segment = previous_segment->next;
+        av_freep(&previous_segment->filename);
+        av_free(previous_segment);
+    }
+
+fail:
+    av_free(path);
+    av_free(dirname);
+
+    return ret;
+}
+
+
 static int segment_mux_init(AVFormatContext *s)
 {
     SegmentContext *seg = s->priv_data;
@@ -381,8 +453,16 @@  static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
             if (seg->list_size && seg->segment_count >= seg->list_size) {
                 entry = seg->segment_list_entries;
                 seg->segment_list_entries = seg->segment_list_entries->next;
+                if (entry && seg->list_flags & SEGMENT_LIST_FLAG_DELETE &&
+                        !seg->segment_idx_wrap) {
+                    entry->next = seg->segment_list_old;
+                    seg->segment_list_old = entry;
+                    if ((ret = delete_old_segments(seg)) < 0)
+                        return ret;
+                } else {
                 av_freep(&entry->filename);
                 av_freep(&entry);
+                }
             }
 
             if ((ret = segment_list_open(s)) < 0)
@@ -957,6 +1037,14 @@  fail:
         cur = next;
     }
 
+    cur = seg->segment_list_old;
+    while (cur) {
+        next = cur->next;
+        av_freep(&cur->filename);
+        av_free(cur);
+        cur = next;
+    }
+
     avformat_free_context(oc);
     seg->avf = NULL;
     return ret;
@@ -974,6 +1062,7 @@  static const AVOption options[] = {
     { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"},
     { "cache",             "allow list caching",                                    0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX,   E, "list_flags"},
     { "live",              "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX,    E, "list_flags"},
+    { "delete",            "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_DELETE }, INT_MIN, INT_MAX,    E, "list_flags"},
 
     { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.i64 = 0},     0, INT_MAX, E },