Mercurial > libavformat.hg
annotate applehttp.c @ 6461:c12416642843 libavformat
matroskadec: allow uint and float elements with length = 0
| author | aurel |
|---|---|
| date | Sun, 05 Sep 2010 21:37:40 +0000 |
| parents | 98bbeb6188e5 |
| children |
| rev | line source |
|---|---|
| 6391 | 1 /* |
| 2 * Apple HTTP Live Streaming demuxer | |
| 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 demuxer | |
| 25 * http://tools.ietf.org/html/draft-pantos-http-live-streaming | |
| 26 */ | |
| 27 | |
|
6393
98bbeb6188e5
applehttp: define _XOPEN_SOURCE=600 as required for usleep()
mru
parents:
6391
diff
changeset
|
28 #define _XOPEN_SOURCE 600 |
| 6391 | 29 #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 * it has an open ByteIOContext too, and potentially an AVPacket | |
| 54 * containing the next packet from this stream. | |
| 55 */ | |
| 56 struct variant { | |
| 57 int bandwidth; | |
| 58 char url[MAX_URL_SIZE]; | |
| 59 ByteIOContext *pb; | |
| 60 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 static int read_chomp_line(ByteIOContext *s, char *buf, int maxlen) | |
| 82 { | |
| 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 make_absolute_url(char *buf, int size, const char *base, | |
| 90 const char *rel) | |
| 91 { | |
| 92 char *sep; | |
| 93 if (!base || strstr(rel, "://")) { | |
| 94 av_strlcpy(buf, rel, size); | |
| 95 return; | |
| 96 } | |
| 97 if (base != buf) | |
| 98 av_strlcpy(buf, base, size); | |
| 99 sep = strrchr(buf, '/'); | |
| 100 if (sep) | |
| 101 sep[1] = '\0'; | |
| 102 while (av_strstart(rel, "../", NULL)) { | |
| 103 if (sep) { | |
| 104 sep[0] = '\0'; | |
| 105 sep = strrchr(buf, '/'); | |
| 106 if (sep) | |
| 107 sep[1] = '\0'; | |
| 108 } | |
| 109 rel += 3; | |
| 110 } | |
| 111 av_strlcat(buf, rel, size); | |
| 112 } | |
| 113 | |
| 114 static void free_segment_list(struct variant *var) | |
| 115 { | |
| 116 int i; | |
| 117 for (i = 0; i < var->n_segments; i++) | |
| 118 av_free(var->segments[i]); | |
| 119 av_freep(&var->segments); | |
| 120 var->n_segments = 0; | |
| 121 } | |
| 122 | |
| 123 static void free_variant_list(AppleHTTPContext *c) | |
| 124 { | |
| 125 int i; | |
| 126 for (i = 0; i < c->n_variants; i++) { | |
| 127 struct variant *var = c->variants[i]; | |
| 128 free_segment_list(var); | |
| 129 av_free_packet(&var->pkt); | |
| 130 if (var->pb) | |
| 131 url_fclose(var->pb); | |
| 132 if (var->ctx) { | |
| 133 var->ctx->pb = NULL; | |
| 134 av_close_input_file(var->ctx); | |
| 135 } | |
| 136 av_free(var); | |
| 137 } | |
| 138 av_freep(&c->variants); | |
| 139 c->n_variants = 0; | |
| 140 } | |
| 141 | |
| 142 /* | |
| 143 * Used to reset a statically allocated AVPacket to a clean slate, | |
| 144 * containing no data. | |
| 145 */ | |
| 146 static void reset_packet(AVPacket *pkt) | |
| 147 { | |
| 148 av_init_packet(pkt); | |
| 149 pkt->data = NULL; | |
| 150 } | |
| 151 | |
| 152 static struct variant *new_variant(AppleHTTPContext *c, int bandwidth, | |
| 153 const char *url, const char *base) | |
| 154 { | |
| 155 struct variant *var = av_mallocz(sizeof(struct variant)); | |
| 156 if (!var) | |
| 157 return NULL; | |
| 158 reset_packet(&var->pkt); | |
| 159 var->bandwidth = bandwidth; | |
| 160 make_absolute_url(var->url, sizeof(var->url), base, url); | |
| 161 dynarray_add(&c->variants, &c->n_variants, var); | |
| 162 return var; | |
| 163 } | |
| 164 | |
| 165 struct variant_info { | |
| 166 char bandwidth[20]; | |
| 167 }; | |
| 168 | |
| 169 static void handle_variant_args(struct variant_info *info, const char *key, | |
| 170 int key_len, char **dest, int *dest_len) | |
| 171 { | |
| 172 if (strncmp(key, "BANDWIDTH", key_len)) { | |
| 173 *dest = info->bandwidth; | |
| 174 *dest_len = sizeof(info->bandwidth); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 static int parse_playlist(AppleHTTPContext *c, const char *url, | |
| 179 struct variant *var, ByteIOContext *in) | |
| 180 { | |
| 181 int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0; | |
| 182 char line[1024]; | |
| 183 const char *ptr; | |
| 184 int close_in = 0; | |
| 185 | |
| 186 if (!in) { | |
| 187 close_in = 1; | |
| 188 if ((ret = url_fopen(&in, url, URL_RDONLY)) < 0) | |
| 189 return ret; | |
| 190 } | |
| 191 | |
| 192 read_chomp_line(in, line, sizeof(line)); | |
| 193 if (strcmp(line, "#EXTM3U")) { | |
| 194 ret = AVERROR_INVALIDDATA; | |
| 195 goto fail; | |
| 196 } | |
| 197 | |
| 198 if (var) | |
| 199 free_segment_list(var); | |
| 200 c->finished = 0; | |
| 201 while (!url_feof(in)) { | |
| 202 read_chomp_line(in, line, sizeof(line)); | |
| 203 if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { | |
| 204 struct variant_info info = {{0}}; | |
| 205 is_variant = 1; | |
| 206 ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args, | |
| 207 &info); | |
| 208 bandwidth = atoi(info.bandwidth); | |
| 209 } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) { | |
| 210 c->target_duration = atoi(ptr); | |
| 211 } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) { | |
| 212 if (!var) { | |
| 213 var = new_variant(c, 0, url, NULL); | |
| 214 if (!var) { | |
| 215 ret = AVERROR(ENOMEM); | |
| 216 goto fail; | |
| 217 } | |
| 218 } | |
| 219 var->start_seq_no = atoi(ptr); | |
| 220 } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { | |
| 221 c->finished = 1; | |
| 222 } else if (av_strstart(line, "#EXTINF:", &ptr)) { | |
| 223 is_segment = 1; | |
| 224 duration = atoi(ptr); | |
| 225 } else if (av_strstart(line, "#", NULL)) { | |
| 226 continue; | |
| 227 } else if (line[0]) { | |
| 228 if (is_variant) { | |
| 229 if (!new_variant(c, bandwidth, line, url)) { | |
| 230 ret = AVERROR(ENOMEM); | |
| 231 goto fail; | |
| 232 } | |
| 233 is_variant = 0; | |
| 234 bandwidth = 0; | |
| 235 } | |
| 236 if (is_segment) { | |
| 237 struct segment *seg; | |
| 238 if (!var) { | |
| 239 var = new_variant(c, 0, url, NULL); | |
| 240 if (!var) { | |
| 241 ret = AVERROR(ENOMEM); | |
| 242 goto fail; | |
| 243 } | |
| 244 } | |
| 245 seg = av_malloc(sizeof(struct segment)); | |
| 246 if (!seg) { | |
| 247 ret = AVERROR(ENOMEM); | |
| 248 goto fail; | |
| 249 } | |
| 250 seg->duration = duration; | |
| 251 make_absolute_url(seg->url, sizeof(seg->url), url, line); | |
| 252 dynarray_add(&var->segments, &var->n_segments, seg); | |
| 253 is_segment = 0; | |
| 254 } | |
| 255 } | |
| 256 } | |
| 257 c->last_load_time = av_gettime(); | |
| 258 | |
| 259 fail: | |
| 260 if (close_in) | |
| 261 url_fclose(in); | |
| 262 return ret; | |
| 263 } | |
| 264 | |
| 265 static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
| 266 { | |
| 267 AppleHTTPContext *c = s->priv_data; | |
| 268 int ret = 0, i, j, stream_offset = 0; | |
| 269 | |
| 270 if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0) | |
| 271 goto fail; | |
| 272 | |
| 273 if (c->n_variants == 0) { | |
| 274 av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); | |
| 275 ret = AVERROR_EOF; | |
| 276 goto fail; | |
| 277 } | |
| 278 /* If the playlist only contained variants, parse each individual | |
| 279 * variant playlist. */ | |
| 280 if (c->n_variants > 1 || c->variants[0]->n_segments == 0) { | |
| 281 for (i = 0; i < c->n_variants; i++) { | |
| 282 struct variant *v = c->variants[i]; | |
| 283 if ((ret = parse_playlist(c, v->url, v, NULL)) < 0) | |
| 284 goto fail; | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 if (c->variants[0]->n_segments == 0) { | |
| 289 av_log(NULL, AV_LOG_WARNING, "Empty playlist\n"); | |
| 290 ret = AVERROR_EOF; | |
| 291 goto fail; | |
| 292 } | |
| 293 | |
| 294 /* If this isn't a live stream, calculate the total duration of the | |
| 295 * stream. */ | |
| 296 if (c->finished) { | |
| 297 int duration = 0; | |
| 298 for (i = 0; i < c->variants[0]->n_segments; i++) | |
| 299 duration += c->variants[0]->segments[i]->duration; | |
| 300 s->duration = duration * AV_TIME_BASE; | |
| 301 } | |
| 302 | |
| 303 c->min_end_seq = INT_MAX; | |
| 304 /* Open the demuxer for each variant */ | |
| 305 for (i = 0; i < c->n_variants; i++) { | |
| 306 struct variant *v = c->variants[i]; | |
| 307 if (v->n_segments == 0) | |
| 308 continue; | |
| 309 c->max_start_seq = FFMAX(c->max_start_seq, v->start_seq_no); | |
| 310 c->min_end_seq = FFMIN(c->min_end_seq, v->start_seq_no + | |
| 311 v->n_segments); | |
| 312 ret = av_open_input_file(&v->ctx, v->segments[0]->url, NULL, 0, NULL); | |
| 313 if (ret < 0) | |
| 314 goto fail; | |
| 315 url_fclose(v->ctx->pb); | |
| 316 v->ctx->pb = NULL; | |
| 317 v->stream_offset = stream_offset; | |
| 318 /* Create new AVStreams for each stream in this variant */ | |
| 319 for (j = 0; j < v->ctx->nb_streams; j++) { | |
| 320 AVStream *st = av_new_stream(s, i); | |
| 321 if (!st) { | |
| 322 ret = AVERROR(ENOMEM); | |
| 323 goto fail; | |
| 324 } | |
| 325 avcodec_copy_context(st->codec, v->ctx->streams[j]->codec); | |
| 326 } | |
| 327 stream_offset += v->ctx->nb_streams; | |
| 328 } | |
| 329 c->last_packet_dts = AV_NOPTS_VALUE; | |
| 330 | |
| 331 c->cur_seq_no = c->max_start_seq; | |
| 332 /* If this is a live stream with more than 3 segments, start at the | |
| 333 * third last segment. */ | |
| 334 if (!c->finished && c->min_end_seq - c->max_start_seq > 3) | |
| 335 c->cur_seq_no = c->min_end_seq - 2; | |
| 336 | |
| 337 return 0; | |
| 338 fail: | |
| 339 free_variant_list(c); | |
| 340 return ret; | |
| 341 } | |
| 342 | |
| 343 static int open_variant(AppleHTTPContext *c, struct variant *var, int skip) | |
| 344 { | |
| 345 int ret; | |
| 346 | |
| 347 if (c->cur_seq_no < var->start_seq_no) { | |
| 348 av_log(NULL, AV_LOG_WARNING, | |
| 349 "seq %d not available in variant %s, skipping\n", | |
| 350 var->start_seq_no, var->url); | |
| 351 return 0; | |
| 352 } | |
| 353 if (c->cur_seq_no - var->start_seq_no >= var->n_segments) | |
| 354 return c->finished ? AVERROR_EOF : 0; | |
| 355 ret = url_fopen(&var->pb, | |
| 356 var->segments[c->cur_seq_no - var->start_seq_no]->url, | |
| 357 URL_RDONLY); | |
| 358 if (ret < 0) | |
| 359 return ret; | |
| 360 var->ctx->pb = var->pb; | |
| 361 /* If this is a new segment in parallel with another one already opened, | |
| 362 * skip ahead so they're all at the same dts. */ | |
| 363 if (skip && c->last_packet_dts != AV_NOPTS_VALUE) { | |
| 364 while (1) { | |
| 365 ret = av_read_frame(var->ctx, &var->pkt); | |
| 366 if (ret < 0) { | |
| 367 if (ret == AVERROR_EOF) { | |
| 368 reset_packet(&var->pkt); | |
| 369 return 0; | |
| 370 } | |
| 371 return ret; | |
| 372 } | |
| 373 if (var->pkt.dts >= c->last_packet_dts) | |
| 374 break; | |
| 375 av_free_packet(&var->pkt); | |
| 376 } | |
| 377 } | |
| 378 return 0; | |
| 379 } | |
| 380 | |
| 381 static int applehttp_read_packet(AVFormatContext *s, AVPacket *pkt) | |
| 382 { | |
| 383 AppleHTTPContext *c = s->priv_data; | |
| 384 int ret, i, minvariant = -1, first = 1, needed = 0, changed = 0, | |
| 385 variants = 0; | |
| 386 | |
| 387 /* Recheck the discard flags - which streams are desired at the moment */ | |
| 388 for (i = 0; i < c->n_variants; i++) | |
| 389 c->variants[i]->needed = 0; | |
| 390 for (i = 0; i < s->nb_streams; i++) { | |
| 391 AVStream *st = s->streams[i]; | |
| 392 struct variant *var = c->variants[s->streams[i]->id]; | |
| 393 if (st->discard < AVDISCARD_ALL) { | |
| 394 var->needed = 1; | |
| 395 needed++; | |
| 396 } | |
| 397 /* Copy the discard flag to the chained demuxer, to indicate which | |
| 398 * streams are desired. */ | |
| 399 var->ctx->streams[i - var->stream_offset]->discard = st->discard; | |
| 400 } | |
| 401 if (!needed) | |
| 402 return AVERROR_EOF; | |
| 403 start: | |
| 404 for (i = 0; i < c->n_variants; i++) { | |
| 405 struct variant *var = c->variants[i]; | |
| 406 /* Close unneeded streams, open newly requested streams */ | |
| 407 if (var->pb && !var->needed) { | |
| 408 av_log(s, AV_LOG_DEBUG, | |
| 409 "Closing variant stream %d, no longer needed\n", i); | |
| 410 av_free_packet(&var->pkt); | |
| 411 reset_packet(&var->pkt); | |
| 412 url_fclose(var->pb); | |
| 413 var->pb = NULL; | |
| 414 changed = 1; | |
| 415 } else if (!var->pb && var->needed) { | |
| 416 if (first) | |
| 417 av_log(s, AV_LOG_DEBUG, "Opening variant stream %d\n", i); | |
| 418 if (first && !c->finished) | |
| 419 if ((ret = parse_playlist(c, var->url, var, NULL)) < 0) | |
| 420 return ret; | |
| 421 ret = open_variant(c, var, first); | |
| 422 if (ret < 0) | |
| 423 return ret; | |
| 424 changed = 1; | |
| 425 } | |
| 426 /* Count the number of open variants */ | |
| 427 if (var->pb) | |
| 428 variants++; | |
| 429 /* Make sure we've got one buffered packet from each open variant | |
| 430 * stream */ | |
| 431 if (var->pb && !var->pkt.data) { | |
| 432 ret = av_read_frame(var->ctx, &var->pkt); | |
| 433 if (ret < 0) { | |
| 434 if (!url_feof(var->pb)) | |
| 435 return ret; | |
| 436 reset_packet(&var->pkt); | |
| 437 } | |
| 438 } | |
| 439 /* Check if this stream has the packet with the lowest dts */ | |
| 440 if (var->pkt.data) { | |
| 441 if (minvariant < 0 || | |
| 442 var->pkt.dts < c->variants[minvariant]->pkt.dts) | |
| 443 minvariant = i; | |
| 444 } | |
| 445 } | |
| 446 if (first && changed) | |
| 447 av_log(s, AV_LOG_INFO, "Receiving %d variant streams\n", variants); | |
| 448 /* If we got a packet, return it */ | |
| 449 if (minvariant >= 0) { | |
| 450 *pkt = c->variants[minvariant]->pkt; | |
| 451 pkt->stream_index += c->variants[minvariant]->stream_offset; | |
| 452 reset_packet(&c->variants[minvariant]->pkt); | |
| 453 c->last_packet_dts = pkt->dts; | |
| 454 return 0; | |
| 455 } | |
| 456 /* No more packets - eof reached in all variant streams, close the | |
| 457 * current segments. */ | |
| 458 for (i = 0; i < c->n_variants; i++) { | |
| 459 struct variant *var = c->variants[i]; | |
| 460 if (var->pb) { | |
| 461 url_fclose(var->pb); | |
| 462 var->pb = NULL; | |
| 463 } | |
| 464 } | |
| 465 /* Indicate that we're opening the next segment, not opening a new | |
| 466 * variant stream in parallel, so we shouldn't try to skip ahead. */ | |
| 467 first = 0; | |
| 468 c->cur_seq_no++; | |
| 469 reload: | |
| 470 if (!c->finished) { | |
| 471 /* If this is a live stream and target_duration has elapsed since | |
| 472 * the last playlist reload, reload the variant playlists now. */ | |
| 473 int64_t now = av_gettime(); | |
| 474 if (now - c->last_load_time >= c->target_duration*1000000) { | |
| 475 c->max_start_seq = 0; | |
| 476 c->min_end_seq = INT_MAX; | |
| 477 for (i = 0; i < c->n_variants; i++) { | |
| 478 struct variant *var = c->variants[i]; | |
| 479 if (var->needed) { | |
| 480 if ((ret = parse_playlist(c, var->url, var, NULL)) < 0) | |
| 481 return ret; | |
| 482 c->max_start_seq = FFMAX(c->max_start_seq, | |
| 483 var->start_seq_no); | |
| 484 c->min_end_seq = FFMIN(c->min_end_seq, | |
| 485 var->start_seq_no + var->n_segments); | |
| 486 } | |
| 487 } | |
| 488 } | |
| 489 } | |
| 490 if (c->cur_seq_no < c->max_start_seq) { | |
| 491 av_log(NULL, AV_LOG_WARNING, | |
| 492 "skipping %d segments ahead, expired from playlists\n", | |
| 493 c->max_start_seq - c->cur_seq_no); | |
| 494 c->cur_seq_no = c->max_start_seq; | |
| 495 } | |
| 496 /* If more segments exit, open the next one */ | |
| 497 if (c->cur_seq_no < c->min_end_seq) | |
| 498 goto start; | |
| 499 /* We've reached the end of the playlists - return eof if this is a | |
| 500 * non-live stream, wait until the next playlist reload if it is live. */ | |
| 501 if (c->finished) | |
| 502 return AVERROR_EOF; | |
| 503 while (av_gettime() - c->last_load_time < c->target_duration*1000000) { | |
| 504 if (url_interrupt_cb()) | |
| 505 return AVERROR(EINTR); | |
| 506 usleep(100*1000); | |
| 507 } | |
| 508 /* Enough time has elapsed since the last reload */ | |
| 509 goto reload; | |
| 510 } | |
| 511 | |
| 512 static int applehttp_close(AVFormatContext *s) | |
| 513 { | |
| 514 AppleHTTPContext *c = s->priv_data; | |
| 515 | |
| 516 free_variant_list(c); | |
| 517 return 0; | |
| 518 } | |
| 519 | |
| 520 static int applehttp_read_seek(AVFormatContext *s, int stream_index, | |
| 521 int64_t timestamp, int flags) | |
| 522 { | |
| 523 AppleHTTPContext *c = s->priv_data; | |
| 524 int pos = 0, i; | |
| 525 struct variant *var = c->variants[0]; | |
| 526 | |
| 527 if ((flags & AVSEEK_FLAG_BYTE) || !c->finished) | |
| 528 return AVERROR(ENOSYS); | |
| 529 | |
| 530 /* Reset the variants */ | |
| 531 c->last_packet_dts = AV_NOPTS_VALUE; | |
| 532 for (i = 0; i < c->n_variants; i++) { | |
| 533 struct variant *var = c->variants[i]; | |
| 534 if (var->pb) { | |
| 535 url_fclose(var->pb); | |
| 536 var->pb = NULL; | |
| 537 } | |
| 538 av_free_packet(&var->pkt); | |
| 539 reset_packet(&var->pkt); | |
| 540 } | |
| 541 | |
| 542 timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ? | |
| 543 s->streams[stream_index]->time_base.den : | |
| 544 AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ? | |
| 545 AV_ROUND_DOWN : AV_ROUND_UP); | |
| 546 /* Locate the segment that contains the target timestamp */ | |
| 547 for (i = 0; i < var->n_segments; i++) { | |
| 548 if (timestamp >= pos && timestamp < pos + var->segments[i]->duration) { | |
| 549 c->cur_seq_no = var->start_seq_no + i; | |
| 550 return 0; | |
| 551 } | |
| 552 pos += var->segments[i]->duration; | |
| 553 } | |
| 554 return AVERROR(EIO); | |
| 555 } | |
| 556 | |
| 557 static int applehttp_probe(AVProbeData *p) | |
| 558 { | |
| 559 /* Require #EXTM3U at the start, and either one of the ones below | |
| 560 * somewhere for a proper match. */ | |
| 561 if (strncmp(p->buf, "#EXTM3U", 7)) | |
| 562 return 0; | |
| 563 if (strstr(p->buf, "#EXT-X-STREAM-INF:") || | |
| 564 strstr(p->buf, "#EXT-X-TARGETDURATION:") || | |
| 565 strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) | |
| 566 return AVPROBE_SCORE_MAX; | |
| 567 return 0; | |
| 568 } | |
| 569 | |
| 570 AVInputFormat applehttp_demuxer = { | |
| 571 "applehttp", | |
| 572 NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"), | |
| 573 sizeof(AppleHTTPContext), | |
| 574 applehttp_probe, | |
| 575 applehttp_read_header, | |
| 576 applehttp_read_packet, | |
| 577 applehttp_close, | |
| 578 applehttp_read_seek, | |
| 579 }; |
