summaryrefslogtreecommitdiff
path: root/libavformat/hlsenc.c
diff options
context:
space:
mode:
authorMika Raento <mika.raento@elisa.fi>2014-09-15 16:26:57 +0300
committerMichael Niedermayer <michaelni@gmx.at>2014-09-15 22:16:44 +0200
commitf685f7d7a8386365f452cc38c546327f7df20296 (patch)
tree4f1a869432a76a7df84ddc2df3e18dd409814615 /libavformat/hlsenc.c
parentad1dadac86d4dff02a8d69b25637371cbc1f31f6 (diff)
hlsenc: single_file, support HLS ver 4 byteranges
This adds a new option -hls_flags single_file that creates one .ts file for HLS and adds byteranges to the .m3u8 file, instead of creating one .ts file for each segment. This is helpful at least for storing large number of videos, as the number of files per video is drastically reduced and copying and storing those files takes less requests and inodes. This is based on work by Nicolas Martyanoff, discussed on ffmpeg-devel in July 2014. That patch seems abandoned by the author, and contained unrelated changes. This patch tries to add the minimum amount of code to support the byterange playlists. Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavformat/hlsenc.c')
-rw-r--r--libavformat/hlsenc.c64
1 files changed, 52 insertions, 12 deletions
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 11f1e5be42..c03737a384 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -34,10 +34,17 @@
typedef struct HLSSegment {
char filename[1024];
double duration; /* in seconds */
+ int64_t pos;
+ int64_t size;
struct HLSSegment *next;
} HLSSegment;
+typedef enum HLSFlags {
+ // Generate a single media file and use byte ranges in the playlist.
+ HLS_SINGLE_FILE = (1 << 0),
+} HLSFlags;
+
typedef struct HLSContext {
const AVClass *class; // Class for private options.
unsigned number;
@@ -50,12 +57,15 @@ typedef struct HLSContext {
float time; // Set by a private option.
int max_nb_segments; // Set by a private option.
int wrap; // Set by a private option.
+ uint32_t flags; // enum HLSFlags
int64_t recording_time;
int has_video;
int64_t start_pts;
int64_t end_pts;
double duration; // last segment duration computed so far, in seconds
+ int64_t start_pos; // last segment starting position
+ int64_t size; // last segment size
int nb_entries;
HLSSegment *segments;
@@ -88,12 +98,14 @@ static int hls_mux_init(AVFormatContext *s)
avcodec_copy_context(st->codec, s->streams[i]->codec);
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
}
+ hls->start_pos = 0;
return 0;
}
/* Create a new segment and append it to the segment list */
-static int hls_append_segment(HLSContext *hls, double duration)
+static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
+ int64_t size)
{
HLSSegment *en = av_malloc(sizeof(*en));
@@ -103,6 +115,8 @@ static int hls_append_segment(HLSContext *hls, double duration)
av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
en->duration = duration;
+ en->pos = pos;
+ en->size = size;
en->next = NULL;
if (!hls->segments)
@@ -142,6 +156,7 @@ static int hls_window(AVFormatContext *s, int last)
int target_duration = 0;
int ret = 0;
int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
+ int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0)
@@ -153,7 +168,7 @@ static int hls_window(AVFormatContext *s, int last)
}
avio_printf(hls->pb, "#EXTM3U\n");
- avio_printf(hls->pb, "#EXT-X-VERSION:3\n");
+ avio_printf(hls->pb, "#EXT-X-VERSION:%d\n", version);
avio_printf(hls->pb, "#EXT-X-TARGETDURATION:%d\n", target_duration);
avio_printf(hls->pb, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
@@ -162,6 +177,9 @@ static int hls_window(AVFormatContext *s, int last)
for (en = hls->segments; en; en = en->next) {
avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration);
+ if (hls->flags & HLS_SINGLE_FILE)
+ avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
+ en->size, en->pos);
if (hls->baseurl)
avio_printf(hls->pb, "%s", hls->baseurl);
avio_printf(hls->pb, "%s\n", en->filename);
@@ -181,11 +199,15 @@ static int hls_start(AVFormatContext *s)
AVFormatContext *oc = c->avf;
int err = 0;
- if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
- c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
- av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
- return AVERROR(EINVAL);
- }
+ if (c->flags & HLS_SINGLE_FILE)
+ av_strlcpy(oc->filename, c->basename,
+ sizeof(oc->filename));
+ else
+ if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
+ c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
+ av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
+ return AVERROR(EINVAL);
+ }
c->number++;
if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
@@ -210,6 +232,9 @@ static int hls_write_header(AVFormatContext *s)
hls->recording_time = hls->time * AV_TIME_BASE;
hls->start_pts = AV_NOPTS_VALUE;
+ if (hls->flags & HLS_SINGLE_FILE)
+ pattern = ".ts";
+
for (i = 0; i < s->nb_streams; i++)
hls->has_video +=
s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO;
@@ -289,17 +314,28 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
end_pts, AV_TIME_BASE_Q) >= 0) {
- ret = hls_append_segment(hls, hls->duration);
+ int64_t new_start_pos;
+ av_write_frame(oc, NULL); /* Flush any buffered data */
+
+ new_start_pos = avio_tell(hls->avf->pb);
+ hls->size = new_start_pos - hls->start_pos;
+ ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
+ hls->start_pos = new_start_pos;
if (ret)
return ret;
hls->end_pts = pkt->pts;
hls->duration = 0;
- av_write_frame(oc, NULL); /* Flush any buffered data */
- avio_close(oc->pb);
+ if (hls->flags & HLS_SINGLE_FILE) {
+ if (hls->avf->oformat->priv_class && hls->avf->priv_data)
+ av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
+ hls->number++;
+ } else {
+ avio_close(oc->pb);
- ret = hls_start(s);
+ ret = hls_start(s);
+ }
if (ret)
return ret;
@@ -321,10 +357,11 @@ static int hls_write_trailer(struct AVFormatContext *s)
AVFormatContext *oc = hls->avf;
av_write_trailer(oc);
+ hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
avio_closep(&oc->pb);
avformat_free_context(oc);
av_free(hls->basename);
- hls_append_segment(hls, hls->duration);
+ hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
hls_window(s, 1);
hls_free_segments(hls);
@@ -340,6 +377,9 @@ static const AVOption options[] = {
{"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
{"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
{"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), 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"},
+
{ NULL },
};