LCOV - code coverage report
Current view: top level - libavformat - hlsproto.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 163 0.0 %
Date: 2017-12-14 01:15:32 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Apple HTTP Live Streaming Protocol Handler
       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 Protocol Handler
      25             :  * http://tools.ietf.org/html/draft-pantos-http-live-streaming
      26             :  */
      27             : 
      28             : #include "libavutil/avstring.h"
      29             : #include "libavutil/time.h"
      30             : #include "avformat.h"
      31             : #include "avio_internal.h"
      32             : #include "internal.h"
      33             : #include "url.h"
      34             : #include "version.h"
      35             : 
      36             : /*
      37             :  * An apple http stream consists of a playlist with media segment files,
      38             :  * played sequentially. There may be several playlists with the same
      39             :  * video content, in different bandwidth variants, that are played in
      40             :  * parallel (preferably only one bandwidth variant at a time). In this case,
      41             :  * the user supplied the url to a main playlist that only lists the variant
      42             :  * playlists.
      43             :  *
      44             :  * If the main playlist doesn't point at any variants, we still create
      45             :  * one anonymous toplevel variant for this, to maintain the structure.
      46             :  */
      47             : 
      48             : struct segment {
      49             :     int64_t duration;
      50             :     char url[MAX_URL_SIZE];
      51             : };
      52             : 
      53             : struct variant {
      54             :     int bandwidth;
      55             :     char url[MAX_URL_SIZE];
      56             : };
      57             : 
      58             : typedef struct HLSContext {
      59             :     char playlisturl[MAX_URL_SIZE];
      60             :     int64_t target_duration;
      61             :     int start_seq_no;
      62             :     int finished;
      63             :     int n_segments;
      64             :     struct segment **segments;
      65             :     int n_variants;
      66             :     struct variant **variants;
      67             :     int cur_seq_no;
      68             :     URLContext *seg_hd;
      69             :     int64_t last_load_time;
      70             : } HLSContext;
      71             : 
      72           0 : static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
      73             : {
      74           0 :     int len = ff_get_line(s, buf, maxlen);
      75           0 :     while (len > 0 && av_isspace(buf[len - 1]))
      76           0 :         buf[--len] = '\0';
      77           0 :     return len;
      78             : }
      79             : 
      80           0 : static void free_segment_list(HLSContext *s)
      81             : {
      82             :     int i;
      83           0 :     for (i = 0; i < s->n_segments; i++)
      84           0 :         av_freep(&s->segments[i]);
      85           0 :     av_freep(&s->segments);
      86           0 :     s->n_segments = 0;
      87           0 : }
      88             : 
      89           0 : static void free_variant_list(HLSContext *s)
      90             : {
      91             :     int i;
      92           0 :     for (i = 0; i < s->n_variants; i++)
      93           0 :         av_freep(&s->variants[i]);
      94           0 :     av_freep(&s->variants);
      95           0 :     s->n_variants = 0;
      96           0 : }
      97             : 
      98             : struct variant_info {
      99             :     char bandwidth[20];
     100             : };
     101             : 
     102           0 : static void handle_variant_args(struct variant_info *info, const char *key,
     103             :                                 int key_len, char **dest, int *dest_len)
     104             : {
     105           0 :     if (!strncmp(key, "BANDWIDTH=", key_len)) {
     106           0 :         *dest     =        info->bandwidth;
     107           0 :         *dest_len = sizeof(info->bandwidth);
     108             :     }
     109           0 : }
     110             : 
     111           0 : static int parse_playlist(URLContext *h, const char *url)
     112             : {
     113           0 :     HLSContext *s = h->priv_data;
     114             :     AVIOContext *in;
     115           0 :     int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
     116           0 :     int64_t duration = 0;
     117             :     char line[1024];
     118             :     const char *ptr;
     119             : 
     120           0 :     if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
     121           0 :                                    &h->interrupt_callback, NULL,
     122             :                                    h->protocol_whitelist, h->protocol_blacklist)) < 0)
     123           0 :         return ret;
     124             : 
     125           0 :     read_chomp_line(in, line, sizeof(line));
     126           0 :     if (strcmp(line, "#EXTM3U")) {
     127           0 :         ret = AVERROR_INVALIDDATA;
     128           0 :         goto fail;
     129             :     }
     130             : 
     131           0 :     free_segment_list(s);
     132           0 :     s->finished = 0;
     133           0 :     while (!avio_feof(in)) {
     134           0 :         read_chomp_line(in, line, sizeof(line));
     135           0 :         if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
     136           0 :             struct variant_info info = {{0}};
     137           0 :             is_variant = 1;
     138           0 :             ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
     139             :                                &info);
     140           0 :             bandwidth = atoi(info.bandwidth);
     141           0 :         } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
     142           0 :             s->target_duration = atoi(ptr) * AV_TIME_BASE;
     143           0 :         } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
     144           0 :             s->start_seq_no = atoi(ptr);
     145           0 :         } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
     146           0 :             s->finished = 1;
     147           0 :         } else if (av_strstart(line, "#EXTINF:", &ptr)) {
     148           0 :             is_segment = 1;
     149           0 :             duration = atof(ptr) * AV_TIME_BASE;
     150           0 :         } else if (av_strstart(line, "#", NULL)) {
     151           0 :             continue;
     152           0 :         } else if (line[0]) {
     153           0 :             if (is_segment) {
     154           0 :                 struct segment *seg = av_malloc(sizeof(struct segment));
     155           0 :                 if (!seg) {
     156           0 :                     ret = AVERROR(ENOMEM);
     157           0 :                     goto fail;
     158             :                 }
     159           0 :                 seg->duration = duration;
     160           0 :                 ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
     161           0 :                 dynarray_add(&s->segments, &s->n_segments, seg);
     162           0 :                 is_segment = 0;
     163           0 :             } else if (is_variant) {
     164           0 :                 struct variant *var = av_malloc(sizeof(struct variant));
     165           0 :                 if (!var) {
     166           0 :                     ret = AVERROR(ENOMEM);
     167           0 :                     goto fail;
     168             :                 }
     169           0 :                 var->bandwidth = bandwidth;
     170           0 :                 ff_make_absolute_url(var->url, sizeof(var->url), url, line);
     171           0 :                 dynarray_add(&s->variants, &s->n_variants, var);
     172           0 :                 is_variant = 0;
     173             :             }
     174             :         }
     175             :     }
     176           0 :     s->last_load_time = av_gettime_relative();
     177             : 
     178           0 : fail:
     179           0 :     avio_close(in);
     180           0 :     return ret;
     181             : }
     182             : 
     183           0 : static int hls_close(URLContext *h)
     184             : {
     185           0 :     HLSContext *s = h->priv_data;
     186             : 
     187           0 :     free_segment_list(s);
     188           0 :     free_variant_list(s);
     189           0 :     ffurl_close(s->seg_hd);
     190           0 :     return 0;
     191             : }
     192             : 
     193           0 : static int hls_open(URLContext *h, const char *uri, int flags)
     194             : {
     195           0 :     HLSContext *s = h->priv_data;
     196             :     int ret, i;
     197             :     const char *nested_url;
     198             : 
     199           0 :     if (flags & AVIO_FLAG_WRITE)
     200           0 :         return AVERROR(ENOSYS);
     201             : 
     202           0 :     h->is_streamed = 1;
     203             : 
     204           0 :     if (av_strstart(uri, "hls+", &nested_url)) {
     205           0 :         av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));
     206           0 :     } else if (av_strstart(uri, "hls://", &nested_url)) {
     207           0 :         av_log(h, AV_LOG_ERROR,
     208             :                "No nested protocol specified. Specify e.g. hls+http://%s\n",
     209             :                nested_url);
     210           0 :         ret = AVERROR(EINVAL);
     211           0 :         goto fail;
     212             :     } else {
     213           0 :         av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
     214           0 :         ret = AVERROR(EINVAL);
     215           0 :         goto fail;
     216             :     }
     217           0 :     av_log(h, AV_LOG_WARNING,
     218             :            "Using the hls protocol is discouraged, please try using the "
     219             :            "hls demuxer instead. The hls demuxer should be more complete "
     220             :            "and work as well as the protocol implementation. (If not, "
     221             :            "please report it.) To use the demuxer, simply use %s as url.\n",
     222           0 :            s->playlisturl);
     223             : 
     224           0 :     if ((ret = parse_playlist(h, s->playlisturl)) < 0)
     225           0 :         goto fail;
     226             : 
     227           0 :     if (s->n_segments == 0 && s->n_variants > 0) {
     228           0 :         int max_bandwidth = 0, maxvar = -1;
     229           0 :         for (i = 0; i < s->n_variants; i++) {
     230           0 :             if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {
     231           0 :                 max_bandwidth = s->variants[i]->bandwidth;
     232           0 :                 maxvar = i;
     233             :             }
     234             :         }
     235           0 :         av_strlcpy(s->playlisturl, s->variants[maxvar]->url,
     236             :                    sizeof(s->playlisturl));
     237           0 :         if ((ret = parse_playlist(h, s->playlisturl)) < 0)
     238           0 :             goto fail;
     239             :     }
     240             : 
     241           0 :     if (s->n_segments == 0) {
     242           0 :         av_log(h, AV_LOG_WARNING, "Empty playlist\n");
     243           0 :         ret = AVERROR(EIO);
     244           0 :         goto fail;
     245             :     }
     246           0 :     s->cur_seq_no = s->start_seq_no;
     247           0 :     if (!s->finished && s->n_segments >= 3)
     248           0 :         s->cur_seq_no = s->start_seq_no + s->n_segments - 3;
     249             : 
     250           0 :     return 0;
     251             : 
     252           0 : fail:
     253           0 :     hls_close(h);
     254           0 :     return ret;
     255             : }
     256             : 
     257           0 : static int hls_read(URLContext *h, uint8_t *buf, int size)
     258             : {
     259           0 :     HLSContext *s = h->priv_data;
     260             :     const char *url;
     261             :     int ret;
     262             :     int64_t reload_interval;
     263             : 
     264           0 : start:
     265           0 :     if (s->seg_hd) {
     266           0 :         ret = ffurl_read(s->seg_hd, buf, size);
     267           0 :         if (ret > 0)
     268           0 :             return ret;
     269             :     }
     270           0 :     if (s->seg_hd) {
     271           0 :         ffurl_close(s->seg_hd);
     272           0 :         s->seg_hd = NULL;
     273           0 :         s->cur_seq_no++;
     274             :     }
     275           0 :     reload_interval = s->n_segments > 0 ?
     276           0 :                       s->segments[s->n_segments - 1]->duration :
     277             :                       s->target_duration;
     278           0 : retry:
     279           0 :     if (!s->finished) {
     280           0 :         int64_t now = av_gettime_relative();
     281           0 :         if (now - s->last_load_time >= reload_interval) {
     282           0 :             if ((ret = parse_playlist(h, s->playlisturl)) < 0)
     283           0 :                 return ret;
     284             :             /* If we need to reload the playlist again below (if
     285             :              * there's still no more segments), switch to a reload
     286             :              * interval of half the target duration. */
     287           0 :             reload_interval = s->target_duration / 2;
     288             :         }
     289             :     }
     290           0 :     if (s->cur_seq_no < s->start_seq_no) {
     291           0 :         av_log(h, AV_LOG_WARNING,
     292             :                "skipping %d segments ahead, expired from playlist\n",
     293           0 :                s->start_seq_no - s->cur_seq_no);
     294           0 :         s->cur_seq_no = s->start_seq_no;
     295             :     }
     296           0 :     if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
     297           0 :         if (s->finished)
     298           0 :             return AVERROR_EOF;
     299           0 :         while (av_gettime_relative() - s->last_load_time < reload_interval) {
     300           0 :             if (ff_check_interrupt(&h->interrupt_callback))
     301           0 :                 return AVERROR_EXIT;
     302           0 :             av_usleep(100*1000);
     303             :         }
     304           0 :         goto retry;
     305             :     }
     306           0 :     url = s->segments[s->cur_seq_no - s->start_seq_no]->url,
     307           0 :     av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
     308           0 :     ret = ffurl_open_whitelist(&s->seg_hd, url, AVIO_FLAG_READ,
     309           0 :                                &h->interrupt_callback, NULL,
     310             :                                h->protocol_whitelist, h->protocol_blacklist, h);
     311           0 :     if (ret < 0) {
     312           0 :         if (ff_check_interrupt(&h->interrupt_callback))
     313           0 :             return AVERROR_EXIT;
     314           0 :         av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url);
     315           0 :         s->cur_seq_no++;
     316           0 :         goto retry;
     317             :     }
     318           0 :     goto start;
     319             : }
     320             : 
     321             : const URLProtocol ff_hls_protocol = {
     322             :     .name           = "hls",
     323             :     .url_open       = hls_open,
     324             :     .url_read       = hls_read,
     325             :     .url_close      = hls_close,
     326             :     .flags          = URL_PROTOCOL_FLAG_NESTED_SCHEME,
     327             :     .priv_data_size = sizeof(HLSContext),
     328             : };

Generated by: LCOV version 1.13