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_relative
();
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_relative
();
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_relative
() - 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 Fri Dec 5 2014 04:42:11 for FFmpeg by
1.8.2