ffmpeg / libavformat / applehttp.c @ 4dcde00c
History | View | Annotate | Download (17.6 KB)
1 | cd223e0b | Martin Storsjö | /*
|
---|---|---|---|
2 | * Apple HTTP Live Streaming demuxer
|
||
3 | * Copyright (c) 2010 Martin Storsjo
|
||
4 | *
|
||
5 | 2912e87a | Mans Rullgard | * This file is part of Libav.
|
6 | cd223e0b | Martin Storsjö | *
|
7 | 2912e87a | Mans Rullgard | * Libav is free software; you can redistribute it and/or
|
8 | cd223e0b | Martin Storsjö | * 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 | 2912e87a | Mans Rullgard | * Libav is distributed in the hope that it will be useful,
|
13 | cd223e0b | Martin Storsjö | * 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 | 2912e87a | Mans Rullgard | * License along with Libav; if not, write to the Free Software
|
19 | cd223e0b | Martin Storsjö | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 | */
|
||
21 | |||
22 | /**
|
||
23 | * @file
|
||
24 | * Apple HTTP Live Streaming demuxer
|
||
25 | * http://tools.ietf.org/html/draft-pantos-http-live-streaming
|
||
26 | */
|
||
27 | |||
28 | b2125520 | Måns Rullgård | #define _XOPEN_SOURCE 600 |
29 | cd223e0b | Martin Storsjö | #include "libavutil/avstring.h" |
30 | #include "avformat.h" |
||
31 | #include "internal.h" |
||
32 | #include <unistd.h> |
||
33 | |||
34 | /*
|
||
35 | * An apple http stream consists of a playlist with media segment files,
|
||
36 | * played sequentially. There may be several playlists with the same
|
||
37 | * video content, in different bandwidth variants, that are played in
|
||
38 | * parallel (preferrably only one bandwidth variant at a time). In this case,
|
||
39 | * the user supplied the url to a main playlist that only lists the variant
|
||
40 | * playlists.
|
||
41 | *
|
||
42 | * If the main playlist doesn't point at any variants, we still create
|
||
43 | * one anonymous toplevel variant for this, to maintain the structure.
|
||
44 | */
|
||
45 | |||
46 | struct segment {
|
||
47 | int duration;
|
||
48 | char url[MAX_URL_SIZE];
|
||
49 | }; |
||
50 | |||
51 | /*
|
||
52 | * Each variant has its own demuxer. If it currently is active,
|
||
53 | ae628ec1 | Anton Khirnov | * it has an open AVIOContext too, and potentially an AVPacket
|
54 | cd223e0b | Martin Storsjö | * containing the next packet from this stream.
|
55 | */
|
||
56 | struct variant {
|
||
57 | int bandwidth;
|
||
58 | char url[MAX_URL_SIZE];
|
||
59 | ae628ec1 | Anton Khirnov | AVIOContext *pb; |
60 | cd223e0b | Martin Storsjö | AVFormatContext *ctx; |
61 | AVPacket pkt; |
||
62 | int stream_offset;
|
||
63 | |||
64 | int start_seq_no;
|
||
65 | int n_segments;
|
||
66 | struct segment **segments;
|
||
67 | int needed;
|
||
68 | }; |
||
69 | |||
70 | typedef struct AppleHTTPContext { |
||
71 | int target_duration;
|
||
72 | int finished;
|
||
73 | int n_variants;
|
||
74 | struct variant **variants;
|
||
75 | int cur_seq_no;
|
||
76 | int64_t last_load_time; |
||
77 | int64_t last_packet_dts; |
||
78 | int max_start_seq, min_end_seq;
|
||
79 | } AppleHTTPContext; |
||
80 | |||
81 | ae628ec1 | Anton Khirnov | static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) |
82 | cd223e0b | Martin Storsjö | { |
83 | int len = ff_get_line(s, buf, maxlen);
|
||
84 | while (len > 0 && isspace(buf[len - 1])) |
||
85 | buf[--len] = '\0';
|
||
86 | return len;
|
||
87 | } |
||
88 | |||
89 | static void free_segment_list(struct variant *var) |
||
90 | { |
||
91 | int i;
|
||
92 | for (i = 0; i < var->n_segments; i++) |
||
93 | av_free(var->segments[i]); |
||
94 | av_freep(&var->segments); |
||
95 | var->n_segments = 0;
|
||
96 | } |
||
97 | |||
98 | static void free_variant_list(AppleHTTPContext *c) |
||
99 | { |
||
100 | int i;
|
||
101 | for (i = 0; i < c->n_variants; i++) { |
||
102 | struct variant *var = c->variants[i];
|
||
103 | free_segment_list(var); |
||
104 | av_free_packet(&var->pkt); |
||
105 | if (var->pb)
|
||
106 | 22a3212e | Anton Khirnov | avio_close(var->pb); |
107 | cd223e0b | Martin Storsjö | if (var->ctx) {
|
108 | var->ctx->pb = NULL;
|
||
109 | av_close_input_file(var->ctx); |
||
110 | } |
||
111 | av_free(var); |
||
112 | } |
||
113 | av_freep(&c->variants); |
||
114 | c->n_variants = 0;
|
||
115 | } |
||
116 | |||
117 | /*
|
||
118 | * Used to reset a statically allocated AVPacket to a clean slate,
|
||
119 | * containing no data.
|
||
120 | */
|
||
121 | static void reset_packet(AVPacket *pkt) |
||
122 | { |
||
123 | av_init_packet(pkt); |
||
124 | pkt->data = NULL;
|
||
125 | } |
||
126 | |||
127 | static struct variant *new_variant(AppleHTTPContext *c, int bandwidth, |
||
128 | const char *url, const char *base) |
||
129 | { |
||
130 | struct variant *var = av_mallocz(sizeof(struct variant)); |
||
131 | if (!var)
|
||
132 | return NULL; |
||
133 | reset_packet(&var->pkt); |
||
134 | var->bandwidth = bandwidth; |
||
135 | f1f60f52 | Martin Storsjö | ff_make_absolute_url(var->url, sizeof(var->url), base, url);
|
136 | cd223e0b | Martin Storsjö | dynarray_add(&c->variants, &c->n_variants, var); |
137 | return var;
|
||
138 | } |
||
139 | |||
140 | struct variant_info {
|
||
141 | char bandwidth[20]; |
||
142 | }; |
||
143 | |||
144 | static void handle_variant_args(struct variant_info *info, const char *key, |
||
145 | int key_len, char **dest, int *dest_len) |
||
146 | { |
||
147 | 2b0decf6 | Martin Storsjö | if (!strncmp(key, "BANDWIDTH=", key_len)) { |
148 | cd223e0b | Martin Storsjö | *dest = info->bandwidth; |
149 | *dest_len = sizeof(info->bandwidth);
|
||
150 | } |
||
151 | } |
||
152 | |||
153 | static int parse_playlist(AppleHTTPContext *c, const char *url, |
||
154 | ae628ec1 | Anton Khirnov | struct variant *var, AVIOContext *in)
|
155 | cd223e0b | Martin Storsjö | { |
156 | int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0; |
||
157 | char line[1024]; |
||
158 | const char *ptr; |
||
159 | int close_in = 0; |
||
160 | |||
161 | if (!in) {
|
||
162 | close_in = 1;
|
||
163 | 22a3212e | Anton Khirnov | if ((ret = avio_open(&in, url, URL_RDONLY)) < 0) |
164 | cd223e0b | Martin Storsjö | return ret;
|
165 | } |
||
166 | |||
167 | read_chomp_line(in, line, sizeof(line));
|
||
168 | if (strcmp(line, "#EXTM3U")) { |
||
169 | ret = AVERROR_INVALIDDATA; |
||
170 | goto fail;
|
||
171 | } |
||
172 | |||
173 | if (var)
|
||
174 | free_segment_list(var); |
||
175 | c->finished = 0;
|
||
176 | 66e5b1df | Anton Khirnov | while (!in->eof_reached) {
|
177 | cd223e0b | Martin Storsjö | read_chomp_line(in, line, sizeof(line));
|
178 | if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { |
||
179 | struct variant_info info = {{0}}; |
||
180 | is_variant = 1;
|
||
181 | ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, |
||
182 | &info); |
||
183 | bandwidth = atoi(info.bandwidth); |
||
184 | } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { |
||
185 | c->target_duration = atoi(ptr); |
||
186 | } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { |
||
187 | if (!var) {
|
||
188 | var = new_variant(c, 0, url, NULL); |
||
189 | if (!var) {
|
||
190 | ret = AVERROR(ENOMEM); |
||
191 | goto fail;
|
||
192 | } |
||
193 | } |
||
194 | var->start_seq_no = atoi(ptr); |
||
195 | } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { |
||
196 | c->finished = 1;
|
||
197 | } else if (av_strstart(line, "#EXTINF:", &ptr)) { |
||
198 | is_segment = 1;
|
||
199 | duration = atoi(ptr); |
||
200 | } else if (av_strstart(line, "#", NULL)) { |
||
201 | continue;
|
||
202 | } else if (line[0]) { |
||
203 | if (is_variant) {
|
||
204 | if (!new_variant(c, bandwidth, line, url)) {
|
||
205 | ret = AVERROR(ENOMEM); |
||
206 | goto fail;
|
||
207 | } |
||
208 | is_variant = 0;
|
||
209 | bandwidth = 0;
|
||
210 | } |
||
211 | if (is_segment) {
|
||
212 | struct segment *seg;
|
||
213 | if (!var) {
|
||
214 | var = new_variant(c, 0, url, NULL); |
||
215 | if (!var) {
|
||
216 | ret = AVERROR(ENOMEM); |
||
217 | goto fail;
|
||
218 | } |
||
219 | } |
||
220 | seg = av_malloc(sizeof(struct segment)); |
||
221 | if (!seg) {
|
||
222 | ret = AVERROR(ENOMEM); |
||
223 | goto fail;
|
||
224 | } |
||
225 | seg->duration = duration; |
||
226 | f1f60f52 | Martin Storsjö | ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
|
227 | cd223e0b | Martin Storsjö | dynarray_add(&var->segments, &var->n_segments, seg); |
228 | is_segment = 0;
|
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | c->last_load_time = av_gettime(); |
||
233 | |||
234 | fail:
|
||
235 | if (close_in)
|
||
236 | 22a3212e | Anton Khirnov | avio_close(in); |
237 | cd223e0b | Martin Storsjö | return ret;
|
238 | } |
||
239 | |||
240 | static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap) |
||
241 | { |
||
242 | AppleHTTPContext *c = s->priv_data; |
||
243 | int ret = 0, i, j, stream_offset = 0; |
||
244 | |||
245 | if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0) |
||
246 | goto fail;
|
||
247 | |||
248 | if (c->n_variants == 0) { |
||
249 | av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); |
||
250 | ret = AVERROR_EOF; |
||
251 | goto fail;
|
||
252 | } |
||
253 | /* If the playlist only contained variants, parse each individual
|
||
254 | * variant playlist. */
|
||
255 | if (c->n_variants > 1 || c->variants[0]->n_segments == 0) { |
||
256 | for (i = 0; i < c->n_variants; i++) { |
||
257 | struct variant *v = c->variants[i];
|
||
258 | if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) |
||
259 | goto fail;
|
||
260 | } |
||
261 | } |
||
262 | |||
263 | if (c->variants[0]->n_segments == 0) { |
||
264 | av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); |
||
265 | ret = AVERROR_EOF; |
||
266 | goto fail;
|
||
267 | } |
||
268 | |||
269 | /* If this isn't a live stream, calculate the total duration of the
|
||
270 | * stream. */
|
||
271 | if (c->finished) {
|
||
272 | b79c3df0 | John Wimer | int64_t duration = 0;
|
273 | cd223e0b | Martin Storsjö | for (i = 0; i < c->variants[0]->n_segments; i++) |
274 | duration += c->variants[0]->segments[i]->duration;
|
||
275 | s->duration = duration * AV_TIME_BASE; |
||
276 | } |
||
277 | |||
278 | c->min_end_seq = INT_MAX; |
||
279 | /* Open the demuxer for each variant */
|
||
280 | for (i = 0; i < c->n_variants; i++) { |
||
281 | struct variant *v = c->variants[i];
|
||
282 | if (v->n_segments == 0) |
||
283 | continue;
|
||
284 | c->max_start_seq = FFMAX(c->max_start_seq, v->start_seq_no); |
||
285 | c->min_end_seq = FFMIN(c->min_end_seq, v->start_seq_no + |
||
286 | v->n_segments); |
||
287 | ret = av_open_input_file(&v->ctx, v->segments[0]->url, NULL, 0, NULL); |
||
288 | if (ret < 0) |
||
289 | goto fail;
|
||
290 | 22a3212e | Anton Khirnov | avio_close(v->ctx->pb); |
291 | cd223e0b | Martin Storsjö | v->ctx->pb = NULL;
|
292 | v->stream_offset = stream_offset; |
||
293 | /* Create new AVStreams for each stream in this variant */
|
||
294 | for (j = 0; j < v->ctx->nb_streams; j++) { |
||
295 | AVStream *st = av_new_stream(s, i); |
||
296 | if (!st) {
|
||
297 | ret = AVERROR(ENOMEM); |
||
298 | goto fail;
|
||
299 | } |
||
300 | avcodec_copy_context(st->codec, v->ctx->streams[j]->codec); |
||
301 | } |
||
302 | stream_offset += v->ctx->nb_streams; |
||
303 | } |
||
304 | c->last_packet_dts = AV_NOPTS_VALUE; |
||
305 | |||
306 | c->cur_seq_no = c->max_start_seq; |
||
307 | /* If this is a live stream with more than 3 segments, start at the
|
||
308 | * third last segment. */
|
||
309 | if (!c->finished && c->min_end_seq - c->max_start_seq > 3) |
||
310 | c->cur_seq_no = c->min_end_seq - 2;
|
||
311 | |||
312 | return 0; |
||
313 | fail:
|
||
314 | free_variant_list(c); |
||
315 | return ret;
|
||
316 | } |
||
317 | |||
318 | static int open_variant(AppleHTTPContext *c, struct variant *var, int skip) |
||
319 | { |
||
320 | int ret;
|
||
321 | |||
322 | if (c->cur_seq_no < var->start_seq_no) {
|
||
323 | av_log(NULL, AV_LOG_WARNING,
|
||
324 | "seq %d not available in variant %s, skipping\n",
|
||
325 | var->start_seq_no, var->url); |
||
326 | return 0; |
||
327 | } |
||
328 | if (c->cur_seq_no - var->start_seq_no >= var->n_segments)
|
||
329 | return c->finished ? AVERROR_EOF : 0; |
||
330 | 22a3212e | Anton Khirnov | ret = avio_open(&var->pb, |
331 | cd223e0b | Martin Storsjö | var->segments[c->cur_seq_no - var->start_seq_no]->url, |
332 | URL_RDONLY); |
||
333 | if (ret < 0) |
||
334 | return ret;
|
||
335 | var->ctx->pb = var->pb; |
||
336 | /* If this is a new segment in parallel with another one already opened,
|
||
337 | * skip ahead so they're all at the same dts. */
|
||
338 | if (skip && c->last_packet_dts != AV_NOPTS_VALUE) {
|
||
339 | while (1) { |
||
340 | ret = av_read_frame(var->ctx, &var->pkt); |
||
341 | if (ret < 0) { |
||
342 | if (ret == AVERROR_EOF) {
|
||
343 | reset_packet(&var->pkt); |
||
344 | return 0; |
||
345 | } |
||
346 | return ret;
|
||
347 | } |
||
348 | if (var->pkt.dts >= c->last_packet_dts)
|
||
349 | break;
|
||
350 | av_free_packet(&var->pkt); |
||
351 | } |
||
352 | } |
||
353 | return 0; |
||
354 | } |
||
355 | |||
356 | static int applehttp_read_packet(AVFormatContext *s, AVPacket *pkt) |
||
357 | { |
||
358 | AppleHTTPContext *c = s->priv_data; |
||
359 | int ret, i, minvariant = -1, first = 1, needed = 0, changed = 0, |
||
360 | variants = 0;
|
||
361 | |||
362 | /* Recheck the discard flags - which streams are desired at the moment */
|
||
363 | for (i = 0; i < c->n_variants; i++) |
||
364 | c->variants[i]->needed = 0;
|
||
365 | for (i = 0; i < s->nb_streams; i++) { |
||
366 | AVStream *st = s->streams[i]; |
||
367 | struct variant *var = c->variants[s->streams[i]->id];
|
||
368 | if (st->discard < AVDISCARD_ALL) {
|
||
369 | var->needed = 1;
|
||
370 | needed++; |
||
371 | } |
||
372 | /* Copy the discard flag to the chained demuxer, to indicate which
|
||
373 | * streams are desired. */
|
||
374 | var->ctx->streams[i - var->stream_offset]->discard = st->discard; |
||
375 | } |
||
376 | if (!needed)
|
||
377 | return AVERROR_EOF;
|
||
378 | start:
|
||
379 | for (i = 0; i < c->n_variants; i++) { |
||
380 | struct variant *var = c->variants[i];
|
||
381 | /* Close unneeded streams, open newly requested streams */
|
||
382 | if (var->pb && !var->needed) {
|
||
383 | av_log(s, AV_LOG_DEBUG, |
||
384 | "Closing variant stream %d, no longer needed\n", i);
|
||
385 | av_free_packet(&var->pkt); |
||
386 | reset_packet(&var->pkt); |
||
387 | 22a3212e | Anton Khirnov | avio_close(var->pb); |
388 | cd223e0b | Martin Storsjö | var->pb = NULL;
|
389 | changed = 1;
|
||
390 | } else if (!var->pb && var->needed) { |
||
391 | if (first)
|
||
392 | av_log(s, AV_LOG_DEBUG, "Opening variant stream %d\n", i);
|
||
393 | if (first && !c->finished)
|
||
394 | if ((ret = parse_playlist(c, var->url, var, NULL)) < 0) |
||
395 | return ret;
|
||
396 | ret = open_variant(c, var, first); |
||
397 | if (ret < 0) |
||
398 | return ret;
|
||
399 | changed = 1;
|
||
400 | } |
||
401 | /* Count the number of open variants */
|
||
402 | if (var->pb)
|
||
403 | variants++; |
||
404 | /* Make sure we've got one buffered packet from each open variant
|
||
405 | * stream */
|
||
406 | if (var->pb && !var->pkt.data) {
|
||
407 | ret = av_read_frame(var->ctx, &var->pkt); |
||
408 | if (ret < 0) { |
||
409 | 66e5b1df | Anton Khirnov | if (!var->pb->eof_reached)
|
410 | cd223e0b | Martin Storsjö | return ret;
|
411 | reset_packet(&var->pkt); |
||
412 | } |
||
413 | } |
||
414 | /* Check if this stream has the packet with the lowest dts */
|
||
415 | if (var->pkt.data) {
|
||
416 | if (minvariant < 0 || |
||
417 | var->pkt.dts < c->variants[minvariant]->pkt.dts) |
||
418 | minvariant = i; |
||
419 | } |
||
420 | } |
||
421 | if (first && changed)
|
||
422 | av_log(s, AV_LOG_INFO, "Receiving %d variant streams\n", variants);
|
||
423 | /* If we got a packet, return it */
|
||
424 | if (minvariant >= 0) { |
||
425 | *pkt = c->variants[minvariant]->pkt; |
||
426 | pkt->stream_index += c->variants[minvariant]->stream_offset; |
||
427 | reset_packet(&c->variants[minvariant]->pkt); |
||
428 | c->last_packet_dts = pkt->dts; |
||
429 | return 0; |
||
430 | } |
||
431 | /* No more packets - eof reached in all variant streams, close the
|
||
432 | * current segments. */
|
||
433 | for (i = 0; i < c->n_variants; i++) { |
||
434 | struct variant *var = c->variants[i];
|
||
435 | if (var->pb) {
|
||
436 | 22a3212e | Anton Khirnov | avio_close(var->pb); |
437 | cd223e0b | Martin Storsjö | var->pb = NULL;
|
438 | } |
||
439 | } |
||
440 | /* Indicate that we're opening the next segment, not opening a new
|
||
441 | * variant stream in parallel, so we shouldn't try to skip ahead. */
|
||
442 | first = 0;
|
||
443 | c->cur_seq_no++; |
||
444 | reload:
|
||
445 | if (!c->finished) {
|
||
446 | /* If this is a live stream and target_duration has elapsed since
|
||
447 | * the last playlist reload, reload the variant playlists now. */
|
||
448 | int64_t now = av_gettime(); |
||
449 | if (now - c->last_load_time >= c->target_duration*1000000) { |
||
450 | c->max_start_seq = 0;
|
||
451 | c->min_end_seq = INT_MAX; |
||
452 | for (i = 0; i < c->n_variants; i++) { |
||
453 | struct variant *var = c->variants[i];
|
||
454 | if (var->needed) {
|
||
455 | if ((ret = parse_playlist(c, var->url, var, NULL)) < 0) |
||
456 | return ret;
|
||
457 | c->max_start_seq = FFMAX(c->max_start_seq, |
||
458 | var->start_seq_no); |
||
459 | c->min_end_seq = FFMIN(c->min_end_seq, |
||
460 | var->start_seq_no + var->n_segments); |
||
461 | } |
||
462 | } |
||
463 | } |
||
464 | } |
||
465 | if (c->cur_seq_no < c->max_start_seq) {
|
||
466 | av_log(NULL, AV_LOG_WARNING,
|
||
467 | "skipping %d segments ahead, expired from playlists\n",
|
||
468 | c->max_start_seq - c->cur_seq_no); |
||
469 | c->cur_seq_no = c->max_start_seq; |
||
470 | } |
||
471 | bc040cb3 | Martin Storsjö | /* If more segments exist, open the next one */
|
472 | cd223e0b | Martin Storsjö | if (c->cur_seq_no < c->min_end_seq)
|
473 | goto start;
|
||
474 | /* We've reached the end of the playlists - return eof if this is a
|
||
475 | * non-live stream, wait until the next playlist reload if it is live. */
|
||
476 | if (c->finished)
|
||
477 | return AVERROR_EOF;
|
||
478 | while (av_gettime() - c->last_load_time < c->target_duration*1000000) { |
||
479 | if (url_interrupt_cb())
|
||
480 | c76374c6 | Nicolas George | return AVERROR_EXIT;
|
481 | cd223e0b | Martin Storsjö | usleep(100*1000); |
482 | } |
||
483 | /* Enough time has elapsed since the last reload */
|
||
484 | goto reload;
|
||
485 | } |
||
486 | |||
487 | static int applehttp_close(AVFormatContext *s) |
||
488 | { |
||
489 | AppleHTTPContext *c = s->priv_data; |
||
490 | |||
491 | free_variant_list(c); |
||
492 | return 0; |
||
493 | } |
||
494 | |||
495 | static int applehttp_read_seek(AVFormatContext *s, int stream_index, |
||
496 | int64_t timestamp, int flags)
|
||
497 | { |
||
498 | AppleHTTPContext *c = s->priv_data; |
||
499 | dc8b73c0 | Martin Storsjö | int64_t pos = 0;
|
500 | int i;
|
||
501 | cd223e0b | Martin Storsjö | struct variant *var = c->variants[0]; |
502 | |||
503 | if ((flags & AVSEEK_FLAG_BYTE) || !c->finished)
|
||
504 | return AVERROR(ENOSYS);
|
||
505 | |||
506 | /* Reset the variants */
|
||
507 | c->last_packet_dts = AV_NOPTS_VALUE; |
||
508 | for (i = 0; i < c->n_variants; i++) { |
||
509 | struct variant *var = c->variants[i];
|
||
510 | if (var->pb) {
|
||
511 | 22a3212e | Anton Khirnov | avio_close(var->pb); |
512 | cd223e0b | Martin Storsjö | var->pb = NULL;
|
513 | } |
||
514 | av_free_packet(&var->pkt); |
||
515 | reset_packet(&var->pkt); |
||
516 | } |
||
517 | |||
518 | timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ? |
||
519 | s->streams[stream_index]->time_base.den : |
||
520 | AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? |
||
521 | AV_ROUND_DOWN : AV_ROUND_UP); |
||
522 | /* Locate the segment that contains the target timestamp */
|
||
523 | for (i = 0; i < var->n_segments; i++) { |
||
524 | if (timestamp >= pos && timestamp < pos + var->segments[i]->duration) {
|
||
525 | c->cur_seq_no = var->start_seq_no + i; |
||
526 | return 0; |
||
527 | } |
||
528 | pos += var->segments[i]->duration; |
||
529 | } |
||
530 | return AVERROR(EIO);
|
||
531 | } |
||
532 | |||
533 | static int applehttp_probe(AVProbeData *p) |
||
534 | { |
||
535 | /* Require #EXTM3U at the start, and either one of the ones below
|
||
536 | * somewhere for a proper match. */
|
||
537 | if (strncmp(p->buf, "#EXTM3U", 7)) |
||
538 | return 0; |
||
539 | if (strstr(p->buf, "#EXT-X-STREAM-INF:") || |
||
540 | strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
|
||
541 | strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:"))
|
||
542 | return AVPROBE_SCORE_MAX;
|
||
543 | return 0; |
||
544 | } |
||
545 | |||
546 | c6610a21 | Diego Elio Pettenò | AVInputFormat ff_applehttp_demuxer = { |
547 | cd223e0b | Martin Storsjö | "applehttp",
|
548 | NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"),
|
||
549 | sizeof(AppleHTTPContext),
|
||
550 | applehttp_probe, |
||
551 | applehttp_read_header, |
||
552 | applehttp_read_packet, |
||
553 | applehttp_close, |
||
554 | applehttp_read_seek, |
||
555 | }; |