Commit 97b65f61 authored by Christian Suloway's avatar Christian Suloway Committed by Michael Niedermayer

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: 's avatarChristian Suloway <csuloway@globaleagleent.com>
Signed-off-by: 's avatarMichael Niedermayer <michaelni@gmx.at>
parent ae939653
...@@ -273,6 +273,10 @@ ffmpeg -i in.nut -hls_flags single_file out.m3u8 ...@@ -273,6 +273,10 @@ ffmpeg -i in.nut -hls_flags single_file out.m3u8
@end example @end example
Will produce the playlist, @file{out.m3u8}, and a single segment file, Will produce the playlist, @file{out.m3u8}, and a single segment file,
@file{out.ts}. @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 @end table
@anchor{ico} @anchor{ico}
......
...@@ -19,8 +19,12 @@ ...@@ -19,8 +19,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "config.h"
#include <float.h> #include <float.h>
#include <stdint.h> #include <stdint.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/mathematics.h" #include "libavutil/mathematics.h"
...@@ -31,6 +35,7 @@ ...@@ -31,6 +35,7 @@
#include "avformat.h" #include "avformat.h"
#include "internal.h" #include "internal.h"
#include "os_support.h"
typedef struct HLSSegment { typedef struct HLSSegment {
char filename[1024]; char filename[1024];
...@@ -44,6 +49,7 @@ typedef struct HLSSegment { ...@@ -44,6 +49,7 @@ typedef struct HLSSegment {
typedef enum HLSFlags { typedef enum HLSFlags {
// Generate a single media file and use byte ranges in the playlist. // Generate a single media file and use byte ranges in the playlist.
HLS_SINGLE_FILE = (1 << 0), HLS_SINGLE_FILE = (1 << 0),
HLS_DELETE_SEGMENTS = (1 << 1),
} HLSFlags; } HLSFlags;
typedef struct HLSContext { typedef struct HLSContext {
...@@ -73,6 +79,7 @@ typedef struct HLSContext { ...@@ -73,6 +79,7 @@ typedef struct HLSContext {
HLSSegment *segments; HLSSegment *segments;
HLSSegment *last_segment; HLSSegment *last_segment;
HLSSegment *old_segments;
char *basename; char *basename;
char *baseurl; char *baseurl;
...@@ -82,6 +89,71 @@ typedef struct HLSContext { ...@@ -82,6 +89,71 @@ typedef struct HLSContext {
AVIOContext *pb; AVIOContext *pb;
} HLSContext; } 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) static int hls_mux_init(AVFormatContext *s)
{ {
HLSContext *hls = s->priv_data; HLSContext *hls = s->priv_data;
...@@ -116,6 +188,7 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, ...@@ -116,6 +188,7 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
int64_t size) int64_t size)
{ {
HLSSegment *en = av_malloc(sizeof(*en)); HLSSegment *en = av_malloc(sizeof(*en));
int ret;
if (!en) if (!en)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
...@@ -137,7 +210,14 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, ...@@ -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) { if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
en = hls->segments; en = hls->segments;
hls->segments = en->next; 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 } else
hls->nb_entries++; hls->nb_entries++;
...@@ -146,9 +226,9 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, ...@@ -146,9 +226,9 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
return 0; 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) { while(p) {
en = p; en = p;
...@@ -402,7 +482,8 @@ static int hls_write_trailer(struct AVFormatContext *s) ...@@ -402,7 +482,8 @@ static int hls_write_trailer(struct AVFormatContext *s)
hls->avf = NULL; hls->avf = NULL;
hls_window(s, 1); hls_window(s, 1);
hls_free_segments(hls); hls_free_segments(hls->segments);
hls_free_segments(hls->old_segments);
avio_close(hls->pb); avio_close(hls->pb);
return 0; return 0;
} }
...@@ -420,6 +501,7 @@ static const AVOption options[] = { ...@@ -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_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"}, {"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"}, {"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 }, { NULL },
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment