FFmpeg
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
Examples
File List
Globals
All
Data Structures
Namespaces
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 &&
av_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
}
else
{
208
av_log
(h,
AV_LOG_ERROR
,
"Unsupported url %s\n"
, uri);
209
ret =
AVERROR
(EINVAL);
210
goto
fail;
211
}
212
av_log
(h,
AV_LOG_WARNING
,
213
"Using the hls protocol is discouraged, please try using the "
214
"hls demuxer instead. The hls demuxer should be more complete "
215
"and work as well as the protocol implementation. (If not, "
216
"please report it.) To use the demuxer, simply use %s as url.\n"
,
217
s->
playlisturl
);
218
219
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
220
goto
fail;
221
222
if
(s->
n_segments
== 0 && s->
n_variants
> 0) {
223
int
max_bandwidth
= 0, maxvar = -1;
224
for
(i = 0; i < s->
n_variants
; i++) {
225
if
(s->
variants
[i]->
bandwidth
> max_bandwidth || i == 0) {
226
max_bandwidth = s->
variants
[i]->
bandwidth
;
227
maxvar = i;
228
}
229
}
230
av_strlcpy
(s->
playlisturl
, s->
variants
[maxvar]->
url
,
231
sizeof
(s->
playlisturl
));
232
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
233
goto
fail;
234
}
235
236
if
(s->
n_segments
== 0) {
237
av_log
(h,
AV_LOG_WARNING
,
"Empty playlist\n"
);
238
ret =
AVERROR
(EIO);
239
goto
fail;
240
}
241
s->
cur_seq_no
= s->
start_seq_no
;
242
if
(!s->
finished
&& s->
n_segments
>= 3)
243
s->
cur_seq_no
= s->
start_seq_no
+ s->
n_segments
- 3;
244
245
return
0;
246
247
fail:
248
hls_close
(h);
249
return
ret
;
250
}
251
252
static
int
hls_read
(
URLContext
*h,
uint8_t
*
buf
,
int
size
)
253
{
254
HLSContext
*
s
= h->
priv_data
;
255
const
char
*
url
;
256
int
ret
;
257
int64_t reload_interval;
258
259
start
:
260
if
(s->
seg_hd
) {
261
ret =
ffurl_read
(s->
seg_hd
, buf, size);
262
if
(ret > 0)
263
return
ret
;
264
}
265
if
(s->
seg_hd
) {
266
ffurl_close
(s->
seg_hd
);
267
s->
seg_hd
= NULL;
268
s->
cur_seq_no
++;
269
}
270
reload_interval = s->
n_segments
> 0 ?
271
s->
segments
[s->
n_segments
- 1]->
duration
:
272
s->
target_duration
;
273
reload_interval *= 1000000;
274
retry:
275
if
(!s->
finished
) {
276
int64_t now =
av_gettime
();
277
if
(now - s->
last_load_time
>= reload_interval) {
278
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
279
return
ret
;
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
* 500000LL;
284
}
285
}
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
;
291
}
292
if
(s->
cur_seq_no
- s->
start_seq_no
>= s->
n_segments
) {
293
if
(s->
finished
)
294
return
AVERROR_EOF
;
295
while
(
av_gettime
() - s->
last_load_time
< reload_interval) {
296
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
297
return
AVERROR_EXIT
;
298
av_usleep
(100*1000);
299
}
300
goto
retry;
301
}
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);
306
if
(ret < 0) {
307
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
308
return
AVERROR_EXIT
;
309
av_log
(h,
AV_LOG_WARNING
,
"Unable to open %s\n"
, url);
310
s->
cur_seq_no
++;
311
goto
retry;
312
}
313
goto
start
;
314
}
315
316
URLProtocol
ff_hls_protocol
= {
317
.
name
=
"hls"
,
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
),
323
};
Generated on Wed Jul 10 2013 23:48:12 for FFmpeg by
1.8.2