#include "avformat.h"
#include "internal.h"
-typedef struct ListEntry {
- char name[1024];
- double duration;
- struct ListEntry *next;
-} ListEntry;
+typedef struct HLSSegment {
+ char filename[1024];
+ double duration; /* in seconds */
+
+ struct HLSSegment *next;
+} HLSSegment;
typedef struct HLSContext {
const AVClass *class; // Class for private options.
int64_t sequence;
int64_t start_sequence;
AVOutputFormat *oformat;
+
AVFormatContext *avf;
+
float time; // Set by a private option.
- int size; // Set by a private option.
+ int max_nb_segments; // Set by a private option.
int wrap; // Set by a private option.
+
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
int nb_entries;
- ListEntry *list;
- ListEntry *end_list;
+
+ HLSSegment *segments;
+ HLSSegment *last_segment;
+
char *basename;
char *baseurl;
+
AVIOContext *pb;
} HLSContext;
return 0;
}
-static int append_entry(HLSContext *hls, double duration)
+/* Create a new segment and append it to the segment list */
+static int hls_append_segment(HLSContext *hls, double duration)
{
- ListEntry *en = av_malloc(sizeof(*en));
+ HLSSegment *en = av_malloc(sizeof(*en));
if (!en)
return AVERROR(ENOMEM);
- av_strlcpy(en->name, av_basename(hls->avf->filename), sizeof(en->name));
+ av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
en->duration = duration;
en->next = NULL;
- if (!hls->list)
- hls->list = en;
+ if (!hls->segments)
+ hls->segments = en;
else
- hls->end_list->next = en;
+ hls->last_segment->next = en;
- hls->end_list = en;
+ hls->last_segment = en;
- if (hls->size && hls->nb_entries >= hls->size) {
- en = hls->list;
- hls->list = en->next;
+ if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
+ en = hls->segments;
+ hls->segments = en->next;
av_free(en);
} else
hls->nb_entries++;
return 0;
}
-static void free_entries(HLSContext *hls)
+static void hls_free_segments(HLSContext *hls)
{
- ListEntry *p = hls->list, *en;
+ HLSSegment *p = hls->segments, *en;
while(p) {
en = p;
static int hls_window(AVFormatContext *s, int last)
{
HLSContext *hls = s->priv_data;
- ListEntry *en;
+ HLSSegment *en;
int target_duration = 0;
int ret = 0;
int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
&s->interrupt_callback, NULL)) < 0)
goto fail;
- for (en = hls->list; en; en = en->next) {
+ for (en = hls->segments; en; en = en->next) {
if (target_duration < en->duration)
- target_duration = (int) floor(en->duration + 0.5);
+ target_duration = ceil(en->duration);
}
avio_printf(hls->pb, "#EXTM3U\n");
av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
sequence);
- for (en = hls->list; en; en = en->next) {
+ for (en = hls->segments; en; en = en->next) {
avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration);
if (hls->baseurl)
avio_printf(hls->pb, "%s", hls->baseurl);
- avio_printf(hls->pb, "%s\n", en->name);
+ avio_printf(hls->pb, "%s\n", en->filename);
}
if (last)
goto fail;
if ((ret = avformat_write_header(hls->avf, NULL)) < 0)
- return ret;
+ goto fail;
fail:
if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
end_pts, AV_TIME_BASE_Q) >= 0) {
- ret = append_entry(hls, hls->duration);
+ ret = hls_append_segment(hls, hls->duration);
if (ret)
return ret;
return ret;
}
- ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
+ ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0);
return ret;
}
avio_closep(&oc->pb);
avformat_free_context(oc);
av_free(hls->basename);
- append_entry(hls, hls->duration);
+ hls_append_segment(hls, hls->duration);
hls_window(s, 1);
- free_entries(hls);
+ hls_free_segments(hls);
avio_close(hls->pb);
return 0;
}
static const AVOption options[] = {
{"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E},
{"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E},
- {"hls_list_size", "set maximum number of playlist entries", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
+ {"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},
{ NULL },
.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
.extensions = "m3u8",
.priv_data_size = sizeof(HLSContext),
- .audio_codec = AV_CODEC_ID_MP2,
- .video_codec = AV_CODEC_ID_MPEG2VIDEO,
+ .audio_codec = AV_CODEC_ID_AAC,
+ .video_codec = AV_CODEC_ID_H264,
.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
.write_header = hls_write_header,
.write_packet = hls_write_packet,