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