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