00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00028 #define _XOPEN_SOURCE 600
00029 #include "libavutil/avstring.h"
00030 #include "avformat.h"
00031 #include "internal.h"
00032 #include <unistd.h>
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 struct segment {
00047 int duration;
00048 char url[MAX_URL_SIZE];
00049 };
00050
00051
00052
00053
00054
00055
00056 struct variant {
00057 int bandwidth;
00058 char url[MAX_URL_SIZE];
00059 AVIOContext *pb;
00060 AVFormatContext *ctx;
00061 AVPacket pkt;
00062 int stream_offset;
00063
00064 int start_seq_no;
00065 int n_segments;
00066 struct segment **segments;
00067 int needed;
00068 };
00069
00070 typedef struct AppleHTTPContext {
00071 int target_duration;
00072 int finished;
00073 int n_variants;
00074 struct variant **variants;
00075 int cur_seq_no;
00076 int64_t last_load_time;
00077 int64_t last_packet_dts;
00078 int max_start_seq, min_end_seq;
00079 } AppleHTTPContext;
00080
00081 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
00082 {
00083 int len = ff_get_line(s, buf, maxlen);
00084 while (len > 0 && isspace(buf[len - 1]))
00085 buf[--len] = '\0';
00086 return len;
00087 }
00088
00089 static void make_absolute_url(char *buf, int size, const char *base,
00090 const char *rel)
00091 {
00092 char *sep;
00093
00094 if (base && strstr(base, "://") && rel[0] == '/') {
00095 if (base != buf)
00096 av_strlcpy(buf, base, size);
00097 sep = strstr(buf, "://");
00098 if (sep) {
00099 sep += 3;
00100 sep = strchr(sep, '/');
00101 if (sep)
00102 *sep = '\0';
00103 }
00104 av_strlcat(buf, rel, size);
00105 return;
00106 }
00107
00108 if (!base || strstr(rel, "://") || rel[0] == '/') {
00109 av_strlcpy(buf, rel, size);
00110 return;
00111 }
00112 if (base != buf)
00113 av_strlcpy(buf, base, size);
00114
00115 sep = strrchr(buf, '/');
00116 if (sep)
00117 sep[1] = '\0';
00118 else
00119 buf[0] = '\0';
00120 while (av_strstart(rel, "../", NULL) && sep) {
00121
00122 sep[0] = '\0';
00123 sep = strrchr(buf, '/');
00124
00125 if (!strcmp(sep ? &sep[1] : buf, "..")) {
00126
00127 av_strlcat(buf, "/", size);
00128 break;
00129 }
00130
00131 if (sep)
00132 sep[1] = '\0';
00133 else
00134 buf[0] = '\0';
00135 rel += 3;
00136 }
00137 av_strlcat(buf, rel, size);
00138 }
00139
00140 static void free_segment_list(struct variant *var)
00141 {
00142 int i;
00143 for (i = 0; i < var->n_segments; i++)
00144 av_free(var->segments[i]);
00145 av_freep(&var->segments);
00146 var->n_segments = 0;
00147 }
00148
00149 static void free_variant_list(AppleHTTPContext *c)
00150 {
00151 int i;
00152 for (i = 0; i < c->n_variants; i++) {
00153 struct variant *var = c->variants[i];
00154 free_segment_list(var);
00155 av_free_packet(&var->pkt);
00156 if (var->pb)
00157 avio_close(var->pb);
00158 if (var->ctx) {
00159 var->ctx->pb = NULL;
00160 av_close_input_file(var->ctx);
00161 }
00162 av_free(var);
00163 }
00164 av_freep(&c->variants);
00165 c->n_variants = 0;
00166 }
00167
00168
00169
00170
00171
00172 static void reset_packet(AVPacket *pkt)
00173 {
00174 av_init_packet(pkt);
00175 pkt->data = NULL;
00176 }
00177
00178 static struct variant *new_variant(AppleHTTPContext *c, int bandwidth,
00179 const char *url, const char *base)
00180 {
00181 struct variant *var = av_mallocz(sizeof(struct variant));
00182 if (!var)
00183 return NULL;
00184 reset_packet(&var->pkt);
00185 var->bandwidth = bandwidth;
00186 make_absolute_url(var->url, sizeof(var->url), base, url);
00187 dynarray_add(&c->variants, &c->n_variants, var);
00188 return var;
00189 }
00190
00191 struct variant_info {
00192 char bandwidth[20];
00193 };
00194
00195 static void handle_variant_args(struct variant_info *info, const char *key,
00196 int key_len, char **dest, int *dest_len)
00197 {
00198 if (!strncmp(key, "BANDWIDTH=", key_len)) {
00199 *dest = info->bandwidth;
00200 *dest_len = sizeof(info->bandwidth);
00201 }
00202 }
00203
00204 static int parse_playlist(AppleHTTPContext *c, const char *url,
00205 struct variant *var, AVIOContext *in)
00206 {
00207 int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
00208 char line[1024];
00209 const char *ptr;
00210 int close_in = 0;
00211
00212 if (!in) {
00213 close_in = 1;
00214 if ((ret = avio_open(&in, url, URL_RDONLY)) < 0)
00215 return ret;
00216 }
00217
00218 read_chomp_line(in, line, sizeof(line));
00219 if (strcmp(line, "#EXTM3U")) {
00220 ret = AVERROR_INVALIDDATA;
00221 goto fail;
00222 }
00223
00224 if (var)
00225 free_segment_list(var);
00226 c->finished = 0;
00227 while (!url_feof(in)) {
00228 read_chomp_line(in, line, sizeof(line));
00229 if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
00230 struct variant_info info = {{0}};
00231 is_variant = 1;
00232 ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
00233 &info);
00234 bandwidth = atoi(info.bandwidth);
00235 } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
00236 c->target_duration = atoi(ptr);
00237 } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
00238 if (!var) {
00239 var = new_variant(c, 0, url, NULL);
00240 if (!var) {
00241 ret = AVERROR(ENOMEM);
00242 goto fail;
00243 }
00244 }
00245 var->start_seq_no = atoi(ptr);
00246 } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
00247 c->finished = 1;
00248 } else if (av_strstart(line, "#EXTINF:", &ptr)) {
00249 is_segment = 1;
00250 duration = atoi(ptr);
00251 } else if (av_strstart(line, "#", NULL)) {
00252 continue;
00253 } else if (line[0]) {
00254 if (is_variant) {
00255 if (!new_variant(c, bandwidth, line, url)) {
00256 ret = AVERROR(ENOMEM);
00257 goto fail;
00258 }
00259 is_variant = 0;
00260 bandwidth = 0;
00261 }
00262 if (is_segment) {
00263 struct segment *seg;
00264 if (!var) {
00265 var = new_variant(c, 0, url, NULL);
00266 if (!var) {
00267 ret = AVERROR(ENOMEM);
00268 goto fail;
00269 }
00270 }
00271 seg = av_malloc(sizeof(struct segment));
00272 if (!seg) {
00273 ret = AVERROR(ENOMEM);
00274 goto fail;
00275 }
00276 seg->duration = duration;
00277 make_absolute_url(seg->url, sizeof(seg->url), url, line);
00278 dynarray_add(&var->segments, &var->n_segments, seg);
00279 is_segment = 0;
00280 }
00281 }
00282 }
00283 c->last_load_time = av_gettime();
00284
00285 fail:
00286 if (close_in)
00287 avio_close(in);
00288 return ret;
00289 }
00290
00291 static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
00292 {
00293 AppleHTTPContext *c = s->priv_data;
00294 int ret = 0, i, j, stream_offset = 0;
00295
00296 if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
00297 goto fail;
00298
00299 if (c->n_variants == 0) {
00300 av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
00301 ret = AVERROR_EOF;
00302 goto fail;
00303 }
00304
00305
00306 if (c->n_variants > 1 || c->variants[0]->n_segments == 0) {
00307 for (i = 0; i < c->n_variants; i++) {
00308 struct variant *v = c->variants[i];
00309 if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
00310 goto fail;
00311 }
00312 }
00313
00314 if (c->variants[0]->n_segments == 0) {
00315 av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
00316 ret = AVERROR_EOF;
00317 goto fail;
00318 }
00319
00320
00321
00322 if (c->finished) {
00323 int64_t duration = 0;
00324 for (i = 0; i < c->variants[0]->n_segments; i++)
00325 duration += c->variants[0]->segments[i]->duration;
00326 s->duration = duration * AV_TIME_BASE;
00327 }
00328
00329 c->min_end_seq = INT_MAX;
00330
00331 for (i = 0; i < c->n_variants; i++) {
00332 struct variant *v = c->variants[i];
00333 if (v->n_segments == 0)
00334 continue;
00335 c->max_start_seq = FFMAX(c->max_start_seq, v->start_seq_no);
00336 c->min_end_seq = FFMIN(c->min_end_seq, v->start_seq_no +
00337 v->n_segments);
00338 ret = av_open_input_file(&v->ctx, v->segments[0]->url, NULL, 0, NULL);
00339 if (ret < 0)
00340 goto fail;
00341 avio_close(v->ctx->pb);
00342 v->ctx->pb = NULL;
00343 v->stream_offset = stream_offset;
00344
00345 for (j = 0; j < v->ctx->nb_streams; j++) {
00346 AVStream *st = av_new_stream(s, i);
00347 if (!st) {
00348 ret = AVERROR(ENOMEM);
00349 goto fail;
00350 }
00351 avcodec_copy_context(st->codec, v->ctx->streams[j]->codec);
00352 }
00353 stream_offset += v->ctx->nb_streams;
00354 }
00355 c->last_packet_dts = AV_NOPTS_VALUE;
00356
00357 c->cur_seq_no = c->max_start_seq;
00358
00359
00360 if (!c->finished && c->min_end_seq - c->max_start_seq > 3)
00361 c->cur_seq_no = c->min_end_seq - 2;
00362
00363 return 0;
00364 fail:
00365 free_variant_list(c);
00366 return ret;
00367 }
00368
00369 static int open_variant(AppleHTTPContext *c, struct variant *var, int skip)
00370 {
00371 int ret;
00372
00373 if (c->cur_seq_no < var->start_seq_no) {
00374 av_log(NULL, AV_LOG_WARNING,
00375 "seq %d not available in variant %s, skipping\n",
00376 var->start_seq_no, var->url);
00377 return 0;
00378 }
00379 if (c->cur_seq_no - var->start_seq_no >= var->n_segments)
00380 return c->finished ? AVERROR_EOF : 0;
00381 ret = avio_open(&var->pb,
00382 var->segments[c->cur_seq_no - var->start_seq_no]->url,
00383 URL_RDONLY);
00384 if (ret < 0)
00385 return ret;
00386 var->ctx->pb = var->pb;
00387
00388
00389 if (skip && c->last_packet_dts != AV_NOPTS_VALUE) {
00390 while (1) {
00391 ret = av_read_frame(var->ctx, &var->pkt);
00392 if (ret < 0) {
00393 if (ret == AVERROR_EOF) {
00394 reset_packet(&var->pkt);
00395 return 0;
00396 }
00397 return ret;
00398 }
00399 if (var->pkt.dts >= c->last_packet_dts)
00400 break;
00401 av_free_packet(&var->pkt);
00402 }
00403 }
00404 return 0;
00405 }
00406
00407 static int applehttp_read_packet(AVFormatContext *s, AVPacket *pkt)
00408 {
00409 AppleHTTPContext *c = s->priv_data;
00410 int ret, i, minvariant = -1, first = 1, needed = 0, changed = 0,
00411 variants = 0;
00412
00413
00414 for (i = 0; i < c->n_variants; i++)
00415 c->variants[i]->needed = 0;
00416 for (i = 0; i < s->nb_streams; i++) {
00417 AVStream *st = s->streams[i];
00418 struct variant *var = c->variants[s->streams[i]->id];
00419 if (st->discard < AVDISCARD_ALL) {
00420 var->needed = 1;
00421 needed++;
00422 }
00423
00424
00425 var->ctx->streams[i - var->stream_offset]->discard = st->discard;
00426 }
00427 if (!needed)
00428 return AVERROR_EOF;
00429 start:
00430 for (i = 0; i < c->n_variants; i++) {
00431 struct variant *var = c->variants[i];
00432
00433 if (var->pb && !var->needed) {
00434 av_log(s, AV_LOG_DEBUG,
00435 "Closing variant stream %d, no longer needed\n", i);
00436 av_free_packet(&var->pkt);
00437 reset_packet(&var->pkt);
00438 avio_close(var->pb);
00439 var->pb = NULL;
00440 changed = 1;
00441 } else if (!var->pb && var->needed) {
00442 if (first)
00443 av_log(s, AV_LOG_DEBUG, "Opening variant stream %d\n", i);
00444 if (first && !c->finished)
00445 if ((ret = parse_playlist(c, var->url, var, NULL)) < 0)
00446 return ret;
00447 ret = open_variant(c, var, first);
00448 if (ret < 0)
00449 return ret;
00450 changed = 1;
00451 }
00452
00453 if (var->pb)
00454 variants++;
00455
00456
00457 if (var->pb && !var->pkt.data) {
00458 ret = av_read_frame(var->ctx, &var->pkt);
00459 if (ret < 0) {
00460 if (!url_feof(var->pb))
00461 return ret;
00462 reset_packet(&var->pkt);
00463 }
00464 }
00465
00466 if (var->pkt.data) {
00467 if (minvariant < 0 ||
00468 var->pkt.dts < c->variants[minvariant]->pkt.dts)
00469 minvariant = i;
00470 }
00471 }
00472 if (first && changed)
00473 av_log(s, AV_LOG_INFO, "Receiving %d variant streams\n", variants);
00474
00475 if (minvariant >= 0) {
00476 *pkt = c->variants[minvariant]->pkt;
00477 pkt->stream_index += c->variants[minvariant]->stream_offset;
00478 reset_packet(&c->variants[minvariant]->pkt);
00479 c->last_packet_dts = pkt->dts;
00480 return 0;
00481 }
00482
00483
00484 for (i = 0; i < c->n_variants; i++) {
00485 struct variant *var = c->variants[i];
00486 if (var->pb) {
00487 avio_close(var->pb);
00488 var->pb = NULL;
00489 }
00490 }
00491
00492
00493 first = 0;
00494 c->cur_seq_no++;
00495 reload:
00496 if (!c->finished) {
00497
00498
00499 int64_t now = av_gettime();
00500 if (now - c->last_load_time >= c->target_duration*1000000) {
00501 c->max_start_seq = 0;
00502 c->min_end_seq = INT_MAX;
00503 for (i = 0; i < c->n_variants; i++) {
00504 struct variant *var = c->variants[i];
00505 if (var->needed) {
00506 if ((ret = parse_playlist(c, var->url, var, NULL)) < 0)
00507 return ret;
00508 c->max_start_seq = FFMAX(c->max_start_seq,
00509 var->start_seq_no);
00510 c->min_end_seq = FFMIN(c->min_end_seq,
00511 var->start_seq_no + var->n_segments);
00512 }
00513 }
00514 }
00515 }
00516 if (c->cur_seq_no < c->max_start_seq) {
00517 av_log(NULL, AV_LOG_WARNING,
00518 "skipping %d segments ahead, expired from playlists\n",
00519 c->max_start_seq - c->cur_seq_no);
00520 c->cur_seq_no = c->max_start_seq;
00521 }
00522
00523 if (c->cur_seq_no < c->min_end_seq)
00524 goto start;
00525
00526
00527 if (c->finished)
00528 return AVERROR_EOF;
00529 while (av_gettime() - c->last_load_time < c->target_duration*1000000) {
00530 if (url_interrupt_cb())
00531 return AVERROR(EINTR);
00532 usleep(100*1000);
00533 }
00534
00535 goto reload;
00536 }
00537
00538 static int applehttp_close(AVFormatContext *s)
00539 {
00540 AppleHTTPContext *c = s->priv_data;
00541
00542 free_variant_list(c);
00543 return 0;
00544 }
00545
00546 static int applehttp_read_seek(AVFormatContext *s, int stream_index,
00547 int64_t timestamp, int flags)
00548 {
00549 AppleHTTPContext *c = s->priv_data;
00550 int pos = 0, i;
00551 struct variant *var = c->variants[0];
00552
00553 if ((flags & AVSEEK_FLAG_BYTE) || !c->finished)
00554 return AVERROR(ENOSYS);
00555
00556
00557 c->last_packet_dts = AV_NOPTS_VALUE;
00558 for (i = 0; i < c->n_variants; i++) {
00559 struct variant *var = c->variants[i];
00560 if (var->pb) {
00561 avio_close(var->pb);
00562 var->pb = NULL;
00563 }
00564 av_free_packet(&var->pkt);
00565 reset_packet(&var->pkt);
00566 }
00567
00568 timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ?
00569 s->streams[stream_index]->time_base.den :
00570 AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ?
00571 AV_ROUND_DOWN : AV_ROUND_UP);
00572
00573 for (i = 0; i < var->n_segments; i++) {
00574 if (timestamp >= pos && timestamp < pos + var->segments[i]->duration) {
00575 c->cur_seq_no = var->start_seq_no + i;
00576 return 0;
00577 }
00578 pos += var->segments[i]->duration;
00579 }
00580 return AVERROR(EIO);
00581 }
00582
00583 static int applehttp_probe(AVProbeData *p)
00584 {
00585
00586
00587 if (strncmp(p->buf, "#EXTM3U", 7))
00588 return 0;
00589 if (strstr(p->buf, "#EXT-X-STREAM-INF:") ||
00590 strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
00591 strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:"))
00592 return AVPROBE_SCORE_MAX;
00593 return 0;
00594 }
00595
00596 AVInputFormat ff_applehttp_demuxer = {
00597 "applehttp",
00598 NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"),
00599 sizeof(AppleHTTPContext),
00600 applehttp_probe,
00601 applehttp_read_header,
00602 applehttp_read_packet,
00603 applehttp_close,
00604 applehttp_read_seek,
00605 };