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/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 {
60  int64_t target_duration;
62  int finished;
64  struct segment **segments;
65  int n_variants;
66  struct variant **variants;
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 
113  &h->interrupt_callback, NULL,
114  h->protocol_whitelist, h->protocol_blacklist)) < 0)
115  return ret;
116 
117  ff_get_chomp_line(in, line, sizeof(line));
118  if (strcmp(line, "#EXTM3U")) {
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  }
168  s->last_load_time = av_gettime_relative();
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_closep(&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)) {
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_closep(&s->seg_hd);
264  s->cur_seq_no++;
265  }
266  reload_interval = s->n_segments > 0 ?
267  s->segments[s->n_segments - 1]->duration :
268  s->target_duration;
269 retry:
270  if (!s->finished) {
271  int64_t now = av_gettime_relative();
272  if (now - s->last_load_time >= reload_interval) {
273  if ((ret = parse_playlist(h, s->playlisturl)) < 0)
274  return ret;
275  /* If we need to reload the playlist again below (if
276  * there's still no more segments), switch to a reload
277  * interval of half the target duration. */
278  reload_interval = s->target_duration / 2;
279  }
280  }
281  if (s->cur_seq_no < s->start_seq_no) {
283  "skipping %d segments ahead, expired from playlist\n",
284  s->start_seq_no - s->cur_seq_no);
285  s->cur_seq_no = s->start_seq_no;
286  }
287  if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
288  if (s->finished)
289  return AVERROR_EOF;
290  while (av_gettime_relative() - s->last_load_time < reload_interval) {
291  if (ff_check_interrupt(&h->interrupt_callback))
292  return AVERROR_EXIT;
293  av_usleep(100*1000);
294  }
295  goto retry;
296  }
297  url = s->segments[s->cur_seq_no - s->start_seq_no]->url;
298  av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
300  &h->interrupt_callback, NULL,
301  h->protocol_whitelist, h->protocol_blacklist, h);
302  if (ret < 0) {
303  if (ff_check_interrupt(&h->interrupt_callback))
304  return AVERROR_EXIT;
305  av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url);
306  s->cur_seq_no++;
307  goto retry;
308  }
309  goto start;
310 }
311 
313  .name = "hls",
314  .url_open = hls_open,
315  .url_read = hls_read,
316  .url_close = hls_close,
318  .priv_data_size = sizeof(HLSContext),
319 };
hls_open
static int hls_open(URLContext *h, const char *uri, int flags)
Definition: hlsproto.c:185
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:806
av_gettime_relative
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition: time.c:56
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
HLSContext::cur_seq_no
int cur_seq_no
Definition: hlsproto.c:67
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:63
HLSContext::n_variants
int n_variants
Definition: hls.c:195
avio_close
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:1169
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:55
ffio_open_whitelist
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:1142
URLProtocol
Definition: url.h:54
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
segment::duration
int64_t duration
Definition: hls.c:69
fail
#define fail()
Definition: checkasm.h:133
variant
Definition: hls.c:180
dynarray_add
#define dynarray_add(tab, nb_ptr, elem)
Definition: internal.h:355
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:661
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
ff_hls_protocol
const URLProtocol ff_hls_protocol
Definition: hlsproto.c:312
duration
int64_t duration
Definition: movenc.c:64
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:309
variant_info
Definition: hls.c:322
hls_close
static int hls_close(URLContext *h)
Definition: hlsproto.c:175
s
#define s(width, name)
Definition: cbs_vp9.c:257
info
MIPS optimizations info
Definition: mips.txt:2
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
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:94
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:215
HLSContext::last_load_time
int64_t last_load_time
Definition: hlsproto.c:69
free_segment_list
static void free_segment_list(HLSContext *s)
Definition: hlsproto.c:72
hls_read
static int hls_read(URLContext *h, uint8_t *buf, int size)
Definition: hlsproto.c:249
key
const char * key
Definition: hwcontext_opencl.c:168
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:55
NULL
#define NULL
Definition: coverity.c:32
HLSContext::variants
struct variant ** variants
Definition: hls.c:196
parse_playlist
static int parse_playlist(URLContext *h, const char *url)
Definition: hlsproto.c:103
time.h
HLSContext::segments
struct segment ** segments
Definition: hlsproto.c:64
HLSContext::finished
int finished
Definition: hlsproto.c:62
AVIOContext
Bytestream IO Context.
Definition: avio.h:161
HLSContext::seg_hd
URLContext * seg_hd
Definition: hlsproto.c:68
HLSContext::playlisturl
char playlisturl[MAX_URL_SIZE]
Definition: hlsproto.c:59
URL_PROTOCOL_FLAG_NESTED_SCHEME
#define URL_PROTOCOL_FLAG_NESTED_SCHEME
Definition: url.h:33
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:55
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:34
in
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
Definition: audio_convert.c:326
i
int i
Definition: input.c:407
URLContext
Definition: url.h:38
avio_internal.h
AV_TIME_BASE
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
HLSContext
Definition: hls.c:192
url.h
uint8_t
uint8_t
Definition: audio_convert.c:194
version.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:441
ret
ret
Definition: filter_design.txt:187
avformat.h
MAX_URL_SIZE
#define MAX_URL_SIZE
Definition: internal.h:30
ffurl_read
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:404
HLSContext::start_seq_no
int start_seq_no
Definition: hlsproto.c:61
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:511
variant::bandwidth
int bandwidth
Definition: hls.c:181
HLSContext::target_duration
int64_t target_duration
Definition: hlsproto.c:60
segment
Definition: hls.c:68
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
free_variant_list
static void free_variant_list(HLSContext *s)
Definition: hlsproto.c:81
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:319
variant_info::bandwidth
char bandwidth[20]
Definition: hls.c:323
segment::url
char * url
Definition: hls.c:72
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:561
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:83
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
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:56
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:4975
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:364