FFmpeg
Main Page
Related Pages
Modules
Data Structures
Files
Examples
File List
Globals
All
Data Structures
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
libavformat
hlsproto.c
Go to the documentation of this file.
1
/*
2
* Apple HTTP Live Streaming Protocol Handler
3
* Copyright (c) 2010 Martin Storsjo
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
/**
23
* @file
24
* Apple HTTP Live Streaming Protocol Handler
25
* http://tools.ietf.org/html/draft-pantos-http-live-streaming
26
*/
27
28
#include "
libavutil/avstring.h
"
29
#include "
libavutil/time.h
"
30
#include "
avformat.h
"
31
#include "
internal.h
"
32
#include "
url.h
"
33
#include "
version.h
"
34
35
/*
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
41
* playlists.
42
*
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.
45
*/
46
47
struct
segment
{
48
int
duration
;
49
char
url
[
MAX_URL_SIZE
];
50
};
51
52
struct
variant
{
53
int
bandwidth
;
54
char
url
[
MAX_URL_SIZE
];
55
};
56
57
typedef
struct
HLSContext
{
58
char
playlisturl
[
MAX_URL_SIZE
];
59
int
target_duration
;
60
int
start_seq_no
;
61
int
finished
;
62
int
n_segments
;
63
struct
segment
**
segments
;
64
int
n_variants
;
65
struct
variant
**
variants
;
66
int
cur_seq_no
;
67
URLContext
*
seg_hd
;
68
int64_t
last_load_time
;
69
}
HLSContext
;
70
71
static
int
read_chomp_line
(
AVIOContext
*s,
char
*buf,
int
maxlen)
72
{
73
int
len
=
ff_get_line
(s, buf, maxlen);
74
while
(len > 0 && isspace(buf[len - 1]))
75
buf[--
len
] =
'\0'
;
76
return
len
;
77
}
78
79
static
void
free_segment_list
(
HLSContext
*s)
80
{
81
int
i;
82
for
(i = 0; i < s->
n_segments
; i++)
83
av_free
(s->
segments
[i]);
84
av_freep
(&s->
segments
);
85
s->
n_segments
= 0;
86
}
87
88
static
void
free_variant_list
(
HLSContext
*s)
89
{
90
int
i;
91
for
(i = 0; i < s->
n_variants
; i++)
92
av_free
(s->
variants
[i]);
93
av_freep
(&s->
variants
);
94
s->
n_variants
= 0;
95
}
96
97
struct
variant_info
{
98
char
bandwidth
[20];
99
};
100
101
static
void
handle_variant_args
(
struct
variant_info
*info,
const
char
*key,
102
int
key_len,
char
**dest,
int
*dest_len)
103
{
104
if
(!strncmp(key,
"BANDWIDTH="
, key_len)) {
105
*dest = info->
bandwidth
;
106
*dest_len =
sizeof
(info->
bandwidth
);
107
}
108
}
109
110
static
int
parse_playlist
(
URLContext
*h,
const
char
*url)
111
{
112
HLSContext
*s = h->
priv_data
;
113
AVIOContext
*in;
114
int
ret = 0,
duration
= 0, is_segment = 0, is_variant = 0, bandwidth = 0;
115
char
line
[1024];
116
const
char
*ptr;
117
118
if
((ret =
avio_open2
(&in, url,
AVIO_FLAG_READ
,
119
&h->
interrupt_callback
,
NULL
)) < 0)
120
return
ret;
121
122
read_chomp_line
(in, line,
sizeof
(line));
123
if
(strcmp(line,
"#EXTM3U"
))
124
return
AVERROR_INVALIDDATA
;
125
126
free_segment_list
(s);
127
s->
finished
= 0;
128
while
(!
url_feof
(in)) {
129
read_chomp_line
(in, line,
sizeof
(line));
130
if
(
av_strstart
(line,
"#EXT-X-STREAM-INF:"
, &ptr)) {
131
struct
variant_info
info = {{0}};
132
is_variant = 1;
133
ff_parse_key_value
(ptr, (
ff_parse_key_val_cb
)
handle_variant_args
,
134
&info);
135
bandwidth
= atoi(info.
bandwidth
);
136
}
else
if
(
av_strstart
(line,
"#EXT-X-TARGETDURATION:"
, &ptr)) {
137
s->
target_duration
= atoi(ptr);
138
}
else
if
(
av_strstart
(line,
"#EXT-X-MEDIA-SEQUENCE:"
, &ptr)) {
139
s->
start_seq_no
= atoi(ptr);
140
}
else
if
(
av_strstart
(line,
"#EXT-X-ENDLIST"
, &ptr)) {
141
s->
finished
= 1;
142
}
else
if
(
av_strstart
(line,
"#EXTINF:"
, &ptr)) {
143
is_segment = 1;
144
duration
= atoi(ptr);
145
}
else
if
(
av_strstart
(line,
"#"
,
NULL
)) {
146
continue
;
147
}
else
if
(line[0]) {
148
if
(is_segment) {
149
struct
segment
*seg =
av_malloc
(
sizeof
(
struct
segment
));
150
if
(!seg) {
151
ret =
AVERROR
(ENOMEM);
152
goto
fail;
153
}
154
seg->
duration
=
duration
;
155
ff_make_absolute_url
(seg->
url
,
sizeof
(seg->
url
), url, line);
156
dynarray_add
(&s->
segments
, &s->
n_segments
, seg);
157
is_segment = 0;
158
}
else
if
(is_variant) {
159
struct
variant
*var =
av_malloc
(
sizeof
(
struct
variant
));
160
if
(!var) {
161
ret =
AVERROR
(ENOMEM);
162
goto
fail;
163
}
164
var->
bandwidth
=
bandwidth
;
165
ff_make_absolute_url
(var->
url
,
sizeof
(var->
url
), url, line);
166
dynarray_add
(&s->
variants
, &s->
n_variants
, var);
167
is_variant = 0;
168
}
169
}
170
}
171
s->
last_load_time
=
av_gettime
();
172
173
fail:
174
avio_close
(in);
175
return
ret;
176
}
177
178
static
int
hls_close
(
URLContext
*h)
179
{
180
HLSContext
*s = h->
priv_data
;
181
182
free_segment_list
(s);
183
free_variant_list
(s);
184
ffurl_close
(s->
seg_hd
);
185
return
0;
186
}
187
188
static
int
hls_open
(
URLContext
*h,
const
char
*uri,
int
flags
)
189
{
190
HLSContext
*s = h->
priv_data
;
191
int
ret, i;
192
const
char
*nested_url;
193
194
if
(flags &
AVIO_FLAG_WRITE
)
195
return
AVERROR
(ENOSYS);
196
197
h->
is_streamed
= 1;
198
199
if
(
av_strstart
(uri,
"hls+"
, &nested_url)) {
200
av_strlcpy
(s->
playlisturl
, nested_url,
sizeof
(s->
playlisturl
));
201
}
else
if
(
av_strstart
(uri,
"hls://"
, &nested_url)) {
202
av_log
(h,
AV_LOG_ERROR
,
203
"No nested protocol specified. Specify e.g. hls+http://%s\n"
,
204
nested_url);
205
ret =
AVERROR
(EINVAL);
206
goto
fail;
207
#if FF_API_APPLEHTTP_PROTO
208
}
else
if
(
av_strstart
(uri,
"applehttp+"
, &nested_url)) {
209
av_strlcpy
(s->
playlisturl
, nested_url,
sizeof
(s->
playlisturl
));
210
av_log
(h,
AV_LOG_WARNING
,
211
"The applehttp protocol is deprecated, use hls+%s as url "
212
"instead.\n"
, nested_url);
213
}
else
if
(
av_strstart
(uri,
"applehttp://"
, &nested_url)) {
214
av_strlcpy
(s->
playlisturl
,
"http://"
,
sizeof
(s->
playlisturl
));
215
av_strlcat
(s->
playlisturl
, nested_url,
sizeof
(s->
playlisturl
));
216
av_log
(h,
AV_LOG_WARNING
,
217
"The applehttp protocol is deprecated, use hls+http://%s as url "
218
"instead.\n"
, nested_url);
219
#endif
220
}
else
{
221
av_log
(h,
AV_LOG_ERROR
,
"Unsupported url %s\n"
, uri);
222
ret =
AVERROR
(EINVAL);
223
goto
fail;
224
}
225
av_log
(h,
AV_LOG_WARNING
,
226
"Using the hls protocol is discouraged, please try using the "
227
"hls demuxer instead. The hls demuxer should be more complete "
228
"and work as well as the protocol implementation. (If not, "
229
"please report it.) To use the demuxer, simply use %s as url.\n"
,
230
s->
playlisturl
);
231
232
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
233
goto
fail;
234
235
if
(s->
n_segments
== 0 && s->
n_variants
> 0) {
236
int
max_bandwidth
= 0, maxvar = -1;
237
for
(i = 0; i < s->
n_variants
; i++) {
238
if
(s->
variants
[i]->
bandwidth
> max_bandwidth || i == 0) {
239
max_bandwidth = s->
variants
[i]->
bandwidth
;
240
maxvar = i;
241
}
242
}
243
av_strlcpy
(s->
playlisturl
, s->
variants
[maxvar]->
url
,
244
sizeof
(s->
playlisturl
));
245
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
246
goto
fail;
247
}
248
249
if
(s->
n_segments
== 0) {
250
av_log
(h,
AV_LOG_WARNING
,
"Empty playlist\n"
);
251
ret =
AVERROR
(EIO);
252
goto
fail;
253
}
254
s->
cur_seq_no
= s->
start_seq_no
;
255
if
(!s->
finished
&& s->
n_segments
>= 3)
256
s->
cur_seq_no
= s->
start_seq_no
+ s->
n_segments
- 3;
257
258
return
0;
259
260
fail:
261
hls_close
(h);
262
return
ret;
263
}
264
265
static
int
hls_read
(
URLContext
*h,
uint8_t
*buf,
int
size
)
266
{
267
HLSContext
*s = h->
priv_data
;
268
const
char
*
url
;
269
int
ret;
270
int64_t reload_interval;
271
272
start:
273
if
(s->
seg_hd
) {
274
ret =
ffurl_read
(s->
seg_hd
, buf, size);
275
if
(ret > 0)
276
return
ret;
277
}
278
if
(s->
seg_hd
) {
279
ffurl_close
(s->
seg_hd
);
280
s->
seg_hd
=
NULL
;
281
s->
cur_seq_no
++;
282
}
283
reload_interval = s->
n_segments
> 0 ?
284
s->
segments
[s->
n_segments
- 1]->
duration
:
285
s->
target_duration
;
286
reload_interval *= 1000000;
287
retry:
288
if
(!s->
finished
) {
289
int64_t now =
av_gettime
();
290
if
(now - s->
last_load_time
>= reload_interval) {
291
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
292
return
ret;
293
/* If we need to reload the playlist again below (if
294
* there's still no more segments), switch to a reload
295
* interval of half the target duration. */
296
reload_interval = s->
target_duration
* 500000LL;
297
}
298
}
299
if
(s->
cur_seq_no
< s->
start_seq_no
) {
300
av_log
(h,
AV_LOG_WARNING
,
301
"skipping %d segments ahead, expired from playlist\n"
,
302
s->
start_seq_no
- s->
cur_seq_no
);
303
s->
cur_seq_no
= s->
start_seq_no
;
304
}
305
if
(s->
cur_seq_no
- s->
start_seq_no
>= s->
n_segments
) {
306
if
(s->
finished
)
307
return
AVERROR_EOF
;
308
while
(
av_gettime
() - s->
last_load_time
< reload_interval) {
309
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
310
return
AVERROR_EXIT
;
311
av_usleep
(100*1000);
312
}
313
goto
retry;
314
}
315
url = s->
segments
[s->
cur_seq_no
- s->
start_seq_no
]->
url
,
316
av_log
(h,
AV_LOG_DEBUG
,
"opening %s\n"
, url);
317
ret =
ffurl_open
(&s->
seg_hd
, url,
AVIO_FLAG_READ
,
318
&h->
interrupt_callback
,
NULL
);
319
if
(ret < 0) {
320
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
321
return
AVERROR_EXIT
;
322
av_log
(h,
AV_LOG_WARNING
,
"Unable to open %s\n"
, url);
323
s->
cur_seq_no
++;
324
goto
retry;
325
}
326
goto
start;
327
}
328
329
#if FF_API_APPLEHTTP_PROTO
330
URLProtocol
ff_applehttp_protocol = {
331
.
name
=
"applehttp"
,
332
.url_open =
hls_open
,
333
.url_read =
hls_read
,
334
.url_close =
hls_close
,
335
.flags =
URL_PROTOCOL_FLAG_NESTED_SCHEME
,
336
.priv_data_size =
sizeof
(
HLSContext
),
337
};
338
#endif
339
340
URLProtocol
ff_hls_protocol
= {
341
.
name
=
"hls"
,
342
.url_open =
hls_open
,
343
.url_read =
hls_read
,
344
.url_close =
hls_close
,
345
.flags =
URL_PROTOCOL_FLAG_NESTED_SCHEME
,
346
.priv_data_size =
sizeof
(
HLSContext
),
347
};
Generated on Sat May 25 2013 03:58:46 for FFmpeg by
1.8.2