Browse code

avformat/hlsenc: added segment file deletion

This option flag deletes segment files removed from the playlist after a
period of time equal to the duration of the segment plus the duration of
the playlist.

Signed-off-by: Christian Suloway <csuloway@globaleagleent.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Christian Suloway authored on 2014/12/09 01:25:05
Showing 2 changed files
... ...
@@ -273,6 +273,10 @@ ffmpeg -i in.nut -hls_flags single_file out.m3u8
273 273
 @end example
274 274
 Will produce the playlist, @file{out.m3u8}, and a single segment file,
275 275
 @file{out.ts}.
276
+
277
+@item hls_flags delete_segments
278
+Segment files removed from the playlist are deleted after a period of time
279
+equal to the duration of the segment plus the duration of the playlist.
276 280
 @end table
277 281
 
278 282
 @anchor{ico}
... ...
@@ -19,8 +19,12 @@
19 19
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 20
  */
21 21
 
22
+#include "config.h"
22 23
 #include <float.h>
23 24
 #include <stdint.h>
25
+#if HAVE_UNISTD_H
26
+#include <unistd.h>
27
+#endif
24 28
 
25 29
 #include "libavutil/avassert.h"
26 30
 #include "libavutil/mathematics.h"
... ...
@@ -31,6 +35,7 @@
31 31
 
32 32
 #include "avformat.h"
33 33
 #include "internal.h"
34
+#include "os_support.h"
34 35
 
35 36
 typedef struct HLSSegment {
36 37
     char filename[1024];
... ...
@@ -44,6 +49,7 @@ typedef struct HLSSegment {
44 44
 typedef enum HLSFlags {
45 45
     // Generate a single media file and use byte ranges in the playlist.
46 46
     HLS_SINGLE_FILE = (1 << 0),
47
+    HLS_DELETE_SEGMENTS = (1 << 1),
47 48
 } HLSFlags;
48 49
 
49 50
 typedef struct HLSContext {
... ...
@@ -73,6 +79,7 @@ typedef struct HLSContext {
73 73
 
74 74
     HLSSegment *segments;
75 75
     HLSSegment *last_segment;
76
+    HLSSegment *old_segments;
76 77
 
77 78
     char *basename;
78 79
     char *baseurl;
... ...
@@ -82,6 +89,71 @@ typedef struct HLSContext {
82 82
     AVIOContext *pb;
83 83
 } HLSContext;
84 84
 
85
+static int hls_delete_old_segments(HLSContext *hls) {
86
+
87
+    HLSSegment *segment, *previous_segment = NULL;
88
+    float playlist_duration = 0.0f;
89
+    int ret = 0, path_size;
90
+    char *dirname = NULL, *p, *path;
91
+
92
+    segment = hls->segments;
93
+    while (segment) {
94
+        playlist_duration += segment->duration;
95
+        segment = segment->next;
96
+    }
97
+
98
+    segment = hls->old_segments;
99
+    while (segment) {
100
+        playlist_duration -= segment->duration;
101
+        previous_segment = segment;
102
+        segment = previous_segment->next;
103
+        if (playlist_duration <= -previous_segment->duration) {
104
+            previous_segment->next = NULL;
105
+            break;
106
+        }
107
+    }
108
+
109
+    if (segment) {
110
+        if (hls->segment_filename) {
111
+            dirname = av_strdup(hls->segment_filename);
112
+        } else {
113
+            dirname = av_strdup(hls->avf->filename);
114
+        }
115
+        if (!dirname) {
116
+            ret = AVERROR(ENOMEM);
117
+            goto fail;
118
+        }
119
+        p = (char *)av_basename(dirname);
120
+        *p = '\0';
121
+    }
122
+
123
+    while (segment) {
124
+        av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
125
+                                  segment->filename);
126
+        path_size = strlen(dirname) + strlen(segment->filename) + 1;
127
+        path = av_malloc(path_size);
128
+        if (!path) {
129
+            ret = AVERROR(ENOMEM);
130
+            goto fail;
131
+        }
132
+        av_strlcpy(path, dirname, path_size);
133
+        av_strlcat(path, segment->filename, path_size);
134
+        if (unlink(path) < 0) {
135
+            av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
136
+                                     path, strerror(errno));
137
+        }
138
+        av_free(path);
139
+        previous_segment = segment;
140
+        segment = previous_segment->next;
141
+        av_free(previous_segment);
142
+    }
143
+
144
+fail:
145
+    av_free(dirname);
146
+
147
+    return ret;
148
+}
149
+
85 150
 static int hls_mux_init(AVFormatContext *s)
86 151
 {
87 152
     HLSContext *hls = s->priv_data;
... ...
@@ -116,6 +188,7 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
116 116
                               int64_t size)
117 117
 {
118 118
     HLSSegment *en = av_malloc(sizeof(*en));
119
+    int ret;
119 120
 
120 121
     if (!en)
121 122
         return AVERROR(ENOMEM);
... ...
@@ -137,7 +210,14 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
137 137
     if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
138 138
         en = hls->segments;
139 139
         hls->segments = en->next;
140
-        av_free(en);
140
+        if (en && hls->flags & HLS_DELETE_SEGMENTS &&
141
+                !(hls->flags & HLS_SINGLE_FILE || hls->wrap)) {
142
+            en->next = hls->old_segments;
143
+            hls->old_segments = en;
144
+            if ((ret = hls_delete_old_segments(hls)) < 0)
145
+                return ret;
146
+        } else
147
+            av_free(en);
141 148
     } else
142 149
         hls->nb_entries++;
143 150
 
... ...
@@ -146,9 +226,9 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
146 146
     return 0;
147 147
 }
148 148
 
149
-static void hls_free_segments(HLSContext *hls)
149
+static void hls_free_segments(HLSSegment *p)
150 150
 {
151
-    HLSSegment *p = hls->segments, *en;
151
+    HLSSegment *en;
152 152
 
153 153
     while(p) {
154 154
         en = p;
... ...
@@ -402,7 +482,8 @@ static int hls_write_trailer(struct AVFormatContext *s)
402 402
     hls->avf = NULL;
403 403
     hls_window(s, 1);
404 404
 
405
-    hls_free_segments(hls);
405
+    hls_free_segments(hls->segments);
406
+    hls_free_segments(hls->old_segments);
406 407
     avio_close(hls->pb);
407 408
     return 0;
408 409
 }
... ...
@@ -420,6 +501,7 @@ static const AVOption options[] = {
420 420
     {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename),   AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
421 421
     {"hls_flags",     "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
422 422
     {"single_file",   "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX,   E, "flags"},
423
+    {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX,   E, "flags"},
423 424
 
424 425
     { NULL },
425 426
 };