2 * Apple HTTP Live Streaming segmenter
3 * Copyright (c) 2012, Luca Barbato
5 * This file is part of FFmpeg.
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.
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.
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
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"
35 typedef struct HLSSegment
{
37 double duration
; /* in seconds */
41 struct HLSSegment
*next
;
44 typedef enum HLSFlags
{
45 // Generate a single media file and use byte ranges in the playlist.
46 HLS_SINGLE_FILE
= (1 << 0),
49 typedef struct HLSContext
{
50 const AVClass
*class; // Class for private options.
53 int64_t start_sequence
;
54 AVOutputFormat
*oformat
;
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.
61 uint32_t flags
; // enum HLSFlags
64 int64_t recording_time
;
68 double duration
; // last segment duration computed so far, in seconds
69 int64_t start_pos
; // last segment starting position
70 int64_t size
; // last segment size
74 HLSSegment
*last_segment
;
78 char *format_options_str
;
79 AVDictionary
*format_options
;
84 static int hls_mux_init(AVFormatContext
*s
)
86 HLSContext
*hls
= s
->priv_data
;
90 ret
= avformat_alloc_output_context2(&hls
->avf
, hls
->oformat
, NULL
, NULL
);
95 oc
->oformat
= hls
->oformat
;
96 oc
->interrupt_callback
= s
->interrupt_callback
;
97 oc
->max_delay
= s
->max_delay
;
98 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
100 for (i
= 0; i
< s
->nb_streams
; i
++) {
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
;
106 st
->time_base
= s
->streams
[i
]->time_base
;
113 /* Create a new segment and append it to the segment list */
114 static int hls_append_segment(HLSContext
*hls
, double duration
, int64_t pos
,
117 HLSSegment
*en
= av_malloc(sizeof(*en
));
120 return AVERROR(ENOMEM
);
122 av_strlcpy(en
->filename
, av_basename(hls
->avf
->filename
), sizeof(en
->filename
));
124 en
->duration
= duration
;
132 hls
->last_segment
->next
= en
;
134 hls
->last_segment
= en
;
136 if (hls
->max_nb_segments
&& hls
->nb_entries
>= hls
->max_nb_segments
) {
138 hls
->segments
= en
->next
;
148 static void hls_free_segments(HLSContext
*hls
)
150 HLSSegment
*p
= hls
->segments
, *en
;
159 static int hls_window(AVFormatContext
*s
, int last
)
161 HLSContext
*hls
= s
->priv_data
;
163 int target_duration
= 0;
165 int64_t sequence
= FFMAX(hls
->start_sequence
, hls
->sequence
- hls
->nb_entries
);
166 int version
= hls
->flags
& HLS_SINGLE_FILE
? 4 : 3;
168 if ((ret
= avio_open2(&hls
->pb
, s
->filename
, AVIO_FLAG_WRITE
,
169 &s
->interrupt_callback
, NULL
)) < 0)
172 for (en
= hls
->segments
; en
; en
= en
->next
) {
173 if (target_duration
< en
->duration
)
174 target_duration
= ceil(en
->duration
);
177 avio_printf(hls
->pb
, "#EXTM3U\n");
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");
182 avio_printf(hls
->pb
, "#EXT-X-TARGETDURATION:%d\n", target_duration
);
183 avio_printf(hls
->pb
, "#EXT-X-MEDIA-SEQUENCE:%"PRId64
"\n", sequence
);
185 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%"PRId64
"\n",
188 for (en
= hls
->segments
; en
; en
= en
->next
) {
189 avio_printf(hls
->pb
, "#EXTINF:%f,\n", en
->duration
);
190 if (hls
->flags
& HLS_SINGLE_FILE
)
191 avio_printf(hls
->pb
, "#EXT-X-BYTERANGE:%"PRIi64
"@%"PRIi64
"\n",
194 avio_printf(hls
->pb
, "%s", hls
->baseurl
);
195 avio_printf(hls
->pb
, "%s\n", en
->filename
);
199 avio_printf(hls
->pb
, "#EXT-X-ENDLIST\n");
202 avio_closep(&hls
->pb
);
206 static int hls_start(AVFormatContext
*s
)
208 HLSContext
*c
= s
->priv_data
;
209 AVFormatContext
*oc
= c
->avf
;
212 if (c
->flags
& HLS_SINGLE_FILE
)
213 av_strlcpy(oc
->filename
, c
->basename
,
214 sizeof(oc
->filename
));
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
);
223 if ((err
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
224 &s
->interrupt_callback
, NULL
)) < 0)
227 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
228 av_opt_set(oc
->priv_data
, "mpegts_flags", "resend_headers", 0);
233 static int hls_write_header(AVFormatContext
*s
)
235 HLSContext
*hls
= s
->priv_data
;
238 const char *pattern
= "%d.ts";
239 AVDictionary
*options
= NULL
;
240 int basename_size
= strlen(s
->filename
) + strlen(pattern
) + 1;
242 hls
->sequence
= hls
->start_sequence
;
243 hls
->recording_time
= hls
->time
* AV_TIME_BASE
;
244 hls
->start_pts
= AV_NOPTS_VALUE
;
246 if (hls
->flags
& HLS_SINGLE_FILE
)
249 if (hls
->format_options_str
) {
250 ret
= av_dict_parse_string(&hls
->format_options
, hls
->format_options_str
, "=", ":", 0);
252 av_log(s
, AV_LOG_ERROR
, "Could not parse format options list '%s'\n", hls
->format_options_str
);
257 for (i
= 0; i
< s
->nb_streams
; i
++)
259 s
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
;
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");
266 hls
->oformat
= av_guess_format("mpegts", NULL
, NULL
);
269 ret
= AVERROR_MUXER_NOT_FOUND
;
273 hls
->basename
= av_malloc(basename_size
);
275 if (!hls
->basename
) {
276 ret
= AVERROR(ENOMEM
);
280 strcpy(hls
->basename
, s
->filename
);
282 p
= strrchr(hls
->basename
, '.');
287 av_strlcat(hls
->basename
, pattern
, basename_size
);
289 if ((ret
= hls_mux_init(s
)) < 0)
292 if ((ret
= hls_start(s
)) < 0)
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
);
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
];
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
);
310 av_dict_free(&options
);
312 av_free(hls
->basename
);
314 avformat_free_context(hls
->avf
);
319 static int hls_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
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
;
326 int ret
, can_split
= 1;
328 if (hls
->start_pts
== AV_NOPTS_VALUE
) {
329 hls
->start_pts
= pkt
->pts
;
330 hls
->end_pts
= pkt
->pts
;
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
;
338 if (pkt
->pts
== AV_NOPTS_VALUE
)
339 is_ref_pkt
= can_split
= 0;
342 hls
->duration
= (double)(pkt
->pts
- hls
->end_pts
)
343 * st
->time_base
.num
/ st
->time_base
.den
;
345 if (can_split
&& av_compare_ts(pkt
->pts
- hls
->start_pts
, st
->time_base
,
346 end_pts
, AV_TIME_BASE_Q
) >= 0) {
347 int64_t new_start_pos
;
348 av_write_frame(oc
, NULL
); /* Flush any buffered data */
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
;
357 hls
->end_pts
= pkt
->pts
;
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);
375 if ((ret
= hls_window(s
, 0)) < 0)
379 ret
= ff_write_chained(oc
, pkt
->stream_index
, pkt
, s
, 0);
384 static int hls_write_trailer(struct AVFormatContext
*s
)
386 HLSContext
*hls
= s
->priv_data
;
387 AVFormatContext
*oc
= hls
->avf
;
389 av_write_trailer(oc
);
390 hls
->size
= avio_tell(hls
->avf
->pb
) - hls
->start_pos
;
391 avio_closep(&oc
->pb
);
392 av_free(hls
->basename
);
393 hls_append_segment(hls
, hls
->duration
, hls
->start_pos
, hls
->size
);
394 avformat_free_context(oc
);
398 hls_free_segments(hls
);
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
},
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
},
410 {"hls_wrap", "set number after which the index wraps", OFFSET(wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
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
},
412 {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
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"},
419 static const AVClass hls_class
= {
420 .class_name
= "hls muxer",
421 .item_name
= av_default_item_name
,
423 .version
= LIBAVUTIL_VERSION_INT
,
427 AVOutputFormat ff_hls_muxer
= {
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
,