FFmpeg
hlsproto.c
Go to the documentation of this file.
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;
51 };
52 
53 struct variant {
54  int bandwidth;
56 };
57 
58 typedef struct HLSContext {
59  char playlisturl[MAX_URL_SIZE];
60  int64_t target_duration;
62  int finished;
64  struct segment **segments;
65  int n_variants;
66  struct variant **variants;
67  int cur_seq_no;
69  int64_t last_load_time;
70 } HLSContext;
71 
73 {
74  int i;
75  for (i = 0; i < s->n_segments; i++)
76  av_freep(&s->segments[i]);
77  av_freep(&s->segments);
78  s->n_segments = 0;
79 }
80 
82 {
83  int i;
84  for (i = 0; i < s->n_variants; i++)
85  av_freep(&s->variants[i]);
86  av_freep(&s->variants);
87  s->n_variants = 0;
88 }
89 
90 struct variant_info {
91  char bandwidth[20];
92 };
93 
94 static void handle_variant_args(struct variant_info *info, const char *key,
95  int key_len, char **dest, int *dest_len)
96 {
97  if (!strncmp(key, "BANDWIDTH=", key_len)) {
98  *dest = info->bandwidth;
99  *dest_len = sizeof(info->bandwidth);
100  }
101 }
102 
103 static int parse_playlist(URLContext *h, const char *url)
104 {
105  HLSContext *s = h->priv_data;
106  AVIOContext *in;
107  int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
108  int64_t duration = 0;
109  char line[1024];
110  const char *ptr;
111 
112  if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
115  return ret;
116 
117  ff_get_chomp_line(in, line, sizeof(line));
118  if (strcmp(line, "#EXTM3U")) {
119  ret = AVERROR_INVALIDDATA;
120  goto fail;
121  }
122 
124  s->finished = 0;
125  while (!avio_feof(in)) {
126  ff_get_chomp_line(in, line, sizeof(line));
127  if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
128  struct variant_info info = {{0}};
129  is_variant = 1;
131  &info);
132  bandwidth = atoi(info.bandwidth);
133  } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
134  s->target_duration = atoi(ptr) * AV_TIME_BASE;
135  } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
136  s->start_seq_no = atoi(ptr);
137  } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
138  s->finished = 1;
139  } else if (av_strstart(line, "#EXTINF:", &ptr)) {
140  is_segment = 1;
141  duration = atof(ptr) * AV_TIME_BASE;
142  } else if (av_strstart(line, "#", NULL)) {
143  continue;
144  } else if (line[0]) {
145  if (is_segment) {
146  struct segment *seg = av_malloc(sizeof(struct segment));
147  if (!seg) {
148  ret = AVERROR(ENOMEM);
149  goto fail;
150  }
151  seg->duration = duration;
152  ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
153  dynarray_add(&s->segments, &s->n_segments, seg);
154  is_segment = 0;
155  } else if (is_variant) {
156  struct variant *var = av_malloc(sizeof(struct variant));
157  if (!var) {
158  ret = AVERROR(ENOMEM);
159  goto fail;
160  }
161  var->bandwidth = bandwidth;
162  ff_make_absolute_url(var->url, sizeof(var->url), url, line);
163  dynarray_add(&s->variants, &s->n_variants, var);
164  is_variant = 0;
165  }
166  }
167  }
169 
170 fail:
171  avio_close(in);
172  return ret;
173 }
174 
175 static int hls_close(URLContext *h)
176 {
177  HLSContext *s = h->priv_data;
178 
181  ffurl_close(s->seg_hd);
182  return 0;
183 }
184 
185 static int hls_open(URLContext *h, const char *uri, int flags)
186 {
187  HLSContext *s = h->priv_data;
188  int ret, i;
189  const char *nested_url;
190 
191  if (flags & AVIO_FLAG_WRITE)
192  return AVERROR(ENOSYS);
193 
194  h->is_streamed = 1;
195 
196  if (av_strstart(uri, "hls+", &nested_url)) {
197  av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));
198  } else if (av_strstart(uri, "hls://", &nested_url)) {
199  av_log(h, AV_LOG_ERROR,
200  "No nested protocol specified. Specify e.g. hls+http://%s\n",
201  nested_url);
202  ret = AVERROR(EINVAL);
203  goto fail;
204  } else {
205  av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
206  ret = AVERROR(EINVAL);
207  goto fail;
208  }
210  "Using the hls protocol is discouraged, please try using the "
211  "hls demuxer instead. The hls demuxer should be more complete "
212  "and work as well as the protocol implementation. (If not, "
213  "please report it.) To use the demuxer, simply use %s as url.\n",
214  s->playlisturl);
215 
216  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
217  goto fail;
218 
219  if (s->n_segments == 0 && s->n_variants > 0) {
220  int max_bandwidth = 0, maxvar = -1;
221  for (i = 0; i < s->n_variants; i++) {
222  if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {
223  max_bandwidth = s->variants[i]->bandwidth;
224  maxvar = i;
225  }
226  }
227  av_strlcpy(s->playlisturl, s->variants[maxvar]->url,
228  sizeof(s->playlisturl));
229  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
230  goto fail;
231  }
232 
233  if (s->n_segments == 0) {
234  av_log(h, AV_LOG_WARNING, "Empty playlist\n");
235  ret = AVERROR(EIO);
236  goto fail;
237  }
238  s->cur_seq_no = s->start_seq_no;
239  if (!s->finished && s->n_segments >= 3)
240  s->cur_seq_no = s->start_seq_no + s->n_segments - 3;
241 
242  return 0;
243 
244 fail:
245  hls_close(h);
246  return ret;
247 }
248 
249 static int hls_read(URLContext *h, uint8_t *buf, int size)
250 {
251  HLSContext *s = h->priv_data;
252  const char *url;
253  int ret;
254  int64_t reload_interval;
255 
256 start:
257  if (s->seg_hd) {
258  ret = ffurl_read(s->seg_hd, buf, size);
259  if (ret > 0)
260  return ret;
261  }
262  if (s->seg_hd) {
263  ffurl_close(s->seg_hd);
264  s->seg_hd = NULL;
265  s->cur_seq_no++;
266  }
267  reload_interval = s->n_segments > 0 ?
268  s->segments[s->n_segments - 1]->duration :
269  s->target_duration;
270 retry:
271  if (!s->finished) {
272  int64_t now = av_gettime_relative();
273  if (now - s->last_load_time >= reload_interval) {
274  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
275  return ret;
276  /* If we need to reload the playlist again below (if
277  * there's still no more segments), switch to a reload
278  * interval of half the target duration. */
279  reload_interval = s->target_duration / 2;
280  }
281  }
282  if (s->cur_seq_no < s->start_seq_no) {
284  "skipping %d segments ahead, expired from playlist\n",
285  s->start_seq_no - s->cur_seq_no);
286  s->cur_seq_no = s->start_seq_no;
287  }
288  if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
289  if (s->finished)
290  return AVERROR_EOF;
291  while (av_gettime_relative() - s->last_load_time < reload_interval) {
293  return AVERROR_EXIT;
294  av_usleep(100*1000);
295  }
296  goto retry;
297  }
298  url = s->segments[s->cur_seq_no - s->start_seq_no]->url;
299  av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
303  if (ret < 0) {
305  return AVERROR_EXIT;
306  av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url);
307  s->cur_seq_no++;
308  goto retry;
309  }
310  goto start;
311 }
312 
314  .name = "hls",
315  .url_open = hls_open,
316  .url_read = hls_read,
317  .url_close = hls_close,
319  .priv_data_size = sizeof(HLSContext),
320 };
#define NULL
Definition: coverity.c:32
void ff_make_absolute_url(char *buf, int size, const char *base, const char *rel)
Convert a relative url into an absolute url, given a base url.
Definition: url.c:80
Bytestream IO Context.
Definition: avio.h:161
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
int bandwidth
Definition: hls.c:178
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it. ...
Definition: avio.c:306
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:45
AVIOInterruptCB interrupt_callback
Definition: url.h:47
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
char * key
Definition: hls.c:73
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition: time.c:84
char * url
Definition: hls.c:72
#define MAX_URL_SIZE
Definition: internal.h:30
uint8_t
int n_variants
Definition: hls.c:192
#define av_malloc(s)
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:100
static void free_variant_list(HLSContext *s)
Definition: hlsproto.c:81
char playlisturl[MAX_URL_SIZE]
Definition: hlsproto.c:59
#define av_log(a,...)
struct variant ** variants
Definition: hls.c:193
int n_segments
Definition: hlsproto.c:63
URLContext * seg_hd
Definition: hlsproto.c:68
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
char bandwidth[20]
Definition: hls.c:320
const char * protocol_whitelist
Definition: url.h:49
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1217
static int parse_playlist(URLContext *h, const char *url)
Definition: hlsproto.c:103
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
#define URL_PROTOCOL_FLAG_NESTED_SCHEME
Definition: url.h:33
Definition: graph2dot.c:48
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:83
#define fail()
Definition: checkasm.h:122
Definition: hls.c:68
MIPS optimizations info
Definition: mips.txt:2
#define dynarray_add(tab, nb_ptr, elem)
Definition: internal.h:198
static void handle_variant_args(struct variant_info *info, const char *key, int key_len, char **dest, int *dest_len)
Definition: hlsproto.c:94
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
void(* ff_parse_key_val_cb)(void *context, const char *key, int key_len, char **dest, int *dest_len)
Callback function type for ff_parse_key_value.
Definition: internal.h:366
static int hls_read(URLContext *h, uint8_t *buf, int size)
Definition: hlsproto.c:249
#define s(width, name)
Definition: cbs_vp9.c:257
Libavformat version macros.
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition: error.h:56
const char * protocol_blacklist
Definition: url.h:50
int ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
Definition: avio.c:663
char url[MAX_URL_SIZE]
Definition: hlsproto.c:55
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31))))#define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac){}void ff_audio_convert_free(AudioConvert **ac){if(!*ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);}AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map){AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method!=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2){ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc){av_free(ac);return NULL;}return ac;}in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar){ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar?ac->channels:1;}else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;}int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in){int use_generic=1;int len=in->nb_samples;int p;if(ac->dc){av_log(ac->avr, AV_LOG_TRACE,"%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
void * priv_data
Definition: url.h:41
int64_t last_load_time
Definition: hlsproto.c:69
int ffio_open_whitelist(AVIOContext **s, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist)
Definition: aviobuf.c:1192
const char * name
Definition: url.h:55
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition: time.c:56
#define flags(name, subs,...)
Definition: cbs_av1.c:561
int ffurl_close(URLContext *h)
Definition: avio.c:466
void ff_parse_key_value(const char *str, ff_parse_key_val_cb callback_get_buf, void *context)
Parse a string with comma-separated key=value pairs.
Definition: utils.c:4959
int start_seq_no
Definition: hlsproto.c:61
int64_t target_duration
Definition: hlsproto.c:60
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
Definition: avstring.c:34
Main libavformat public API header.
struct segment ** segments
Definition: hlsproto.c:64
int cur_seq_no
Definition: hls.c:199
static void free_segment_list(HLSContext *s)
Definition: hlsproto.c:72
static int hls_open(URLContext *h, const char *uri, int flags)
Definition: hlsproto.c:185
const URLProtocol ff_hls_protocol
Definition: hlsproto.c:313
static int hls_close(URLContext *h)
Definition: hlsproto.c:175
Definition: hls.c:177
#define av_freep(p)
void INT64 start
Definition: avisynth_c.h:766
unbuffered private I/O API
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:361
int finished
Definition: hlsproto.c:62
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
int64_t duration
Definition: hls.c:69
int ff_get_chomp_line(AVIOContext *s, char *buf, int maxlen)
Same as ff_get_line but strip the white-space characters in the text tail.
Definition: aviobuf.c:828
int ffurl_read(URLContext *h, unsigned char *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf...
Definition: avio.c:406