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 */
39 struct HLSSegment
*next
;
42 typedef struct HLSContext
{
43 const AVClass
*class; // Class for private options.
46 int64_t start_sequence
;
47 AVOutputFormat
*oformat
;
51 float time
; // Set by a private option.
52 int max_nb_segments
; // Set by a private option.
53 int wrap
; // Set by a private option.
55 int64_t recording_time
;
59 double duration
; // last segment duration computed so far, in seconds
63 HLSSegment
*last_segment
;
71 static int hls_mux_init(AVFormatContext
*s
)
73 HLSContext
*hls
= s
->priv_data
;
77 hls
->avf
= oc
= avformat_alloc_context();
79 return AVERROR(ENOMEM
);
81 oc
->oformat
= hls
->oformat
;
82 oc
->interrupt_callback
= s
->interrupt_callback
;
83 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
85 for (i
= 0; i
< s
->nb_streams
; i
++) {
87 if (!(st
= avformat_new_stream(oc
, NULL
)))
88 return AVERROR(ENOMEM
);
89 avcodec_copy_context(st
->codec
, s
->streams
[i
]->codec
);
90 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
96 /* Create a new segment and append it to the segment list */
97 static int hls_append_segment(HLSContext
*hls
, double duration
)
99 HLSSegment
*en
= av_malloc(sizeof(*en
));
102 return AVERROR(ENOMEM
);
104 av_strlcpy(en
->filename
, av_basename(hls
->avf
->filename
), sizeof(en
->filename
));
106 en
->duration
= duration
;
112 hls
->last_segment
->next
= en
;
114 hls
->last_segment
= en
;
116 if (hls
->max_nb_segments
&& hls
->nb_entries
>= hls
->max_nb_segments
) {
118 hls
->segments
= en
->next
;
128 static void hls_free_segments(HLSContext
*hls
)
130 HLSSegment
*p
= hls
->segments
, *en
;
139 static int hls_window(AVFormatContext
*s
, int last
)
141 HLSContext
*hls
= s
->priv_data
;
143 int target_duration
= 0;
145 int64_t sequence
= FFMAX(hls
->start_sequence
, hls
->sequence
- hls
->nb_entries
);
147 if ((ret
= avio_open2(&hls
->pb
, s
->filename
, AVIO_FLAG_WRITE
,
148 &s
->interrupt_callback
, NULL
)) < 0)
151 for (en
= hls
->segments
; en
; en
= en
->next
) {
152 if (target_duration
< en
->duration
)
153 target_duration
= ceil(en
->duration
);
156 avio_printf(hls
->pb
, "#EXTM3U\n");
157 avio_printf(hls
->pb
, "#EXT-X-VERSION:3\n");
158 avio_printf(hls
->pb
, "#EXT-X-TARGETDURATION:%d\n", target_duration
);
159 avio_printf(hls
->pb
, "#EXT-X-MEDIA-SEQUENCE:%"PRId64
"\n", sequence
);
161 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%"PRId64
"\n",
164 for (en
= hls
->segments
; en
; en
= en
->next
) {
165 avio_printf(hls
->pb
, "#EXTINF:%f,\n", en
->duration
);
167 avio_printf(hls
->pb
, "%s", hls
->baseurl
);
168 avio_printf(hls
->pb
, "%s\n", en
->filename
);
172 avio_printf(hls
->pb
, "#EXT-X-ENDLIST\n");
175 avio_closep(&hls
->pb
);
179 static int hls_start(AVFormatContext
*s
)
181 HLSContext
*c
= s
->priv_data
;
182 AVFormatContext
*oc
= c
->avf
;
185 if (av_get_frame_filename(oc
->filename
, sizeof(oc
->filename
),
186 c
->basename
, c
->wrap
? c
->sequence
% c
->wrap
: c
->sequence
) < 0) {
187 av_log(oc
, AV_LOG_ERROR
, "Invalid segment filename template '%s'\n", c
->basename
);
188 return AVERROR(EINVAL
);
192 if ((err
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
193 &s
->interrupt_callback
, NULL
)) < 0)
196 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
197 av_opt_set(oc
->priv_data
, "mpegts_flags", "resend_headers", 0);
202 static int hls_write_header(AVFormatContext
*s
)
204 HLSContext
*hls
= s
->priv_data
;
207 const char *pattern
= "%d.ts";
208 int basename_size
= strlen(s
->filename
) + strlen(pattern
) + 1;
210 hls
->sequence
= hls
->start_sequence
;
211 hls
->recording_time
= hls
->time
* AV_TIME_BASE
;
212 hls
->start_pts
= AV_NOPTS_VALUE
;
214 for (i
= 0; i
< s
->nb_streams
; i
++)
216 s
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
;
218 if (hls
->has_video
> 1)
219 av_log(s
, AV_LOG_WARNING
,
220 "More than a single video stream present, "
221 "expect issues decoding it.\n");
223 hls
->oformat
= av_guess_format("mpegts", NULL
, NULL
);
226 ret
= AVERROR_MUXER_NOT_FOUND
;
230 hls
->basename
= av_malloc(basename_size
);
232 if (!hls
->basename
) {
233 ret
= AVERROR(ENOMEM
);
237 strcpy(hls
->basename
, s
->filename
);
239 p
= strrchr(hls
->basename
, '.');
244 av_strlcat(hls
->basename
, pattern
, basename_size
);
246 if ((ret
= hls_mux_init(s
)) < 0)
249 if ((ret
= hls_start(s
)) < 0)
252 if ((ret
= avformat_write_header(hls
->avf
, NULL
)) < 0)
255 av_assert0(s
->nb_streams
== hls
->avf
->nb_streams
);
256 for (i
= 0; i
< s
->nb_streams
; i
++) {
257 AVStream
*inner_st
= hls
->avf
->streams
[i
];
258 AVStream
*outter_st
= s
->streams
[i
];
259 avpriv_set_pts_info(outter_st
, inner_st
->pts_wrap_bits
, inner_st
->time_base
.num
, inner_st
->time_base
.den
);
264 av_free(hls
->basename
);
266 avformat_free_context(hls
->avf
);
271 static int hls_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
273 HLSContext
*hls
= s
->priv_data
;
274 AVFormatContext
*oc
= hls
->avf
;
275 AVStream
*st
= s
->streams
[pkt
->stream_index
];
276 int64_t end_pts
= hls
->recording_time
* hls
->number
;
278 int ret
, can_split
= 1;
280 if (hls
->start_pts
== AV_NOPTS_VALUE
) {
281 hls
->start_pts
= pkt
->pts
;
282 hls
->end_pts
= pkt
->pts
;
285 if (hls
->has_video
) {
286 can_split
= st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
&&
287 pkt
->flags
& AV_PKT_FLAG_KEY
;
288 is_ref_pkt
= st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
;
290 if (pkt
->pts
== AV_NOPTS_VALUE
)
291 is_ref_pkt
= can_split
= 0;
294 hls
->duration
= (double)(pkt
->pts
- hls
->end_pts
)
295 * st
->time_base
.num
/ st
->time_base
.den
;
297 if (can_split
&& av_compare_ts(pkt
->pts
- hls
->start_pts
, st
->time_base
,
298 end_pts
, AV_TIME_BASE_Q
) >= 0) {
299 ret
= hls_append_segment(hls
, hls
->duration
);
303 hls
->end_pts
= pkt
->pts
;
306 av_write_frame(oc
, NULL
); /* Flush any buffered data */
316 if ((ret
= hls_window(s
, 0)) < 0)
320 ret
= ff_write_chained(oc
, pkt
->stream_index
, pkt
, s
, 0);
325 static int hls_write_trailer(struct AVFormatContext
*s
)
327 HLSContext
*hls
= s
->priv_data
;
328 AVFormatContext
*oc
= hls
->avf
;
330 av_write_trailer(oc
);
331 avio_closep(&oc
->pb
);
332 avformat_free_context(oc
);
333 av_free(hls
->basename
);
334 hls_append_segment(hls
, hls
->duration
);
337 hls_free_segments(hls
);
342 #define OFFSET(x) offsetof(HLSContext, x)
343 #define E AV_OPT_FLAG_ENCODING_PARAM
344 static const AVOption options
[] = {
345 {"start_number", "set first number in the sequence", OFFSET(start_sequence
),AV_OPT_TYPE_INT64
, {.i64
= 0}, 0, INT64_MAX
, E
},
346 {"hls_time", "set segment length in seconds", OFFSET(time
), AV_OPT_TYPE_FLOAT
, {.dbl
= 2}, 0, FLT_MAX
, E
},
347 {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments
), AV_OPT_TYPE_INT
, {.i64
= 5}, 0, INT_MAX
, E
},
348 {"hls_wrap", "set number after which the index wraps", OFFSET(wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
349 {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
353 static const AVClass hls_class
= {
354 .class_name
= "hls muxer",
355 .item_name
= av_default_item_name
,
357 .version
= LIBAVUTIL_VERSION_INT
,
361 AVOutputFormat ff_hls_muxer
= {
363 .long_name
= NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
364 .extensions
= "m3u8",
365 .priv_data_size
= sizeof(HLSContext
),
366 .audio_codec
= AV_CODEC_ID_AAC
,
367 .video_codec
= AV_CODEC_ID_H264
,
368 .flags
= AVFMT_NOFILE
| AVFMT_ALLOW_FLUSH
,
369 .write_header
= hls_write_header
,
370 .write_packet
= hls_write_packet
,
371 .write_trailer
= hls_write_trailer
,
372 .priv_class
= &hls_class
,