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  * https://www.rfc-editor.org/rfc/rfc8216.txt
26  */
27 
28 #include "libavutil/avstring.h"
29 #include "libavutil/mem.h"
30 #include "libavutil/time.h"
31 #include "avio_internal.h"
32 #include "internal.h"
33 #include "url.h"
34 
35 /*
36  * An apple http stream consists of a playlist with media segment files,
37  * played sequentially. There may be several playlists with the same
38  * video content, in different bandwidth variants, that are played in
39  * parallel (preferably only one bandwidth variant at a time). In this case,
40  * the user supplied the url to a main playlist that only lists the variant
41  * playlists.
42  *
43  * If the main playlist doesn't point at any variants, we still create
44  * one anonymous toplevel variant for this, to maintain the structure.
45  */
46 
47 struct segment {
48  int64_t duration;
50 };
51 
52 struct variant {
53  int bandwidth;
55 };
56 
57 typedef struct HLSContext {
59  int64_t target_duration;
61  int finished;
63  struct segment **segments;
64  int n_variants;
65  struct variant **variants;
68  int64_t last_load_time;
69 } HLSContext;
70 
72 {
73  int i;
74  for (i = 0; i < s->n_segments; i++)
75  av_freep(&s->segments[i]);
76  av_freep(&s->segments);
77  s->n_segments = 0;
78 }
79 
81 {
82  int i;
83  for (i = 0; i < s->n_variants; i++)
84  av_freep(&s->variants[i]);
85  av_freep(&s->variants);
86  s->n_variants = 0;
87 }
88 
89 struct variant_info {
90  char bandwidth[20];
91 };
92 
93 static void handle_variant_args(struct variant_info *info, const char *key,
94  int key_len, char **dest, int *dest_len)
95 {
96  if (!strncmp(key, "BANDWIDTH=", key_len)) {
97  *dest = info->bandwidth;
98  *dest_len = sizeof(info->bandwidth);
99  }
100 }
101 
102 static int parse_playlist(URLContext *h, const char *url)
103 {
104  HLSContext *s = h->priv_data;
105  AVIOContext *in;
106  int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
107  int64_t duration = 0;
108  char line[1024];
109  const char *ptr;
110 
111  if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
112  &h->interrupt_callback, NULL,
113  h->protocol_whitelist, h->protocol_blacklist)) < 0)
114  return ret;
115 
116  ff_get_chomp_line(in, line, sizeof(line));
117  if (strcmp(line, "#EXTM3U")) {
119  goto fail;
120  }
121 
123  s->finished = 0;
124  while (!avio_feof(in)) {
125  ff_get_chomp_line(in, line, sizeof(line));
126  if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
127  struct variant_info info = {{0}};
128  is_variant = 1;
130  &info);
131  bandwidth = atoi(info.bandwidth);
132  } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
133  s->target_duration = atoi(ptr) * AV_TIME_BASE;
134  } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
135  s->start_seq_no = atoi(ptr);
136  } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
137  s->finished = 1;
138  } else if (av_strstart(line, "#EXTINF:", &ptr)) {
139  is_segment = 1;
140  duration = atof(ptr) * AV_TIME_BASE;
141  } else if (av_strstart(line, "#", NULL)) {
142  continue;
143  } else if (line[0]) {
144  if (is_segment) {
145  struct segment *seg = av_malloc(sizeof(struct segment));
146  if (!seg) {
147  ret = AVERROR(ENOMEM);
148  goto fail;
149  }
150  seg->duration = duration;
151  ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
152  dynarray_add(&s->segments, &s->n_segments, seg);
153  is_segment = 0;
154  } else if (is_variant) {
155  struct variant *var = av_malloc(sizeof(struct variant));
156  if (!var) {
157  ret = AVERROR(ENOMEM);
158  goto fail;
159  }
160  var->bandwidth = bandwidth;
161  ff_make_absolute_url(var->url, sizeof(var->url), url, line);
162  dynarray_add(&s->variants, &s->n_variants, var);
163  is_variant = 0;
164  }
165  }
166  }
167  s->last_load_time = av_gettime_relative();
168 
169 fail:
170  avio_close(in);
171  return ret;
172 }
173 
174 static int hls_close(URLContext *h)
175 {
176  HLSContext *s = h->priv_data;
177 
180  ffurl_closep(&s->seg_hd);
181  return 0;
182 }
183 
184 static int hls_open(URLContext *h, const char *uri, int flags)
185 {
186  HLSContext *s = h->priv_data;
187  int ret, i;
188  const char *nested_url;
189 
190  if (flags & AVIO_FLAG_WRITE)
191  return AVERROR(ENOSYS);
192 
193  h->is_streamed = 1;
194 
195  if (av_strstart(uri, "hls+", &nested_url)) {
196  av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));
197  } else if (av_strstart(uri, "hls://", &nested_url)) {
199  "No nested protocol specified. Specify e.g. hls+http://%s\n",
200  nested_url);
201  ret = AVERROR(EINVAL);
202  goto fail;
203  } else {
204  av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
205  ret = AVERROR(EINVAL);
206  goto fail;
207  }
209  "Using the hls protocol is discouraged, please try using the "
210  "hls demuxer instead. The hls demuxer should be more complete "
211  "and work as well as the protocol implementation. (If not, "
212  "please report it.) To use the demuxer, simply use %s as url.\n",
213  s->playlisturl);
214 
215  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
216  goto fail;
217 
218  if (s->n_segments == 0 && s->n_variants > 0) {
219  int max_bandwidth = 0, maxvar = -1;
220  for (i = 0; i < s->n_variants; i++) {
221  if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {
222  max_bandwidth = s->variants[i]->bandwidth;
223  maxvar = i;
224  }
225  }
226  av_strlcpy(s->playlisturl, s->variants[maxvar]->url,
227  sizeof(s->playlisturl));
228  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
229  goto fail;
230  }
231 
232  if (s->n_segments == 0) {
233  av_log(h, AV_LOG_WARNING, "Empty playlist\n");
234  ret = AVERROR(EIO);
235  goto fail;
236  }
237  s->cur_seq_no = s->start_seq_no;
238  if (!s->finished && s->n_segments >= 3)
239  s->cur_seq_no = s->start_seq_no + s->n_segments - 3;
240 
241  return 0;
242 
243 fail:
244  hls_close(h);
245  return ret;
246 }
247 
248 static int hls_read(URLContext *h, uint8_t *buf, int size)
249 {
250  HLSContext *s = h->priv_data;
251  const char *url;
252  int ret;
253  int64_t reload_interval;
254 
255 start:
256  if (s->seg_hd) {
257  ret = ffurl_read(s->seg_hd, buf, size);
258  if (ret > 0)
259  return ret;
260  }
261  if (s->seg_hd) {
262  ffurl_closep(&s->seg_hd);
263  s->cur_seq_no++;
264  }
265  reload_interval = s->n_segments > 0 ?
266  s->segments[s->n_segments - 1]->duration :
267  s->target_duration;
268 retry:
269  if (!s->finished) {
270  int64_t now = av_gettime_relative();
271  if (now - s->last_load_time >= reload_interval) {
272  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
273  return ret;
274  /* If we need to reload the playlist again below (if
275  * there's still no more segments), switch to a reload
276  * interval of half the target duration. */
277  reload_interval = s->target_duration / 2;
278  }
279  }
280  if (s->cur_seq_no < s->start_seq_no) {
282  "skipping %d segments ahead, expired from playlist\n",
283  s->start_seq_no - s->cur_seq_no);
284  s->cur_seq_no = s->start_seq_no;
285  }
286  if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
287  if (s->finished)
288  return AVERROR_EOF;
289  while (av_gettime_relative() - s->last_load_time < reload_interval) {
290  if (ff_check_interrupt(&h->interrupt_callback))
291  return AVERROR_EXIT;
292  av_usleep(100*1000);
293  }
294  goto retry;
295  }
296  url = s->segments[s->cur_seq_no - s->start_seq_no]->url;
297  av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
299  &h->interrupt_callback, NULL,
300  h->protocol_whitelist, h->protocol_blacklist, h);
301  if (ret < 0) {
302  if (ff_check_interrupt(&h->interrupt_callback))
303  return AVERROR_EXIT;
304  av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url);
305  s->cur_seq_no++;
306  goto retry;
307  }
308  goto start;
309 }
310 
312  .name = "hls",
313  .url_open = hls_open,
314  .url_read = hls_read,
315  .url_close = hls_close,
317  .priv_data_size = sizeof(HLSContext),
318 };
hls_open
static int hls_open(URLContext *h, const char *uri, int flags)
Definition: hlsproto.c:184
ff_get_chomp_line
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:786
av_gettime_relative
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition: time.c:56
ffio_open_whitelist
int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist)
Definition: avio.c:471
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
HLSContext::cur_seq_no
int cur_seq_no
Definition: hlsproto.c:66
AVERROR
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
HLSContext::n_segments
int n_segments
Definition: hlsproto.c:62
HLSContext::n_variants
int n_variants
Definition: hls.c:208
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
URLProtocol
Definition: url.h:51
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:30
segment::duration
int64_t duration
Definition: hls.c:78
fail
#define fail()
Definition: checkasm.h:179
variant
Definition: hls.c:193
dynarray_add
#define dynarray_add(tab, nb_ptr, elem)
Definition: internal.h:448
ff_check_interrupt
int ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
Definition: avio.c:854
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
ff_hls_protocol
const URLProtocol ff_hls_protocol
Definition: hlsproto.c:311
duration
int64_t duration
Definition: movenc.c:65
ffurl_open_whitelist
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:362
variant_info
Definition: hls.c:340
hls_close
static int hls_close(URLContext *h)
Definition: hlsproto.c:174
s
#define s(width, name)
Definition: cbs_vp9.c:198
info
MIPS optimizations info
Definition: mips.txt:2
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:618
handle_variant_args
static void handle_variant_args(struct variant_info *info, const char *key, int key_len, char **dest, int *dest_len)
Definition: hlsproto.c:93
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
HLSContext::last_load_time
int64_t last_load_time
Definition: hlsproto.c:68
free_segment_list
static void free_segment_list(HLSContext *s)
Definition: hlsproto.c:71
hls_read
static int hls_read(URLContext *h, uint8_t *buf, int size)
Definition: hlsproto.c:248
key
const char * key
Definition: hwcontext_opencl.c:189
av_usleep
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition: time.c:84
internal.h
variant::url
char url[MAX_URL_SIZE]
Definition: hlsproto.c:54
NULL
#define NULL
Definition: coverity.c:32
HLSContext::variants
struct variant ** variants
Definition: hls.c:209
parse_playlist
static int parse_playlist(URLContext *h, const char *url)
Definition: hlsproto.c:102
time.h
HLSContext::segments
struct segment ** segments
Definition: hlsproto.c:63
HLSContext::finished
int finished
Definition: hlsproto.c:61
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
HLSContext::seg_hd
URLContext * seg_hd
Definition: hlsproto.c:67
HLSContext::playlisturl
char playlisturl[MAX_URL_SIZE]
Definition: hlsproto.c:58
URL_PROTOCOL_FLAG_NESTED_SCHEME
#define URL_PROTOCOL_FLAG_NESTED_SCHEME
Definition: url.h:32
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:52
line
Definition: graph2dot.c:48
av_strstart
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:36
URLContext
Definition: url.h:35
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
avio_internal.h
AV_TIME_BASE
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
HLSContext
Definition: hls.c:205
url.h
ffurl_closep
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:588
ret
ret
Definition: filter_design.txt:187
MAX_URL_SIZE
#define MAX_URL_SIZE
Definition: internal.h:30
HLSContext::start_seq_no
int start_seq_no
Definition: hlsproto.c:60
ff_parse_key_val_cb
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:576
variant::bandwidth
int bandwidth
Definition: hls.c:194
HLSContext::target_duration
int64_t target_duration
Definition: hlsproto.c:59
segment
Definition: hls.c:77
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:617
mem.h
free_variant_list
static void free_variant_list(HLSContext *s)
Definition: hlsproto.c:80
ff_make_absolute_url
int 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:321
variant_info::bandwidth
char bandwidth[20]
Definition: hls.c:341
segment::url
char * url
Definition: hls.c:81
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: avio.c:616
av_strlcpy
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:85
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
h
h
Definition: vp9dsp_template.c:2038
AVERROR_EXIT
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition: error.h:58
avstring.h
ff_parse_key_value
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:505
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:346
ffurl_read
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.
Definition: url.h:181