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
int64_t
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
int64_t
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, is_segment = 0, is_variant = 0, bandwidth = 0;
115
int64_t
duration
= 0;
116
char
line
[1024];
117
const
char
*ptr;
118
119
if
((ret =
avio_open2
(&in, url,
AVIO_FLAG_READ
,
120
&h->
interrupt_callback
, NULL)) < 0)
121
return
ret
;
122
123
read_chomp_line
(in, line,
sizeof
(line));
124
if
(strcmp(line,
"#EXTM3U"
))
125
return
AVERROR_INVALIDDATA
;
126
127
free_segment_list
(s);
128
s->
finished
= 0;
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}};
133
is_variant = 1;
134
ff_parse_key_value
(ptr, (
ff_parse_key_val_cb
)
handle_variant_args
,
135
&info);
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)) {
142
s->
finished
= 1;
143
}
else
if
(
av_strstart
(line,
"#EXTINF:"
, &ptr)) {
144
is_segment = 1;
145
duration = atof(ptr) *
AV_TIME_BASE
;
146
}
else
if
(
av_strstart
(line,
"#"
, NULL)) {
147
continue
;
148
}
else
if
(line[0]) {
149
if
(is_segment) {
150
struct
segment
*seg =
av_malloc
(
sizeof
(
struct
segment
));
151
if
(!seg) {
152
ret =
AVERROR
(ENOMEM);
153
goto
fail;
154
}
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);
158
is_segment = 0;
159
}
else
if
(is_variant) {
160
struct
variant
*var =
av_malloc
(
sizeof
(
struct
variant
));
161
if
(!var) {
162
ret =
AVERROR
(ENOMEM);
163
goto
fail;
164
}
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);
168
is_variant = 0;
169
}
170
}
171
}
172
s->
last_load_time
=
av_gettime
();
173
174
fail:
175
avio_close
(in);
176
return
ret
;
177
}
178
179
static
int
hls_close
(
URLContext
*h)
180
{
181
HLSContext
*
s
= h->
priv_data
;
182
183
free_segment_list
(s);
184
free_variant_list
(s);
185
ffurl_close
(s->
seg_hd
);
186
return
0;
187
}
188
189
static
int
hls_open
(
URLContext
*h,
const
char
*uri,
int
flags
)
190
{
191
HLSContext
*
s
= h->
priv_data
;
192
int
ret
, i;
193
const
char
*nested_url;
194
195
if
(flags &
AVIO_FLAG_WRITE
)
196
return
AVERROR
(ENOSYS);
197
198
h->
is_streamed
= 1;
199
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"
,
205
nested_url);
206
ret =
AVERROR
(EINVAL);
207
goto
fail;
208
}
else
{
209
av_log
(h,
AV_LOG_ERROR
,
"Unsupported url %s\n"
, uri);
210
ret =
AVERROR
(EINVAL);
211
goto
fail;
212
}
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"
,
218
s->
playlisturl
);
219
220
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
221
goto
fail;
222
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
;
228
maxvar = i;
229
}
230
}
231
av_strlcpy
(s->
playlisturl
, s->
variants
[maxvar]->
url
,
232
sizeof
(s->
playlisturl
));
233
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
234
goto
fail;
235
}
236
237
if
(s->
n_segments
== 0) {
238
av_log
(h,
AV_LOG_WARNING
,
"Empty playlist\n"
);
239
ret =
AVERROR
(EIO);
240
goto
fail;
241
}
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;
245
246
return
0;
247
248
fail:
249
hls_close
(h);
250
return
ret
;
251
}
252
253
static
int
hls_read
(
URLContext
*h,
uint8_t
*
buf
,
int
size
)
254
{
255
HLSContext
*
s
= h->
priv_data
;
256
const
char
*
url
;
257
int
ret
;
258
int64_t reload_interval;
259
260
start
:
261
if
(s->
seg_hd
) {
262
ret =
ffurl_read
(s->
seg_hd
, buf, size);
263
if
(ret > 0)
264
return
ret
;
265
}
266
if
(s->
seg_hd
) {
267
ffurl_close
(s->
seg_hd
);
268
s->
seg_hd
= NULL;
269
s->
cur_seq_no
++;
270
}
271
reload_interval = s->
n_segments
> 0 ?
272
s->
segments
[s->
n_segments
- 1]->
duration
:
273
s->
target_duration
;
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
/ 2;
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 Sun Sep 14 2014 18:56:12 for FFmpeg by
1.8.2