annotate ffmpeg/libavformat/hlsproto.c @ 13:844d341cf643 tip

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