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