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