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_freep
(&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_freep
(&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
ret =
AVERROR_INVALIDDATA
;
126
goto
fail;
127
}
128
129
free_segment_list
(s);
130
s->
finished
= 0;
131
while
(!
avio_feof
(in)) {
132
read_chomp_line
(in, line,
sizeof
(line));
133
if
(
av_strstart
(line,
"#EXT-X-STREAM-INF:"
, &ptr)) {
134
struct
variant_info
info = {{0}};
135
is_variant = 1;
136
ff_parse_key_value
(ptr, (
ff_parse_key_val_cb
)
handle_variant_args
,
137
&info);
138
bandwidth
= atoi(info.
bandwidth
);
139
}
else
if
(
av_strstart
(line,
"#EXT-X-TARGETDURATION:"
, &ptr)) {
140
s->
target_duration
= atoi(ptr) *
AV_TIME_BASE
;
141
}
else
if
(
av_strstart
(line,
"#EXT-X-MEDIA-SEQUENCE:"
, &ptr)) {
142
s->
start_seq_no
= atoi(ptr);
143
}
else
if
(
av_strstart
(line,
"#EXT-X-ENDLIST"
, &ptr)) {
144
s->
finished
= 1;
145
}
else
if
(
av_strstart
(line,
"#EXTINF:"
, &ptr)) {
146
is_segment = 1;
147
duration = atof(ptr) *
AV_TIME_BASE
;
148
}
else
if
(
av_strstart
(line,
"#"
,
NULL
)) {
149
continue
;
150
}
else
if
(line[0]) {
151
if
(is_segment) {
152
struct
segment
*seg =
av_malloc
(
sizeof
(
struct
segment
));
153
if
(!seg) {
154
ret =
AVERROR
(ENOMEM);
155
goto
fail;
156
}
157
seg->
duration
=
duration
;
158
ff_make_absolute_url
(seg->
url
,
sizeof
(seg->
url
), url, line);
159
dynarray_add
(&s->
segments
, &s->
n_segments
, seg);
160
is_segment = 0;
161
}
else
if
(is_variant) {
162
struct
variant
*var =
av_malloc
(
sizeof
(
struct
variant
));
163
if
(!var) {
164
ret =
AVERROR
(ENOMEM);
165
goto
fail;
166
}
167
var->
bandwidth
=
bandwidth
;
168
ff_make_absolute_url
(var->
url
,
sizeof
(var->
url
), url, line);
169
dynarray_add
(&s->
variants
, &s->
n_variants
, var);
170
is_variant = 0;
171
}
172
}
173
}
174
s->
last_load_time
=
av_gettime_relative
();
175
176
fail:
177
avio_close
(in);
178
return
ret
;
179
}
180
181
static
int
hls_close
(
URLContext
*h)
182
{
183
HLSContext
*
s
= h->
priv_data
;
184
185
free_segment_list
(s);
186
free_variant_list
(s);
187
ffurl_close
(s->
seg_hd
);
188
return
0;
189
}
190
191
static
int
hls_open
(
URLContext
*h,
const
char
*uri,
int
flags
)
192
{
193
HLSContext
*
s
= h->
priv_data
;
194
int
ret
, i;
195
const
char
*nested_url;
196
197
if
(flags &
AVIO_FLAG_WRITE
)
198
return
AVERROR
(ENOSYS);
199
200
h->
is_streamed
= 1;
201
202
if
(
av_strstart
(uri,
"hls+"
, &nested_url)) {
203
av_strlcpy
(s->
playlisturl
, nested_url,
sizeof
(s->
playlisturl
));
204
}
else
if
(
av_strstart
(uri,
"hls://"
, &nested_url)) {
205
av_log
(h,
AV_LOG_ERROR
,
206
"No nested protocol specified. Specify e.g. hls+http://%s\n"
,
207
nested_url);
208
ret =
AVERROR
(EINVAL);
209
goto
fail;
210
}
else
{
211
av_log
(h,
AV_LOG_ERROR
,
"Unsupported url %s\n"
, uri);
212
ret =
AVERROR
(EINVAL);
213
goto
fail;
214
}
215
av_log
(h,
AV_LOG_WARNING
,
216
"Using the hls protocol is discouraged, please try using the "
217
"hls demuxer instead. The hls demuxer should be more complete "
218
"and work as well as the protocol implementation. (If not, "
219
"please report it.) To use the demuxer, simply use %s as url.\n"
,
220
s->
playlisturl
);
221
222
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
223
goto
fail;
224
225
if
(s->
n_segments
== 0 && s->
n_variants
> 0) {
226
int
max_bandwidth = 0, maxvar = -1;
227
for
(i = 0; i < s->
n_variants
; i++) {
228
if
(s->
variants
[i]->
bandwidth
> max_bandwidth || i == 0) {
229
max_bandwidth = s->
variants
[i]->
bandwidth
;
230
maxvar = i;
231
}
232
}
233
av_strlcpy
(s->
playlisturl
, s->
variants
[maxvar]->
url
,
234
sizeof
(s->
playlisturl
));
235
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
236
goto
fail;
237
}
238
239
if
(s->
n_segments
== 0) {
240
av_log
(h,
AV_LOG_WARNING
,
"Empty playlist\n"
);
241
ret =
AVERROR
(EIO);
242
goto
fail;
243
}
244
s->
cur_seq_no
= s->
start_seq_no
;
245
if
(!s->
finished
&& s->
n_segments
>= 3)
246
s->
cur_seq_no
= s->
start_seq_no
+ s->
n_segments
- 3;
247
248
return
0;
249
250
fail:
251
hls_close
(h);
252
return
ret
;
253
}
254
255
static
int
hls_read
(
URLContext
*h,
uint8_t
*
buf
,
int
size
)
256
{
257
HLSContext
*
s
= h->
priv_data
;
258
const
char
*
url
;
259
int
ret
;
260
int64_t reload_interval;
261
262
start
:
263
if
(s->
seg_hd
) {
264
ret =
ffurl_read
(s->
seg_hd
, buf, size);
265
if
(ret > 0)
266
return
ret
;
267
}
268
if
(s->
seg_hd
) {
269
ffurl_close
(s->
seg_hd
);
270
s->
seg_hd
=
NULL
;
271
s->
cur_seq_no
++;
272
}
273
reload_interval = s->
n_segments
> 0 ?
274
s->
segments
[s->
n_segments
- 1]->
duration
:
275
s->
target_duration
;
276
retry:
277
if
(!s->
finished
) {
278
int64_t now =
av_gettime_relative
();
279
if
(now - s->
last_load_time
>= reload_interval) {
280
if
((ret =
parse_playlist
(h, s->
playlisturl
)) < 0)
281
return
ret
;
282
/* If we need to reload the playlist again below (if
283
* there's still no more segments), switch to a reload
284
* interval of half the target duration. */
285
reload_interval = s->
target_duration
/ 2;
286
}
287
}
288
if
(s->
cur_seq_no
< s->
start_seq_no
) {
289
av_log
(h,
AV_LOG_WARNING
,
290
"skipping %d segments ahead, expired from playlist\n"
,
291
s->
start_seq_no
- s->
cur_seq_no
);
292
s->
cur_seq_no
= s->
start_seq_no
;
293
}
294
if
(s->
cur_seq_no
- s->
start_seq_no
>= s->
n_segments
) {
295
if
(s->
finished
)
296
return
AVERROR_EOF
;
297
while
(
av_gettime_relative
() - s->
last_load_time
< reload_interval) {
298
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
299
return
AVERROR_EXIT
;
300
av_usleep
(100*1000);
301
}
302
goto
retry;
303
}
304
url = s->
segments
[s->
cur_seq_no
- s->
start_seq_no
]->url,
305
av_log
(h,
AV_LOG_DEBUG
,
"opening %s\n"
, url);
306
ret =
ffurl_open
(&s->
seg_hd
, url,
AVIO_FLAG_READ
,
307
&h->
interrupt_callback
,
NULL
);
308
if
(ret < 0) {
309
if
(
ff_check_interrupt
(&h->
interrupt_callback
))
310
return
AVERROR_EXIT
;
311
av_log
(h,
AV_LOG_WARNING
,
"Unable to open %s\n"
, url);
312
s->
cur_seq_no
++;
313
goto
retry;
314
}
315
goto
start
;
316
}
317
318
URLProtocol
ff_hls_protocol
= {
319
.
name
=
"hls"
,
320
.url_open =
hls_open
,
321
.url_read =
hls_read
,
322
.url_close =
hls_close
,
323
.flags =
URL_PROTOCOL_FLAG_NESTED_SCHEME
,
324
.priv_data_size =
sizeof
(
HLSContext
),
325
};
Generated on Sun Mar 8 2015 02:35:09 for FFmpeg by
1.8.2