Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Apple HTTP Live Streaming segmenter | |
3 | * Copyright (c) 2012, Luca Barbato | |
4 | * | |
5 | * This file is part of FFmpeg. | |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * FFmpeg is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with FFmpeg; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | #include <float.h> | |
23 | #include <stdint.h> | |
24 | ||
25 | #include "libavutil/avassert.h" | |
26 | #include "libavutil/mathematics.h" | |
27 | #include "libavutil/parseutils.h" | |
28 | #include "libavutil/avstring.h" | |
29 | #include "libavutil/opt.h" | |
30 | #include "libavutil/log.h" | |
31 | ||
32 | #include "avformat.h" | |
33 | #include "internal.h" | |
34 | ||
35 | typedef struct HLSSegment { | |
36 | char filename[1024]; | |
37 | double duration; /* in seconds */ | |
f6fa7814 DM |
38 | int64_t pos; |
39 | int64_t size; | |
2ba45a60 DM |
40 | |
41 | struct HLSSegment *next; | |
42 | } HLSSegment; | |
43 | ||
f6fa7814 DM |
44 | typedef enum HLSFlags { |
45 | // Generate a single media file and use byte ranges in the playlist. | |
46 | HLS_SINGLE_FILE = (1 << 0), | |
47 | } HLSFlags; | |
48 | ||
2ba45a60 DM |
49 | typedef struct HLSContext { |
50 | const AVClass *class; // Class for private options. | |
51 | unsigned number; | |
52 | int64_t sequence; | |
53 | int64_t start_sequence; | |
54 | AVOutputFormat *oformat; | |
55 | ||
56 | AVFormatContext *avf; | |
57 | ||
58 | float time; // Set by a private option. | |
59 | int max_nb_segments; // Set by a private option. | |
60 | int wrap; // Set by a private option. | |
f6fa7814 | 61 | uint32_t flags; // enum HLSFlags |
2ba45a60 | 62 | |
f6fa7814 | 63 | int allowcache; |
2ba45a60 DM |
64 | int64_t recording_time; |
65 | int has_video; | |
66 | int64_t start_pts; | |
67 | int64_t end_pts; | |
68 | double duration; // last segment duration computed so far, in seconds | |
f6fa7814 DM |
69 | int64_t start_pos; // last segment starting position |
70 | int64_t size; // last segment size | |
2ba45a60 DM |
71 | int nb_entries; |
72 | ||
73 | HLSSegment *segments; | |
74 | HLSSegment *last_segment; | |
75 | ||
76 | char *basename; | |
77 | char *baseurl; | |
f6fa7814 DM |
78 | char *format_options_str; |
79 | AVDictionary *format_options; | |
2ba45a60 DM |
80 | |
81 | AVIOContext *pb; | |
82 | } HLSContext; | |
83 | ||
84 | static int hls_mux_init(AVFormatContext *s) | |
85 | { | |
86 | HLSContext *hls = s->priv_data; | |
87 | AVFormatContext *oc; | |
f6fa7814 | 88 | int i, ret; |
2ba45a60 | 89 | |
f6fa7814 DM |
90 | ret = avformat_alloc_output_context2(&hls->avf, hls->oformat, NULL, NULL); |
91 | if (ret < 0) | |
92 | return ret; | |
93 | oc = hls->avf; | |
2ba45a60 DM |
94 | |
95 | oc->oformat = hls->oformat; | |
96 | oc->interrupt_callback = s->interrupt_callback; | |
f6fa7814 | 97 | oc->max_delay = s->max_delay; |
2ba45a60 DM |
98 | av_dict_copy(&oc->metadata, s->metadata, 0); |
99 | ||
100 | for (i = 0; i < s->nb_streams; i++) { | |
101 | AVStream *st; | |
102 | if (!(st = avformat_new_stream(oc, NULL))) | |
103 | return AVERROR(ENOMEM); | |
104 | avcodec_copy_context(st->codec, s->streams[i]->codec); | |
105 | st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; | |
f6fa7814 | 106 | st->time_base = s->streams[i]->time_base; |
2ba45a60 | 107 | } |
f6fa7814 | 108 | hls->start_pos = 0; |
2ba45a60 DM |
109 | |
110 | return 0; | |
111 | } | |
112 | ||
113 | /* Create a new segment and append it to the segment list */ | |
f6fa7814 DM |
114 | static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, |
115 | int64_t size) | |
2ba45a60 DM |
116 | { |
117 | HLSSegment *en = av_malloc(sizeof(*en)); | |
118 | ||
119 | if (!en) | |
120 | return AVERROR(ENOMEM); | |
121 | ||
122 | av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename)); | |
123 | ||
124 | en->duration = duration; | |
f6fa7814 DM |
125 | en->pos = pos; |
126 | en->size = size; | |
2ba45a60 DM |
127 | en->next = NULL; |
128 | ||
129 | if (!hls->segments) | |
130 | hls->segments = en; | |
131 | else | |
132 | hls->last_segment->next = en; | |
133 | ||
134 | hls->last_segment = en; | |
135 | ||
136 | if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) { | |
137 | en = hls->segments; | |
138 | hls->segments = en->next; | |
139 | av_free(en); | |
140 | } else | |
141 | hls->nb_entries++; | |
142 | ||
143 | hls->sequence++; | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static void hls_free_segments(HLSContext *hls) | |
149 | { | |
150 | HLSSegment *p = hls->segments, *en; | |
151 | ||
152 | while(p) { | |
153 | en = p; | |
154 | p = p->next; | |
155 | av_free(en); | |
156 | } | |
157 | } | |
158 | ||
159 | static int hls_window(AVFormatContext *s, int last) | |
160 | { | |
161 | HLSContext *hls = s->priv_data; | |
162 | HLSSegment *en; | |
163 | int target_duration = 0; | |
164 | int ret = 0; | |
165 | int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries); | |
f6fa7814 | 166 | int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3; |
2ba45a60 DM |
167 | |
168 | if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE, | |
169 | &s->interrupt_callback, NULL)) < 0) | |
170 | goto fail; | |
171 | ||
172 | for (en = hls->segments; en; en = en->next) { | |
173 | if (target_duration < en->duration) | |
174 | target_duration = ceil(en->duration); | |
175 | } | |
176 | ||
177 | avio_printf(hls->pb, "#EXTM3U\n"); | |
f6fa7814 DM |
178 | avio_printf(hls->pb, "#EXT-X-VERSION:%d\n", version); |
179 | if (hls->allowcache == 0 || hls->allowcache == 1) { | |
180 | avio_printf(hls->pb, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES"); | |
181 | } | |
2ba45a60 DM |
182 | avio_printf(hls->pb, "#EXT-X-TARGETDURATION:%d\n", target_duration); |
183 | avio_printf(hls->pb, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); | |
184 | ||
185 | av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", | |
186 | sequence); | |
187 | ||
188 | for (en = hls->segments; en; en = en->next) { | |
189 | avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration); | |
f6fa7814 DM |
190 | if (hls->flags & HLS_SINGLE_FILE) |
191 | avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n", | |
192 | en->size, en->pos); | |
2ba45a60 DM |
193 | if (hls->baseurl) |
194 | avio_printf(hls->pb, "%s", hls->baseurl); | |
195 | avio_printf(hls->pb, "%s\n", en->filename); | |
196 | } | |
197 | ||
198 | if (last) | |
199 | avio_printf(hls->pb, "#EXT-X-ENDLIST\n"); | |
200 | ||
201 | fail: | |
202 | avio_closep(&hls->pb); | |
203 | return ret; | |
204 | } | |
205 | ||
206 | static int hls_start(AVFormatContext *s) | |
207 | { | |
208 | HLSContext *c = s->priv_data; | |
209 | AVFormatContext *oc = c->avf; | |
210 | int err = 0; | |
211 | ||
f6fa7814 DM |
212 | if (c->flags & HLS_SINGLE_FILE) |
213 | av_strlcpy(oc->filename, c->basename, | |
214 | sizeof(oc->filename)); | |
215 | else | |
216 | if (av_get_frame_filename(oc->filename, sizeof(oc->filename), | |
217 | c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) { | |
218 | av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename); | |
219 | return AVERROR(EINVAL); | |
220 | } | |
2ba45a60 DM |
221 | c->number++; |
222 | ||
223 | if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, | |
224 | &s->interrupt_callback, NULL)) < 0) | |
225 | return err; | |
226 | ||
227 | if (oc->oformat->priv_class && oc->priv_data) | |
228 | av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | static int hls_write_header(AVFormatContext *s) | |
234 | { | |
235 | HLSContext *hls = s->priv_data; | |
236 | int ret, i; | |
237 | char *p; | |
238 | const char *pattern = "%d.ts"; | |
f6fa7814 | 239 | AVDictionary *options = NULL; |
2ba45a60 DM |
240 | int basename_size = strlen(s->filename) + strlen(pattern) + 1; |
241 | ||
242 | hls->sequence = hls->start_sequence; | |
243 | hls->recording_time = hls->time * AV_TIME_BASE; | |
244 | hls->start_pts = AV_NOPTS_VALUE; | |
245 | ||
f6fa7814 DM |
246 | if (hls->flags & HLS_SINGLE_FILE) |
247 | pattern = ".ts"; | |
248 | ||
249 | if (hls->format_options_str) { | |
250 | ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); | |
251 | if (ret < 0) { | |
252 | av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str); | |
253 | goto fail; | |
254 | } | |
255 | } | |
256 | ||
2ba45a60 DM |
257 | for (i = 0; i < s->nb_streams; i++) |
258 | hls->has_video += | |
259 | s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO; | |
260 | ||
261 | if (hls->has_video > 1) | |
262 | av_log(s, AV_LOG_WARNING, | |
263 | "More than a single video stream present, " | |
264 | "expect issues decoding it.\n"); | |
265 | ||
266 | hls->oformat = av_guess_format("mpegts", NULL, NULL); | |
267 | ||
268 | if (!hls->oformat) { | |
269 | ret = AVERROR_MUXER_NOT_FOUND; | |
270 | goto fail; | |
271 | } | |
272 | ||
273 | hls->basename = av_malloc(basename_size); | |
274 | ||
275 | if (!hls->basename) { | |
276 | ret = AVERROR(ENOMEM); | |
277 | goto fail; | |
278 | } | |
279 | ||
280 | strcpy(hls->basename, s->filename); | |
281 | ||
282 | p = strrchr(hls->basename, '.'); | |
283 | ||
284 | if (p) | |
285 | *p = '\0'; | |
286 | ||
287 | av_strlcat(hls->basename, pattern, basename_size); | |
288 | ||
289 | if ((ret = hls_mux_init(s)) < 0) | |
290 | goto fail; | |
291 | ||
292 | if ((ret = hls_start(s)) < 0) | |
293 | goto fail; | |
294 | ||
f6fa7814 DM |
295 | av_dict_copy(&options, hls->format_options, 0); |
296 | ret = avformat_write_header(hls->avf, &options); | |
297 | if (av_dict_count(options)) { | |
298 | av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str); | |
299 | ret = AVERROR(EINVAL); | |
2ba45a60 | 300 | goto fail; |
f6fa7814 | 301 | } |
2ba45a60 DM |
302 | av_assert0(s->nb_streams == hls->avf->nb_streams); |
303 | for (i = 0; i < s->nb_streams; i++) { | |
304 | AVStream *inner_st = hls->avf->streams[i]; | |
f6fa7814 DM |
305 | AVStream *outer_st = s->streams[i]; |
306 | avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); | |
2ba45a60 | 307 | } |
2ba45a60 | 308 | fail: |
f6fa7814 DM |
309 | |
310 | av_dict_free(&options); | |
2ba45a60 DM |
311 | if (ret) { |
312 | av_free(hls->basename); | |
313 | if (hls->avf) | |
314 | avformat_free_context(hls->avf); | |
315 | } | |
316 | return ret; | |
317 | } | |
318 | ||
319 | static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) | |
320 | { | |
321 | HLSContext *hls = s->priv_data; | |
322 | AVFormatContext *oc = hls->avf; | |
323 | AVStream *st = s->streams[pkt->stream_index]; | |
324 | int64_t end_pts = hls->recording_time * hls->number; | |
325 | int is_ref_pkt = 1; | |
326 | int ret, can_split = 1; | |
327 | ||
328 | if (hls->start_pts == AV_NOPTS_VALUE) { | |
329 | hls->start_pts = pkt->pts; | |
330 | hls->end_pts = pkt->pts; | |
331 | } | |
332 | ||
333 | if (hls->has_video) { | |
334 | can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO && | |
335 | pkt->flags & AV_PKT_FLAG_KEY; | |
336 | is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO; | |
337 | } | |
338 | if (pkt->pts == AV_NOPTS_VALUE) | |
339 | is_ref_pkt = can_split = 0; | |
340 | ||
341 | if (is_ref_pkt) | |
342 | hls->duration = (double)(pkt->pts - hls->end_pts) | |
343 | * st->time_base.num / st->time_base.den; | |
344 | ||
345 | if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, | |
346 | end_pts, AV_TIME_BASE_Q) >= 0) { | |
f6fa7814 DM |
347 | int64_t new_start_pos; |
348 | av_write_frame(oc, NULL); /* Flush any buffered data */ | |
349 | ||
350 | new_start_pos = avio_tell(hls->avf->pb); | |
351 | hls->size = new_start_pos - hls->start_pos; | |
352 | ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size); | |
353 | hls->start_pos = new_start_pos; | |
2ba45a60 DM |
354 | if (ret) |
355 | return ret; | |
356 | ||
357 | hls->end_pts = pkt->pts; | |
358 | hls->duration = 0; | |
359 | ||
f6fa7814 DM |
360 | if (hls->flags & HLS_SINGLE_FILE) { |
361 | if (hls->avf->oformat->priv_class && hls->avf->priv_data) | |
362 | av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0); | |
363 | hls->number++; | |
364 | } else { | |
365 | avio_close(oc->pb); | |
2ba45a60 | 366 | |
f6fa7814 DM |
367 | ret = hls_start(s); |
368 | } | |
2ba45a60 DM |
369 | |
370 | if (ret) | |
371 | return ret; | |
372 | ||
373 | oc = hls->avf; | |
374 | ||
375 | if ((ret = hls_window(s, 0)) < 0) | |
376 | return ret; | |
377 | } | |
378 | ||
379 | ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0); | |
380 | ||
381 | return ret; | |
382 | } | |
383 | ||
384 | static int hls_write_trailer(struct AVFormatContext *s) | |
385 | { | |
386 | HLSContext *hls = s->priv_data; | |
387 | AVFormatContext *oc = hls->avf; | |
388 | ||
389 | av_write_trailer(oc); | |
f6fa7814 | 390 | hls->size = avio_tell(hls->avf->pb) - hls->start_pos; |
2ba45a60 | 391 | avio_closep(&oc->pb); |
2ba45a60 | 392 | av_free(hls->basename); |
f6fa7814 DM |
393 | hls_append_segment(hls, hls->duration, hls->start_pos, hls->size); |
394 | avformat_free_context(oc); | |
395 | hls->avf = NULL; | |
2ba45a60 DM |
396 | hls_window(s, 1); |
397 | ||
398 | hls_free_segments(hls); | |
399 | avio_close(hls->pb); | |
400 | return 0; | |
401 | } | |
402 | ||
403 | #define OFFSET(x) offsetof(HLSContext, x) | |
404 | #define E AV_OPT_FLAG_ENCODING_PARAM | |
405 | static const AVOption options[] = { | |
406 | {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, | |
407 | {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, | |
408 | {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, | |
f6fa7814 | 409 | {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, |
2ba45a60 | 410 | {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, |
f6fa7814 | 411 | {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E}, |
2ba45a60 | 412 | {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, |
f6fa7814 DM |
413 | {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"}, |
414 | {"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"}, | |
415 | ||
2ba45a60 DM |
416 | { NULL }, |
417 | }; | |
418 | ||
419 | static const AVClass hls_class = { | |
420 | .class_name = "hls muxer", | |
421 | .item_name = av_default_item_name, | |
422 | .option = options, | |
423 | .version = LIBAVUTIL_VERSION_INT, | |
424 | }; | |
425 | ||
426 | ||
427 | AVOutputFormat ff_hls_muxer = { | |
428 | .name = "hls", | |
429 | .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), | |
430 | .extensions = "m3u8", | |
431 | .priv_data_size = sizeof(HLSContext), | |
432 | .audio_codec = AV_CODEC_ID_AAC, | |
433 | .video_codec = AV_CODEC_ID_H264, | |
434 | .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, | |
435 | .write_header = hls_write_header, | |
436 | .write_packet = hls_write_packet, | |
437 | .write_trailer = hls_write_trailer, | |
438 | .priv_class = &hls_class, | |
439 | }; |