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>
... | ... |
@@ -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 |
}; |