2 * Apple HTTP Live Streaming Protocol Handler
3 * Copyright (c) 2010 Martin Storsjo
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
24 * Apple HTTP Live Streaming Protocol Handler
25 * http://tools.ietf.org/html/draft-pantos-http-live-streaming
28 #include "libavutil/avstring.h"
29 #include "libavutil/time.h"
36 * An apple http stream consists of a playlist with media segment files,
37 * played sequentially. There may be several playlists with the same
38 * video content, in different bandwidth variants, that are played in
39 * parallel (preferably only one bandwidth variant at a time). In this case,
40 * the user supplied the url to a main playlist that only lists the variant
43 * If the main playlist doesn't point at any variants, we still create
44 * one anonymous toplevel variant for this, to maintain the structure.
49 char url
[MAX_URL_SIZE
];
54 char url
[MAX_URL_SIZE
];
57 typedef struct HLSContext
{
58 char playlisturl
[MAX_URL_SIZE
];
59 int64_t target_duration
;
63 struct segment
**segments
;
65 struct variant
**variants
;
68 int64_t last_load_time
;
71 static int read_chomp_line(AVIOContext
*s
, char *buf
, int maxlen
)
73 int len
= ff_get_line(s
, buf
, maxlen
);
74 while (len
> 0 && av_isspace(buf
[len
- 1]))
79 static void free_segment_list(HLSContext
*s
)
82 for (i
= 0; i
< s
->n_segments
; i
++)
83 av_free(s
->segments
[i
]);
84 av_freep(&s
->segments
);
88 static void free_variant_list(HLSContext
*s
)
91 for (i
= 0; i
< s
->n_variants
; i
++)
92 av_free(s
->variants
[i
]);
93 av_freep(&s
->variants
);
101 static void handle_variant_args(struct variant_info
*info
, const char *key
,
102 int key_len
, char **dest
, int *dest_len
)
104 if (!strncmp(key
, "BANDWIDTH=", key_len
)) {
105 *dest
= info
->bandwidth
;
106 *dest_len
= sizeof(info
->bandwidth
);
110 static int parse_playlist(URLContext
*h
, const char *url
)
112 HLSContext
*s
= h
->priv_data
;
114 int ret
= 0, is_segment
= 0, is_variant
= 0, bandwidth
= 0;
115 int64_t duration
= 0;
119 if ((ret
= avio_open2(&in
, url
, AVIO_FLAG_READ
,
120 &h
->interrupt_callback
, NULL
)) < 0)
123 read_chomp_line(in
, line
, sizeof(line
));
124 if (strcmp(line
, "#EXTM3U"))
125 return AVERROR_INVALIDDATA
;
127 free_segment_list(s
);
129 while (!avio_feof(in
)) {
130 read_chomp_line(in
, line
, sizeof(line
));
131 if (av_strstart(line
, "#EXT-X-STREAM-INF:", &ptr
)) {
132 struct variant_info info
= {{0}};
134 ff_parse_key_value(ptr
, (ff_parse_key_val_cb
) handle_variant_args
,
136 bandwidth
= atoi(info
.bandwidth
);
137 } else if (av_strstart(line
, "#EXT-X-TARGETDURATION:", &ptr
)) {
138 s
->target_duration
= atoi(ptr
) * AV_TIME_BASE
;
139 } else if (av_strstart(line
, "#EXT-X-MEDIA-SEQUENCE:", &ptr
)) {
140 s
->start_seq_no
= atoi(ptr
);
141 } else if (av_strstart(line
, "#EXT-X-ENDLIST", &ptr
)) {
143 } else if (av_strstart(line
, "#EXTINF:", &ptr
)) {
145 duration
= atof(ptr
) * AV_TIME_BASE
;
146 } else if (av_strstart(line
, "#", NULL
)) {
148 } else if (line
[0]) {
150 struct segment
*seg
= av_malloc(sizeof(struct segment
));
152 ret
= AVERROR(ENOMEM
);
155 seg
->duration
= duration
;
156 ff_make_absolute_url(seg
->url
, sizeof(seg
->url
), url
, line
);
157 dynarray_add(&s
->segments
, &s
->n_segments
, seg
);
159 } else if (is_variant
) {
160 struct variant
*var
= av_malloc(sizeof(struct variant
));
162 ret
= AVERROR(ENOMEM
);
165 var
->bandwidth
= bandwidth
;
166 ff_make_absolute_url(var
->url
, sizeof(var
->url
), url
, line
);
167 dynarray_add(&s
->variants
, &s
->n_variants
, var
);
172 s
->last_load_time
= av_gettime_relative();
179 static int hls_close(URLContext
*h
)
181 HLSContext
*s
= h
->priv_data
;
183 free_segment_list(s
);
184 free_variant_list(s
);
185 ffurl_close(s
->seg_hd
);
189 static int hls_open(URLContext
*h
, const char *uri
, int flags
)
191 HLSContext
*s
= h
->priv_data
;
193 const char *nested_url
;
195 if (flags
& AVIO_FLAG_WRITE
)
196 return AVERROR(ENOSYS
);
200 if (av_strstart(uri
, "hls+", &nested_url
)) {
201 av_strlcpy(s
->playlisturl
, nested_url
, sizeof(s
->playlisturl
));
202 } else if (av_strstart(uri
, "hls://", &nested_url
)) {
203 av_log(h
, AV_LOG_ERROR
,
204 "No nested protocol specified. Specify e.g. hls+http://%s\n",
206 ret
= AVERROR(EINVAL
);
209 av_log(h
, AV_LOG_ERROR
, "Unsupported url %s\n", uri
);
210 ret
= AVERROR(EINVAL
);
213 av_log(h
, AV_LOG_WARNING
,
214 "Using the hls protocol is discouraged, please try using the "
215 "hls demuxer instead. The hls demuxer should be more complete "
216 "and work as well as the protocol implementation. (If not, "
217 "please report it.) To use the demuxer, simply use %s as url.\n",
220 if ((ret
= parse_playlist(h
, s
->playlisturl
)) < 0)
223 if (s
->n_segments
== 0 && s
->n_variants
> 0) {
224 int max_bandwidth
= 0, maxvar
= -1;
225 for (i
= 0; i
< s
->n_variants
; i
++) {
226 if (s
->variants
[i
]->bandwidth
> max_bandwidth
|| i
== 0) {
227 max_bandwidth
= s
->variants
[i
]->bandwidth
;
231 av_strlcpy(s
->playlisturl
, s
->variants
[maxvar
]->url
,
232 sizeof(s
->playlisturl
));
233 if ((ret
= parse_playlist(h
, s
->playlisturl
)) < 0)
237 if (s
->n_segments
== 0) {
238 av_log(h
, AV_LOG_WARNING
, "Empty playlist\n");
242 s
->cur_seq_no
= s
->start_seq_no
;
243 if (!s
->finished
&& s
->n_segments
>= 3)
244 s
->cur_seq_no
= s
->start_seq_no
+ s
->n_segments
- 3;
253 static int hls_read(URLContext
*h
, uint8_t *buf
, int size
)
255 HLSContext
*s
= h
->priv_data
;
258 int64_t reload_interval
;
262 ret
= ffurl_read(s
->seg_hd
, buf
, size
);
267 ffurl_close(s
->seg_hd
);
271 reload_interval
= s
->n_segments
> 0 ?
272 s
->segments
[s
->n_segments
- 1]->duration
:
276 int64_t now
= av_gettime_relative();
277 if (now
- s
->last_load_time
>= reload_interval
) {
278 if ((ret
= parse_playlist(h
, s
->playlisturl
)) < 0)
280 /* If we need to reload the playlist again below (if
281 * there's still no more segments), switch to a reload
282 * interval of half the target duration. */
283 reload_interval
= s
->target_duration
/ 2;
286 if (s
->cur_seq_no
< s
->start_seq_no
) {
287 av_log(h
, AV_LOG_WARNING
,
288 "skipping %d segments ahead, expired from playlist\n",
289 s
->start_seq_no
- s
->cur_seq_no
);
290 s
->cur_seq_no
= s
->start_seq_no
;
292 if (s
->cur_seq_no
- s
->start_seq_no
>= s
->n_segments
) {
295 while (av_gettime_relative() - s
->last_load_time
< reload_interval
) {
296 if (ff_check_interrupt(&h
->interrupt_callback
))
302 url
= s
->segments
[s
->cur_seq_no
- s
->start_seq_no
]->url
,
303 av_log(h
, AV_LOG_DEBUG
, "opening %s\n", url
);
304 ret
= ffurl_open(&s
->seg_hd
, url
, AVIO_FLAG_READ
,
305 &h
->interrupt_callback
, NULL
);
307 if (ff_check_interrupt(&h
->interrupt_callback
))
309 av_log(h
, AV_LOG_WARNING
, "Unable to open %s\n", url
);
316 URLProtocol ff_hls_protocol
= {
318 .url_open
= hls_open
,
319 .url_read
= hls_read
,
320 .url_close
= hls_close
,
321 .flags
= URL_PROTOCOL_FLAG_NESTED_SCHEME
,
322 .priv_data_size
= sizeof(HLSContext
),