• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavformat/applehttp.c

Go to the documentation of this file.
00001 /*
00002  * Apple HTTP Live Streaming demuxer
00003  * Copyright (c) 2010 Martin Storsjo
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00028 #include "libavutil/avstring.h"
00029 #include "libavutil/intreadwrite.h"
00030 #include "libavutil/mathematics.h"
00031 #include "libavutil/opt.h"
00032 #include "libavutil/dict.h"
00033 #include "avformat.h"
00034 #include "internal.h"
00035 #include <unistd.h>
00036 #include "avio_internal.h"
00037 #include "url.h"
00038 
00039 #define INITIAL_BUFFER_SIZE 32768
00040 
00041 /*
00042  * An apple http stream consists of a playlist with media segment files,
00043  * played sequentially. There may be several playlists with the same
00044  * video content, in different bandwidth variants, that are played in
00045  * parallel (preferrably only one bandwidth variant at a time). In this case,
00046  * the user supplied the url to a main playlist that only lists the variant
00047  * playlists.
00048  *
00049  * If the main playlist doesn't point at any variants, we still create
00050  * one anonymous toplevel variant for this, to maintain the structure.
00051  */
00052 
00053 enum KeyType {
00054     KEY_NONE,
00055     KEY_AES_128,
00056 };
00057 
00058 struct segment {
00059     int duration;
00060     char url[MAX_URL_SIZE];
00061     char key[MAX_URL_SIZE];
00062     enum KeyType key_type;
00063     uint8_t iv[16];
00064 };
00065 
00066 /*
00067  * Each variant has its own demuxer. If it currently is active,
00068  * it has an open AVIOContext too, and potentially an AVPacket
00069  * containing the next packet from this stream.
00070  */
00071 struct variant {
00072     int bandwidth;
00073     char url[MAX_URL_SIZE];
00074     AVIOContext pb;
00075     uint8_t* read_buffer;
00076     URLContext *input;
00077     AVFormatContext *parent;
00078     int index;
00079     AVFormatContext *ctx;
00080     AVPacket pkt;
00081     int stream_offset;
00082 
00083     int finished;
00084     int target_duration;
00085     int start_seq_no;
00086     int n_segments;
00087     struct segment **segments;
00088     int needed, cur_needed;
00089     int cur_seq_no;
00090     int64_t last_load_time;
00091 
00092     char key_url[MAX_URL_SIZE];
00093     uint8_t key[16];
00094 };
00095 
00096 typedef struct AppleHTTPContext {
00097     int n_variants;
00098     struct variant **variants;
00099     int cur_seq_no;
00100     int end_of_segment;
00101     int first_packet;
00102     int64_t first_timestamp;
00103     AVIOInterruptCB *interrupt_callback;
00104 } AppleHTTPContext;
00105 
00106 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
00107 {
00108     int len = ff_get_line(s, buf, maxlen);
00109     while (len > 0 && isspace(buf[len - 1]))
00110         buf[--len] = '\0';
00111     return len;
00112 }
00113 
00114 static void free_segment_list(struct variant *var)
00115 {
00116     int i;
00117     for (i = 0; i < var->n_segments; i++)
00118         av_free(var->segments[i]);
00119     av_freep(&var->segments);
00120     var->n_segments = 0;
00121 }
00122 
00123 static void free_variant_list(AppleHTTPContext *c)
00124 {
00125     int i;
00126     for (i = 0; i < c->n_variants; i++) {
00127         struct variant *var = c->variants[i];
00128         free_segment_list(var);
00129         av_free_packet(&var->pkt);
00130         av_free(var->pb.buffer);
00131         if (var->input)
00132             ffurl_close(var->input);
00133         if (var->ctx) {
00134             var->ctx->pb = NULL;
00135             avformat_close_input(&var->ctx);
00136         }
00137         av_free(var);
00138     }
00139     av_freep(&c->variants);
00140     c->n_variants = 0;
00141 }
00142 
00143 /*
00144  * Used to reset a statically allocated AVPacket to a clean slate,
00145  * containing no data.
00146  */
00147 static void reset_packet(AVPacket *pkt)
00148 {
00149     av_init_packet(pkt);
00150     pkt->data = NULL;
00151 }
00152 
00153 static struct variant *new_variant(AppleHTTPContext *c, int bandwidth,
00154                                    const char *url, const char *base)
00155 {
00156     struct variant *var = av_mallocz(sizeof(struct variant));
00157     if (!var)
00158         return NULL;
00159     reset_packet(&var->pkt);
00160     var->bandwidth = bandwidth;
00161     ff_make_absolute_url(var->url, sizeof(var->url), base, url);
00162     dynarray_add(&c->variants, &c->n_variants, var);
00163     return var;
00164 }
00165 
00166 struct variant_info {
00167     char bandwidth[20];
00168 };
00169 
00170 static void handle_variant_args(struct variant_info *info, const char *key,
00171                                 int key_len, char **dest, int *dest_len)
00172 {
00173     if (!strncmp(key, "BANDWIDTH=", key_len)) {
00174         *dest     =        info->bandwidth;
00175         *dest_len = sizeof(info->bandwidth);
00176     }
00177 }
00178 
00179 struct key_info {
00180      char uri[MAX_URL_SIZE];
00181      char method[10];
00182      char iv[35];
00183 };
00184 
00185 static void handle_key_args(struct key_info *info, const char *key,
00186                             int key_len, char **dest, int *dest_len)
00187 {
00188     if (!strncmp(key, "METHOD=", key_len)) {
00189         *dest     =        info->method;
00190         *dest_len = sizeof(info->method);
00191     } else if (!strncmp(key, "URI=", key_len)) {
00192         *dest     =        info->uri;
00193         *dest_len = sizeof(info->uri);
00194     } else if (!strncmp(key, "IV=", key_len)) {
00195         *dest     =        info->iv;
00196         *dest_len = sizeof(info->iv);
00197     }
00198 }
00199 
00200 static int parse_playlist(AppleHTTPContext *c, const char *url,
00201                           struct variant *var, AVIOContext *in)
00202 {
00203     int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
00204     enum KeyType key_type = KEY_NONE;
00205     uint8_t iv[16] = "";
00206     int has_iv = 0;
00207     char key[MAX_URL_SIZE] = "";
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_open2(&in, url, AVIO_FLAG_READ,
00215                               c->interrupt_callback, NULL)) < 0)
00216             return ret;
00217     }
00218 
00219     read_chomp_line(in, line, sizeof(line));
00220     if (strcmp(line, "#EXTM3U")) {
00221         ret = AVERROR_INVALIDDATA;
00222         goto fail;
00223     }
00224 
00225     if (var) {
00226         free_segment_list(var);
00227         var->finished = 0;
00228     }
00229     while (!in->eof_reached) {
00230         read_chomp_line(in, line, sizeof(line));
00231         if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
00232             struct variant_info info = {{0}};
00233             is_variant = 1;
00234             ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
00235                                &info);
00236             bandwidth = atoi(info.bandwidth);
00237         } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
00238             struct key_info info = {{0}};
00239             ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_key_args,
00240                                &info);
00241             key_type = KEY_NONE;
00242             has_iv = 0;
00243             if (!strcmp(info.method, "AES-128"))
00244                 key_type = KEY_AES_128;
00245             if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) {
00246                 ff_hex_to_data(iv, info.iv + 2);
00247                 has_iv = 1;
00248             }
00249             av_strlcpy(key, info.uri, sizeof(key));
00250         } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
00251             if (!var) {
00252                 var = new_variant(c, 0, url, NULL);
00253                 if (!var) {
00254                     ret = AVERROR(ENOMEM);
00255                     goto fail;
00256                 }
00257             }
00258             var->target_duration = atoi(ptr);
00259         } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
00260             if (!var) {
00261                 var = new_variant(c, 0, url, NULL);
00262                 if (!var) {
00263                     ret = AVERROR(ENOMEM);
00264                     goto fail;
00265                 }
00266             }
00267             var->start_seq_no = atoi(ptr);
00268         } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
00269             if (var)
00270                 var->finished = 1;
00271         } else if (av_strstart(line, "#EXTINF:", &ptr)) {
00272             is_segment = 1;
00273             duration   = atoi(ptr);
00274         } else if (av_strstart(line, "#", NULL)) {
00275             continue;
00276         } else if (line[0]) {
00277             if (is_variant) {
00278                 if (!new_variant(c, bandwidth, line, url)) {
00279                     ret = AVERROR(ENOMEM);
00280                     goto fail;
00281                 }
00282                 is_variant = 0;
00283                 bandwidth  = 0;
00284             }
00285             if (is_segment) {
00286                 struct segment *seg;
00287                 if (!var) {
00288                     var = new_variant(c, 0, url, NULL);
00289                     if (!var) {
00290                         ret = AVERROR(ENOMEM);
00291                         goto fail;
00292                     }
00293                 }
00294                 seg = av_malloc(sizeof(struct segment));
00295                 if (!seg) {
00296                     ret = AVERROR(ENOMEM);
00297                     goto fail;
00298                 }
00299                 seg->duration = duration;
00300                 seg->key_type = key_type;
00301                 if (has_iv) {
00302                     memcpy(seg->iv, iv, sizeof(iv));
00303                 } else {
00304                     int seq = var->start_seq_no + var->n_segments;
00305                     memset(seg->iv, 0, sizeof(seg->iv));
00306                     AV_WB32(seg->iv + 12, seq);
00307                 }
00308                 ff_make_absolute_url(seg->key, sizeof(seg->key), url, key);
00309                 ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
00310                 dynarray_add(&var->segments, &var->n_segments, seg);
00311                 is_segment = 0;
00312             }
00313         }
00314     }
00315     if (var)
00316         var->last_load_time = av_gettime();
00317 
00318 fail:
00319     if (close_in)
00320         avio_close(in);
00321     return ret;
00322 }
00323 
00324 static int open_input(struct variant *var)
00325 {
00326     struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
00327     if (seg->key_type == KEY_NONE) {
00328         return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
00329                           &var->parent->interrupt_callback, NULL);
00330     } else if (seg->key_type == KEY_AES_128) {
00331         char iv[33], key[33], url[MAX_URL_SIZE];
00332         int ret;
00333         if (strcmp(seg->key, var->key_url)) {
00334             URLContext *uc;
00335             if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ,
00336                            &var->parent->interrupt_callback, NULL) == 0) {
00337                 if (ffurl_read_complete(uc, var->key, sizeof(var->key))
00338                     != sizeof(var->key)) {
00339                     av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
00340                            seg->key);
00341                 }
00342                 ffurl_close(uc);
00343             } else {
00344                 av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n",
00345                        seg->key);
00346             }
00347             av_strlcpy(var->key_url, seg->key, sizeof(var->key_url));
00348         }
00349         ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
00350         ff_data_to_hex(key, var->key, sizeof(var->key), 0);
00351         iv[32] = key[32] = '\0';
00352         if (strstr(seg->url, "://"))
00353             snprintf(url, sizeof(url), "crypto+%s", seg->url);
00354         else
00355             snprintf(url, sizeof(url), "crypto:%s", seg->url);
00356         if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ,
00357                                &var->parent->interrupt_callback)) < 0)
00358             return ret;
00359         av_opt_set(var->input->priv_data, "key", key, 0);
00360         av_opt_set(var->input->priv_data, "iv", iv, 0);
00361         if ((ret = ffurl_connect(var->input, NULL)) < 0) {
00362             ffurl_close(var->input);
00363             var->input = NULL;
00364             return ret;
00365         }
00366         return 0;
00367     }
00368     return AVERROR(ENOSYS);
00369 }
00370 
00371 static int read_data(void *opaque, uint8_t *buf, int buf_size)
00372 {
00373     struct variant *v = opaque;
00374     AppleHTTPContext *c = v->parent->priv_data;
00375     int ret, i;
00376 
00377 restart:
00378     if (!v->input) {
00379         /* If this is a live stream and the reload interval has elapsed since
00380          * the last playlist reload, reload the variant playlists now. */
00381         int64_t reload_interval = v->n_segments > 0 ?
00382                                   v->segments[v->n_segments - 1]->duration :
00383                                   v->target_duration;
00384         reload_interval *= 1000000;
00385 
00386 reload:
00387         if (!v->finished &&
00388             av_gettime() - v->last_load_time >= reload_interval) {
00389             if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
00390                 return ret;
00391             /* If we need to reload the playlist again below (if
00392              * there's still no more segments), switch to a reload
00393              * interval of half the target duration. */
00394             reload_interval = v->target_duration * 500000;
00395         }
00396         if (v->cur_seq_no < v->start_seq_no) {
00397             av_log(NULL, AV_LOG_WARNING,
00398                    "skipping %d segments ahead, expired from playlists\n",
00399                    v->start_seq_no - v->cur_seq_no);
00400             v->cur_seq_no = v->start_seq_no;
00401         }
00402         if (v->cur_seq_no >= v->start_seq_no + v->n_segments) {
00403             if (v->finished)
00404                 return AVERROR_EOF;
00405             while (av_gettime() - v->last_load_time < reload_interval) {
00406                 if (ff_check_interrupt(c->interrupt_callback))
00407                     return AVERROR_EXIT;
00408                 usleep(100*1000);
00409             }
00410             /* Enough time has elapsed since the last reload */
00411             goto reload;
00412         }
00413 
00414         ret = open_input(v);
00415         if (ret < 0)
00416             return ret;
00417     }
00418     ret = ffurl_read(v->input, buf, buf_size);
00419     if (ret > 0)
00420         return ret;
00421     if (ret < 0 && ret != AVERROR_EOF)
00422         return ret;
00423     ffurl_close(v->input);
00424     v->input = NULL;
00425     v->cur_seq_no++;
00426 
00427     c->end_of_segment = 1;
00428     c->cur_seq_no = v->cur_seq_no;
00429 
00430     if (v->ctx && v->ctx->nb_streams) {
00431         v->needed = 0;
00432         for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
00433              i++) {
00434             if (v->parent->streams[i]->discard < AVDISCARD_ALL)
00435                 v->needed = 1;
00436         }
00437     }
00438     if (!v->needed) {
00439         av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n",
00440                v->index);
00441         return AVERROR_EOF;
00442     }
00443     goto restart;
00444 }
00445 
00446 static int applehttp_read_header(AVFormatContext *s, AVFormatParameters *ap)
00447 {
00448     AppleHTTPContext *c = s->priv_data;
00449     int ret = 0, i, j, stream_offset = 0;
00450 
00451     c->interrupt_callback = &s->interrupt_callback;
00452 
00453     if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
00454         goto fail;
00455 
00456     if (c->n_variants == 0) {
00457         av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
00458         ret = AVERROR_EOF;
00459         goto fail;
00460     }
00461     /* If the playlist only contained variants, parse each individual
00462      * variant playlist. */
00463     if (c->n_variants > 1 || c->variants[0]->n_segments == 0) {
00464         for (i = 0; i < c->n_variants; i++) {
00465             struct variant *v = c->variants[i];
00466             if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
00467                 goto fail;
00468         }
00469     }
00470 
00471     if (c->variants[0]->n_segments == 0) {
00472         av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
00473         ret = AVERROR_EOF;
00474         goto fail;
00475     }
00476 
00477     /* If this isn't a live stream, calculate the total duration of the
00478      * stream. */
00479     if (c->variants[0]->finished) {
00480         int64_t duration = 0;
00481         for (i = 0; i < c->variants[0]->n_segments; i++)
00482             duration += c->variants[0]->segments[i]->duration;
00483         s->duration = duration * AV_TIME_BASE;
00484     }
00485 
00486     /* Open the demuxer for each variant */
00487     for (i = 0; i < c->n_variants; i++) {
00488         struct variant *v = c->variants[i];
00489         AVInputFormat *in_fmt = NULL;
00490         char bitrate_str[20];
00491         if (v->n_segments == 0)
00492             continue;
00493 
00494         if (!(v->ctx = avformat_alloc_context())) {
00495             ret = AVERROR(ENOMEM);
00496             goto fail;
00497         }
00498 
00499         v->index  = i;
00500         v->needed = 1;
00501         v->parent = s;
00502 
00503         /* If this is a live stream with more than 3 segments, start at the
00504          * third last segment. */
00505         v->cur_seq_no = v->start_seq_no;
00506         if (!v->finished && v->n_segments > 3)
00507             v->cur_seq_no = v->start_seq_no + v->n_segments - 3;
00508 
00509         v->read_buffer = av_malloc(INITIAL_BUFFER_SIZE);
00510         ffio_init_context(&v->pb, v->read_buffer, INITIAL_BUFFER_SIZE, 0, v,
00511                           read_data, NULL, NULL);
00512         v->pb.seekable = 0;
00513         ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url,
00514                                     NULL, 0, 0);
00515         if (ret < 0) {
00516             /* Free the ctx - it isn't initialized properly at this point,
00517              * so avformat_close_input shouldn't be called. If
00518              * avformat_open_input fails below, it frees and zeros the
00519              * context, so it doesn't need any special treatment like this. */
00520             avformat_free_context(v->ctx);
00521             v->ctx = NULL;
00522             goto fail;
00523         }
00524         v->ctx->pb       = &v->pb;
00525         ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
00526         if (ret < 0)
00527             goto fail;
00528         v->stream_offset = stream_offset;
00529         snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth);
00530         /* Create new AVStreams for each stream in this variant */
00531         for (j = 0; j < v->ctx->nb_streams; j++) {
00532             AVStream *st = avformat_new_stream(s, NULL);
00533             if (!st) {
00534                 ret = AVERROR(ENOMEM);
00535                 goto fail;
00536             }
00537             st->id = i;
00538             avcodec_copy_context(st->codec, v->ctx->streams[j]->codec);
00539             if (v->bandwidth)
00540                 av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
00541                                  0);
00542         }
00543         stream_offset += v->ctx->nb_streams;
00544     }
00545 
00546     c->first_packet = 1;
00547     c->first_timestamp = AV_NOPTS_VALUE;
00548 
00549     return 0;
00550 fail:
00551     free_variant_list(c);
00552     return ret;
00553 }
00554 
00555 static int recheck_discard_flags(AVFormatContext *s, int first)
00556 {
00557     AppleHTTPContext *c = s->priv_data;
00558     int i, changed = 0;
00559 
00560     /* Check if any new streams are needed */
00561     for (i = 0; i < c->n_variants; i++)
00562         c->variants[i]->cur_needed = 0;;
00563 
00564     for (i = 0; i < s->nb_streams; i++) {
00565         AVStream *st = s->streams[i];
00566         struct variant *var = c->variants[s->streams[i]->id];
00567         if (st->discard < AVDISCARD_ALL)
00568             var->cur_needed = 1;
00569     }
00570     for (i = 0; i < c->n_variants; i++) {
00571         struct variant *v = c->variants[i];
00572         if (v->cur_needed && !v->needed) {
00573             v->needed = 1;
00574             changed = 1;
00575             v->cur_seq_no = c->cur_seq_no;
00576             v->pb.eof_reached = 0;
00577             av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i);
00578         } else if (first && !v->cur_needed && v->needed) {
00579             if (v->input)
00580                 ffurl_close(v->input);
00581             v->input = NULL;
00582             v->needed = 0;
00583             changed = 1;
00584             av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i);
00585         }
00586     }
00587     return changed;
00588 }
00589 
00590 static int applehttp_read_packet(AVFormatContext *s, AVPacket *pkt)
00591 {
00592     AppleHTTPContext *c = s->priv_data;
00593     int ret, i, minvariant = -1;
00594 
00595     if (c->first_packet) {
00596         recheck_discard_flags(s, 1);
00597         c->first_packet = 0;
00598     }
00599 
00600 start:
00601     c->end_of_segment = 0;
00602     for (i = 0; i < c->n_variants; i++) {
00603         struct variant *var = c->variants[i];
00604         /* Make sure we've got one buffered packet from each open variant
00605          * stream */
00606         if (var->needed && !var->pkt.data) {
00607             ret = av_read_frame(var->ctx, &var->pkt);
00608             if (ret < 0) {
00609                 if (!var->pb.eof_reached)
00610                     return ret;
00611                 reset_packet(&var->pkt);
00612             } else {
00613                 if (c->first_timestamp == AV_NOPTS_VALUE)
00614                     c->first_timestamp = var->pkt.dts;
00615             }
00616         }
00617         /* Check if this stream has the packet with the lowest dts */
00618         if (var->pkt.data) {
00619             if (minvariant < 0 ||
00620                 var->pkt.dts < c->variants[minvariant]->pkt.dts)
00621                 minvariant = i;
00622         }
00623     }
00624     if (c->end_of_segment) {
00625         if (recheck_discard_flags(s, 0))
00626             goto start;
00627     }
00628     /* If we got a packet, return it */
00629     if (minvariant >= 0) {
00630         *pkt = c->variants[minvariant]->pkt;
00631         pkt->stream_index += c->variants[minvariant]->stream_offset;
00632         reset_packet(&c->variants[minvariant]->pkt);
00633         return 0;
00634     }
00635     return AVERROR_EOF;
00636 }
00637 
00638 static int applehttp_close(AVFormatContext *s)
00639 {
00640     AppleHTTPContext *c = s->priv_data;
00641 
00642     free_variant_list(c);
00643     return 0;
00644 }
00645 
00646 static int applehttp_read_seek(AVFormatContext *s, int stream_index,
00647                                int64_t timestamp, int flags)
00648 {
00649     AppleHTTPContext *c = s->priv_data;
00650     int i, j, ret;
00651 
00652     if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished)
00653         return AVERROR(ENOSYS);
00654 
00655     timestamp = av_rescale_rnd(timestamp, 1, stream_index >= 0 ?
00656                                s->streams[stream_index]->time_base.den :
00657                                AV_TIME_BASE, flags & AVSEEK_FLAG_BACKWARD ?
00658                                AV_ROUND_DOWN : AV_ROUND_UP);
00659     ret = AVERROR(EIO);
00660     for (i = 0; i < c->n_variants; i++) {
00661         /* Reset reading */
00662         struct variant *var = c->variants[i];
00663         int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ? 0 :
00664                       av_rescale_rnd(c->first_timestamp, 1,
00665                           stream_index >= 0 ? s->streams[stream_index]->time_base.den : AV_TIME_BASE,
00666                           flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP);
00667         if (var->input) {
00668             ffurl_close(var->input);
00669             var->input = NULL;
00670         }
00671         av_free_packet(&var->pkt);
00672         reset_packet(&var->pkt);
00673         var->pb.eof_reached = 0;
00674 
00675         /* Locate the segment that contains the target timestamp */
00676         for (j = 0; j < var->n_segments; j++) {
00677             if (timestamp >= pos &&
00678                 timestamp < pos + var->segments[j]->duration) {
00679                 var->cur_seq_no = var->start_seq_no + j;
00680                 ret = 0;
00681                 break;
00682             }
00683             pos += var->segments[j]->duration;
00684         }
00685     }
00686     return ret;
00687 }
00688 
00689 static int applehttp_probe(AVProbeData *p)
00690 {
00691     /* Require #EXTM3U at the start, and either one of the ones below
00692      * somewhere for a proper match. */
00693     if (strncmp(p->buf, "#EXTM3U", 7))
00694         return 0;
00695     if (strstr(p->buf, "#EXT-X-STREAM-INF:")     ||
00696         strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
00697         strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:"))
00698         return AVPROBE_SCORE_MAX;
00699     return 0;
00700 }
00701 
00702 AVInputFormat ff_applehttp_demuxer = {
00703     .name           = "applehttp",
00704     .long_name      = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming format"),
00705     .priv_data_size = sizeof(AppleHTTPContext),
00706     .read_probe     = applehttp_probe,
00707     .read_header    = applehttp_read_header,
00708     .read_packet    = applehttp_read_packet,
00709     .read_close     = applehttp_close,
00710     .read_seek      = applehttp_read_seek,
00711 };
Generated on Thu Jan 24 2013 17:08:55 for Libav by doxygen 1.7.1