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