| OLD | NEW |
| (Empty) | |
| 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 |
| 28 #define _XOPEN_SOURCE 600 |
| 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 }; |
| OLD | NEW |