aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Suloway <csuloway@row44.com>2014-12-08 10:25:05 -0600
committerMichael Niedermayer <michaelni@gmx.at>2014-12-10 23:49:25 +0100
commit97b65f612617b9e238c8f95bcce82be145c2fafd (patch)
tree363c5cfa8780567c253dbb6975fcbd18548dcfce
parentae93965359e71c1f88ba170f8efd6a198344c235 (diff)
downloadffmpeg-97b65f612617b9e238c8f95bcce82be145c2fafd.tar.gz
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>
-rw-r--r--doc/muxers.texi4
-rw-r--r--libavformat/hlsenc.c90
2 files changed, 90 insertions, 4 deletions
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 34e827cde7..a1264d2125 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -273,6 +273,10 @@ ffmpeg -i in.nut -hls_flags single_file out.m3u8
@end example
Will produce the playlist, @file{out.m3u8}, and a single segment file,
@file{out.ts}.
+
+@item hls_flags delete_segments
+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
@anchor{ico}
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index d5ea990476..79f3a2311d 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -19,8 +19,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "config.h"
#include <float.h>
#include <stdint.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#include "libavutil/avassert.h"
#include "libavutil/mathematics.h"
@@ -31,6 +35,7 @@
#include "avformat.h"
#include "internal.h"
+#include "os_support.h"
typedef struct HLSSegment {
char filename[1024];
@@ -44,6 +49,7 @@ typedef struct HLSSegment {
typedef enum HLSFlags {
// Generate a single media file and use byte ranges in the playlist.
HLS_SINGLE_FILE = (1 << 0),
+ HLS_DELETE_SEGMENTS = (1 << 1),
} HLSFlags;
typedef struct HLSContext {
@@ -73,6 +79,7 @@ typedef struct HLSContext {
HLSSegment *segments;
HLSSegment *last_segment;
+ HLSSegment *old_segments;
char *basename;
char *baseurl;
@@ -82,6 +89,71 @@ typedef struct HLSContext {
AVIOContext *pb;
} HLSContext;
+static int hls_delete_old_segments(HLSContext *hls) {
+
+ HLSSegment *segment, *previous_segment = NULL;
+ float playlist_duration = 0.0f;
+ int ret = 0, path_size;
+ char *dirname = NULL, *p, *path;
+
+ segment = hls->segments;
+ while (segment) {
+ playlist_duration += segment->duration;
+ segment = segment->next;
+ }
+
+ segment = hls->old_segments;
+ while (segment) {
+ playlist_duration -= segment->duration;
+ previous_segment = segment;
+ segment = previous_segment->next;
+ if (playlist_duration <= -previous_segment->duration) {
+ previous_segment->next = NULL;
+ break;
+ }
+ }
+
+ if (segment) {
+ if (hls->segment_filename) {
+ dirname = av_strdup(hls->segment_filename);
+ } else {
+ dirname = av_strdup(hls->avf->filename);
+ }
+ if (!dirname) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ p = (char *)av_basename(dirname);
+ *p = '\0';
+ }
+
+ while (segment) {
+ av_log(hls, 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(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
+ path, strerror(errno));
+ }
+ av_free(path);
+ previous_segment = segment;
+ segment = previous_segment->next;
+ av_free(previous_segment);
+ }
+
+fail:
+ av_free(dirname);
+
+ return ret;
+}
+
static int hls_mux_init(AVFormatContext *s)
{
HLSContext *hls = s->priv_data;
@@ -116,6 +188,7 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
int64_t size)
{
HLSSegment *en = av_malloc(sizeof(*en));
+ int ret;
if (!en)
return AVERROR(ENOMEM);
@@ -137,7 +210,14 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
en = hls->segments;
hls->segments = en->next;
- av_free(en);
+ if (en && hls->flags & HLS_DELETE_SEGMENTS &&
+ !(hls->flags & HLS_SINGLE_FILE || hls->wrap)) {
+ en->next = hls->old_segments;
+ hls->old_segments = en;
+ if ((ret = hls_delete_old_segments(hls)) < 0)
+ return ret;
+ } else
+ av_free(en);
} else
hls->nb_entries++;
@@ -146,9 +226,9 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
return 0;
}
-static void hls_free_segments(HLSContext *hls)
+static void hls_free_segments(HLSSegment *p)
{
- HLSSegment *p = hls->segments, *en;
+ HLSSegment *en;
while(p) {
en = p;
@@ -402,7 +482,8 @@ static int hls_write_trailer(struct AVFormatContext *s)
hls->avf = NULL;
hls_window(s, 1);
- hls_free_segments(hls);
+ hls_free_segments(hls->segments);
+ hls_free_segments(hls->old_segments);
avio_close(hls->pb);
return 0;
}
@@ -420,6 +501,7 @@ static const AVOption options[] = {
{"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
{"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
{"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"},
+ {"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"},
{ NULL },
};