Libav
hls.c
Go to the documentation of this file.
1 /*
2  * Apple HTTP Live Streaming demuxer
3  * Copyright (c) 2010 Martin Storsjo
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
28 #include "libavutil/avstring.h"
29 #include "libavutil/intreadwrite.h"
30 #include "libavutil/mathematics.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/dict.h"
33 #include "libavutil/time.h"
34 #include "avformat.h"
35 #include "internal.h"
36 #include "avio_internal.h"
37 
38 #define INITIAL_BUFFER_SIZE 32768
39 
40 /*
41  * An apple http stream consists of a playlist with media segment files,
42  * played sequentially. There may be several playlists with the same
43  * video content, in different bandwidth variants, that are played in
44  * parallel (preferably only one bandwidth variant at a time). In this case,
45  * the user supplied the url to a main playlist that only lists the variant
46  * playlists.
47  *
48  * If the main playlist doesn't point at any variants, we still create
49  * one anonymous toplevel variant for this, to maintain the structure.
50  */
51 
52 enum KeyType {
55 };
56 
57 struct segment {
58  int64_t duration;
62  uint8_t iv[16];
63 };
64 
65 /*
66  * Each variant has its own demuxer. If it currently is active,
67  * it has an open AVIOContext too, and potentially an AVPacket
68  * containing the next packet from this stream.
69  */
70 struct variant {
71  int bandwidth;
77  int index;
81 
82  int finished;
83  int64_t target_duration;
86  struct segment **segments;
87  int needed, cur_needed;
89  int64_t last_load_time;
90 
91  char key_url[MAX_URL_SIZE];
92  uint8_t key[16];
93 };
94 
95 typedef struct HLSContext {
98  struct variant **variants;
103  int64_t seek_timestamp;
107 } HLSContext;
108 
109 static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
110 {
111  int len = ff_get_line(s, buf, maxlen);
112  while (len > 0 && av_isspace(buf[len - 1]))
113  buf[--len] = '\0';
114  return len;
115 }
116 
117 static void free_segment_list(struct variant *var)
118 {
119  int i;
120  for (i = 0; i < var->n_segments; i++)
121  av_free(var->segments[i]);
122  av_freep(&var->segments);
123  var->n_segments = 0;
124 }
125 
127 {
128  int i;
129  for (i = 0; i < c->n_variants; i++) {
130  struct variant *var = c->variants[i];
131  free_segment_list(var);
132  av_packet_unref(&var->pkt);
133  av_free(var->pb.buffer);
134  if (var->input)
135  ff_format_io_close(c->ctx, &var->input);
136  if (var->ctx) {
137  var->ctx->pb = NULL;
138  avformat_close_input(&var->ctx);
139  }
140  av_free(var);
141  }
142  av_freep(&c->variants);
143  c->n_variants = 0;
144 }
145 
146 /*
147  * Used to reset a statically allocated AVPacket to a clean slate,
148  * containing no data.
149  */
150 static void reset_packet(AVPacket *pkt)
151 {
152  av_init_packet(pkt);
153  pkt->data = NULL;
154 }
155 
156 static struct variant *new_variant(HLSContext *c, int bandwidth,
157  const char *url, const char *base)
158 {
159  struct variant *var = av_mallocz(sizeof(struct variant));
160  if (!var)
161  return NULL;
162  reset_packet(&var->pkt);
163  var->bandwidth = bandwidth;
164  ff_make_absolute_url(var->url, sizeof(var->url), base, url);
165  dynarray_add(&c->variants, &c->n_variants, var);
166  return var;
167 }
168 
169 struct variant_info {
170  char bandwidth[20];
171 };
172 
173 static void handle_variant_args(struct variant_info *info, const char *key,
174  int key_len, char **dest, int *dest_len)
175 {
176  if (!strncmp(key, "BANDWIDTH=", key_len)) {
177  *dest = info->bandwidth;
178  *dest_len = sizeof(info->bandwidth);
179  }
180 }
181 
182 struct key_info {
183  char uri[MAX_URL_SIZE];
184  char method[10];
185  char iv[35];
186 };
187 
188 static void handle_key_args(struct key_info *info, const char *key,
189  int key_len, char **dest, int *dest_len)
190 {
191  if (!strncmp(key, "METHOD=", key_len)) {
192  *dest = info->method;
193  *dest_len = sizeof(info->method);
194  } else if (!strncmp(key, "URI=", key_len)) {
195  *dest = info->uri;
196  *dest_len = sizeof(info->uri);
197  } else if (!strncmp(key, "IV=", key_len)) {
198  *dest = info->iv;
199  *dest_len = sizeof(info->iv);
200  }
201 }
202 
203 static int open_in(HLSContext *c, AVIOContext **in, const char *url)
204 {
205  AVDictionary *tmp = NULL;
206  int ret;
207 
208  av_dict_copy(&tmp, c->avio_opts, 0);
209 
210  ret = c->ctx->io_open(c->ctx, in, url, AVIO_FLAG_READ, &tmp);
211 
212  av_dict_free(&tmp);
213  return ret;
214 }
215 
216 static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
217  const AVDictionary *opts)
218 {
219  AVDictionary *tmp = NULL;
220  int ret;
221 
222  av_dict_copy(&tmp, opts, 0);
223 
224  ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
225 
226  av_dict_free(&tmp);
227 
228  return ret;
229 }
230 
231 static int parse_playlist(HLSContext *c, const char *url,
232  struct variant *var, AVIOContext *in)
233 {
234  int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
235  int64_t duration = 0;
236  enum KeyType key_type = KEY_NONE;
237  uint8_t iv[16] = "";
238  int has_iv = 0;
239  char key[MAX_URL_SIZE] = "";
240  char line[1024];
241  const char *ptr;
242  int close_in = 0;
243  uint8_t *new_url = NULL;
244 
245  if (!in) {
246  ret = open_in(c, &in, url);
247  if (ret < 0)
248  return ret;
249  close_in = 1;
250  }
251 
252  if (av_opt_get(in, "location", AV_OPT_SEARCH_CHILDREN, &new_url) >= 0)
253  url = new_url;
254 
255  read_chomp_line(in, line, sizeof(line));
256  if (strcmp(line, "#EXTM3U")) {
257  ret = AVERROR_INVALIDDATA;
258  goto fail;
259  }
260 
261  if (var) {
262  free_segment_list(var);
263  var->finished = 0;
264  }
265  while (!in->eof_reached) {
266  read_chomp_line(in, line, sizeof(line));
267  if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
268  struct variant_info info = {{0}};
269  is_variant = 1;
271  &info);
272  bandwidth = atoi(info.bandwidth);
273  } else if (av_strstart(line, "#EXT-X-KEY:", &ptr)) {
274  struct key_info info = {{0}};
276  &info);
277  key_type = KEY_NONE;
278  has_iv = 0;
279  if (!strcmp(info.method, "AES-128"))
280  key_type = KEY_AES_128;
281  if (!strncmp(info.iv, "0x", 2) || !strncmp(info.iv, "0X", 2)) {
282  ff_hex_to_data(iv, info.iv + 2);
283  has_iv = 1;
284  }
285  av_strlcpy(key, info.uri, sizeof(key));
286  } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
287  if (!var) {
288  var = new_variant(c, 0, url, NULL);
289  if (!var) {
290  ret = AVERROR(ENOMEM);
291  goto fail;
292  }
293  }
294  var->target_duration = atoi(ptr) * AV_TIME_BASE;
295  } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
296  if (!var) {
297  var = new_variant(c, 0, url, NULL);
298  if (!var) {
299  ret = AVERROR(ENOMEM);
300  goto fail;
301  }
302  }
303  var->start_seq_no = atoi(ptr);
304  } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
305  if (var)
306  var->finished = 1;
307  } else if (av_strstart(line, "#EXTINF:", &ptr)) {
308  is_segment = 1;
309  duration = atof(ptr) * AV_TIME_BASE;
310  } else if (av_strstart(line, "#", NULL)) {
311  continue;
312  } else if (line[0]) {
313  if (is_variant) {
314  if (!new_variant(c, bandwidth, line, url)) {
315  ret = AVERROR(ENOMEM);
316  goto fail;
317  }
318  is_variant = 0;
319  bandwidth = 0;
320  }
321  if (is_segment) {
322  struct segment *seg;
323  if (!var) {
324  var = new_variant(c, 0, url, NULL);
325  if (!var) {
326  ret = AVERROR(ENOMEM);
327  goto fail;
328  }
329  }
330  seg = av_malloc(sizeof(struct segment));
331  if (!seg) {
332  ret = AVERROR(ENOMEM);
333  goto fail;
334  }
335  seg->duration = duration;
336  seg->key_type = key_type;
337  if (has_iv) {
338  memcpy(seg->iv, iv, sizeof(iv));
339  } else {
340  int seq = var->start_seq_no + var->n_segments;
341  memset(seg->iv, 0, sizeof(seg->iv));
342  AV_WB32(seg->iv + 12, seq);
343  }
344  ff_make_absolute_url(seg->key, sizeof(seg->key), url, key);
345  ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
346  dynarray_add(&var->segments, &var->n_segments, seg);
347  is_segment = 0;
348  }
349  }
350  }
351  if (var)
353 
354 fail:
355  av_free(new_url);
356  if (close_in)
357  ff_format_io_close(c->ctx, &in);
358  return ret;
359 }
360 
361 static int open_input(struct variant *var)
362 {
363  HLSContext *c = var->parent->priv_data;
364  struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
365  if (seg->key_type == KEY_NONE) {
366  return open_url(var->parent, &var->input, seg->url, c->avio_opts);
367  } else if (seg->key_type == KEY_AES_128) {
369  char iv[33], key[33], url[MAX_URL_SIZE];
370  int ret;
371  if (strcmp(seg->key, var->key_url)) {
372  AVIOContext *pb;
373  if (open_url(var->parent, &pb, seg->key, c->avio_opts) == 0) {
374  ret = avio_read(pb, var->key, sizeof(var->key));
375  if (ret != sizeof(var->key)) {
376  av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
377  seg->key);
378  }
379  ff_format_io_close(var->parent, &pb);
380  } else {
381  av_log(NULL, AV_LOG_ERROR, "Unable to open key file %s\n",
382  seg->key);
383  }
384  av_strlcpy(var->key_url, seg->key, sizeof(var->key_url));
385  }
386  ff_data_to_hex(iv, seg->iv, sizeof(seg->iv), 0);
387  ff_data_to_hex(key, var->key, sizeof(var->key), 0);
388  iv[32] = key[32] = '\0';
389  if (strstr(seg->url, "://"))
390  snprintf(url, sizeof(url), "crypto+%s", seg->url);
391  else
392  snprintf(url, sizeof(url), "crypto:%s", seg->url);
393 
394  av_dict_copy(&opts, c->avio_opts, 0);
395  av_dict_set(&opts, "key", key, 0);
396  av_dict_set(&opts, "iv", iv, 0);
397 
398  ret = open_url(var->parent, &var->input, url, opts);
399  av_dict_free(&opts);
400  return ret;
401  }
402  return AVERROR(ENOSYS);
403 }
404 
405 static int read_data(void *opaque, uint8_t *buf, int buf_size)
406 {
407  struct variant *v = opaque;
408  HLSContext *c = v->parent->priv_data;
409  int ret, i;
410 
411 restart:
412  if (!v->input) {
413  /* If this is a live stream and the reload interval has elapsed since
414  * the last playlist reload, reload the variant playlists now. */
415  int64_t reload_interval = v->n_segments > 0 ?
416  v->segments[v->n_segments - 1]->duration :
417  v->target_duration;
418 
419 reload:
420  if (!v->finished &&
421  av_gettime_relative() - v->last_load_time >= reload_interval) {
422  if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
423  return ret;
424  /* If we need to reload the playlist again below (if
425  * there's still no more segments), switch to a reload
426  * interval of half the target duration. */
427  reload_interval = v->target_duration / 2;
428  }
429  if (v->cur_seq_no < v->start_seq_no) {
431  "skipping %d segments ahead, expired from playlists\n",
432  v->start_seq_no - v->cur_seq_no);
433  v->cur_seq_no = v->start_seq_no;
434  }
435  if (v->cur_seq_no >= v->start_seq_no + v->n_segments) {
436  if (v->finished)
437  return AVERROR_EOF;
438  while (av_gettime_relative() - v->last_load_time < reload_interval) {
440  return AVERROR_EXIT;
441  av_usleep(100*1000);
442  }
443  /* Enough time has elapsed since the last reload */
444  goto reload;
445  }
446 
447  ret = open_input(v);
448  if (ret < 0)
449  return ret;
450  }
451  ret = avio_read(v->input, buf, buf_size);
452  if (ret > 0)
453  return ret;
454  ff_format_io_close(c->ctx, &v->input);
455  v->cur_seq_no++;
456 
457  c->end_of_segment = 1;
458  c->cur_seq_no = v->cur_seq_no;
459 
460  if (v->ctx && v->ctx->nb_streams &&
461  v->parent->nb_streams >= v->stream_offset + v->ctx->nb_streams) {
462  v->needed = 0;
463  for (i = v->stream_offset; i < v->stream_offset + v->ctx->nb_streams;
464  i++) {
465  if (v->parent->streams[i]->discard < AVDISCARD_ALL)
466  v->needed = 1;
467  }
468  }
469  if (!v->needed) {
470  av_log(v->parent, AV_LOG_INFO, "No longer receiving variant %d\n",
471  v->index);
472  return AVERROR_EOF;
473  }
474  goto restart;
475 }
476 
478 {
479  HLSContext *c = s->priv_data;
480  static const char *opts[] = { "headers", "user_agent", NULL };
481  const char **opt = opts;
482  uint8_t *buf;
483  int ret = 0;
484 
485  while (*opt) {
486  if (av_opt_get(s->pb, *opt, AV_OPT_SEARCH_CHILDREN, &buf) >= 0) {
487  ret = av_dict_set(&c->avio_opts, *opt, buf,
489  if (ret < 0)
490  return ret;
491  }
492  opt++;
493  }
494 
495  return ret;
496 }
497 
498 static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url,
499  int flags, AVDictionary **opts)
500 {
501  av_log(s, AV_LOG_ERROR,
502  "A HLS playlist item '%s' referred to an external file '%s'. "
503  "Opening this file was forbidden for security reasons\n",
504  s->filename, url);
505  return AVERROR(EPERM);
506 }
507 
509 {
510  HLSContext *c = s->priv_data;
511  int ret = 0, i, j, stream_offset = 0;
512 
513  c->ctx = s;
515 
516  if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
517  goto fail;
518 
519  if ((ret = save_avio_options(s)) < 0)
520  goto fail;
521 
522  if (c->n_variants == 0) {
523  av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
524  ret = AVERROR_EOF;
525  goto fail;
526  }
527  /* If the playlist only contained variants, parse each individual
528  * variant playlist. */
529  if (c->n_variants > 1 || c->variants[0]->n_segments == 0) {
530  for (i = 0; i < c->n_variants; i++) {
531  struct variant *v = c->variants[i];
532  if ((ret = parse_playlist(c, v->url, v, NULL)) < 0)
533  goto fail;
534  }
535  }
536 
537  if (c->variants[0]->n_segments == 0) {
538  av_log(NULL, AV_LOG_WARNING, "Empty playlist\n");
539  ret = AVERROR_EOF;
540  goto fail;
541  }
542 
543  /* If this isn't a live stream, calculate the total duration of the
544  * stream. */
545  if (c->variants[0]->finished) {
546  int64_t duration = 0;
547  for (i = 0; i < c->variants[0]->n_segments; i++)
548  duration += c->variants[0]->segments[i]->duration;
549  s->duration = duration;
550  }
551 
552  /* Open the demuxer for each variant */
553  for (i = 0; i < c->n_variants; i++) {
554  struct variant *v = c->variants[i];
555  AVInputFormat *in_fmt = NULL;
556  char bitrate_str[20];
557  AVProgram *program;
558 
559  if (v->n_segments == 0)
560  continue;
561 
562  if (!(v->ctx = avformat_alloc_context())) {
563  ret = AVERROR(ENOMEM);
564  goto fail;
565  }
566 
567  v->index = i;
568  v->needed = 1;
569  v->parent = s;
570 
571  /* If this is a live stream with more than 3 segments, start at the
572  * third last segment. */
573  v->cur_seq_no = v->start_seq_no;
574  if (!v->finished && v->n_segments > 3)
575  v->cur_seq_no = v->start_seq_no + v->n_segments - 3;
576 
579  read_data, NULL, NULL);
580  v->pb.seekable = 0;
581  ret = av_probe_input_buffer(&v->pb, &in_fmt, v->segments[0]->url,
582  NULL, 0, 0);
583  if (ret < 0) {
584  /* Free the ctx - it isn't initialized properly at this point,
585  * so avformat_close_input shouldn't be called. If
586  * avformat_open_input fails below, it frees and zeros the
587  * context, so it doesn't need any special treatment like this. */
589  v->ctx = NULL;
590  goto fail;
591  }
592  v->ctx->pb = &v->pb;
593  v->ctx->io_open = nested_io_open;
595  ret = avformat_open_input(&v->ctx, v->segments[0]->url, in_fmt, NULL);
596  if (ret < 0)
597  goto fail;
598 
601  if (ret < 0)
602  goto fail;
603  snprintf(bitrate_str, sizeof(bitrate_str), "%d", v->bandwidth);
604 
605  program = av_new_program(s, i);
606  if (!program)
607  goto fail;
608  av_dict_set(&program->metadata, "variant_bitrate", bitrate_str, 0);
609 
610  /* Create new AVStreams for each stream in this variant */
611  for (j = 0; j < v->ctx->nb_streams; j++) {
613  AVStream *ist = v->ctx->streams[j];
614  if (!st) {
615  ret = AVERROR(ENOMEM);
616  goto fail;
617  }
618  ff_program_add_stream_index(s, i, stream_offset + j);
619  st->id = i;
622  if (v->bandwidth)
623  av_dict_set(&st->metadata, "variant_bitrate", bitrate_str,
624  0);
625  }
626  stream_offset += v->ctx->nb_streams;
627  }
628 
629  c->first_packet = 1;
632 
633  return 0;
634 fail:
636  return ret;
637 }
638 
639 static int recheck_discard_flags(AVFormatContext *s, int first)
640 {
641  HLSContext *c = s->priv_data;
642  int i, changed = 0;
643 
644  /* Check if any new streams are needed */
645  for (i = 0; i < c->n_variants; i++)
646  c->variants[i]->cur_needed = 0;;
647 
648  for (i = 0; i < s->nb_streams; i++) {
649  AVStream *st = s->streams[i];
650  struct variant *var = c->variants[s->streams[i]->id];
651  if (st->discard < AVDISCARD_ALL)
652  var->cur_needed = 1;
653  }
654  for (i = 0; i < c->n_variants; i++) {
655  struct variant *v = c->variants[i];
656  if (v->cur_needed && !v->needed) {
657  v->needed = 1;
658  changed = 1;
659  v->cur_seq_no = c->cur_seq_no;
660  v->pb.eof_reached = 0;
661  av_log(s, AV_LOG_INFO, "Now receiving variant %d\n", i);
662  } else if (first && !v->cur_needed && v->needed) {
663  if (v->input)
664  ff_format_io_close(s, &v->input);
665  v->needed = 0;
666  changed = 1;
667  av_log(s, AV_LOG_INFO, "No longer receiving variant %d\n", i);
668  }
669  }
670  return changed;
671 }
672 
674 {
675  HLSContext *c = s->priv_data;
676  int ret, i, minvariant = -1;
677 
678  if (c->first_packet) {
679  recheck_discard_flags(s, 1);
680  c->first_packet = 0;
681  }
682 
683 start:
684  c->end_of_segment = 0;
685  for (i = 0; i < c->n_variants; i++) {
686  struct variant *var = c->variants[i];
687  /* Make sure we've got one buffered packet from each open variant
688  * stream */
689  if (var->needed && !var->pkt.data) {
690  while (1) {
691  int64_t ts_diff;
692  AVStream *st;
693  ret = av_read_frame(var->ctx, &var->pkt);
694  if (ret < 0) {
695  if (!var->pb.eof_reached)
696  return ret;
697  reset_packet(&var->pkt);
698  break;
699  } else {
700  if (c->first_timestamp == AV_NOPTS_VALUE &&
701  var->pkt.dts != AV_NOPTS_VALUE)
703  var->ctx->streams[var->pkt.stream_index]->time_base,
705  }
706 
707  if (c->seek_timestamp == AV_NOPTS_VALUE)
708  break;
709 
710  if (var->pkt.dts == AV_NOPTS_VALUE) {
712  break;
713  }
714 
715  st = var->ctx->streams[var->pkt.stream_index];
716  ts_diff = av_rescale_rnd(var->pkt.dts, AV_TIME_BASE,
717  st->time_base.den, AV_ROUND_DOWN) -
718  c->seek_timestamp;
719  if (ts_diff >= 0 && (c->seek_flags & AVSEEK_FLAG_ANY ||
720  var->pkt.flags & AV_PKT_FLAG_KEY)) {
722  break;
723  }
724  av_packet_unref(&var->pkt);
725  reset_packet(&var->pkt);
726  }
727  }
728  /* Check if this stream still is on an earlier segment number, or
729  * has the packet with the lowest dts */
730  if (var->pkt.data) {
731  struct variant *minvar = minvariant < 0 ?
732  NULL : c->variants[minvariant];
733  if (minvariant < 0 || var->cur_seq_no < minvar->cur_seq_no) {
734  minvariant = i;
735  } else if (var->cur_seq_no == minvar->cur_seq_no) {
736  int64_t dts = var->pkt.dts;
737  int64_t mindts = minvar->pkt.dts;
738  AVStream *st = var->ctx->streams[var->pkt.stream_index];
739  AVStream *minst = minvar->ctx->streams[minvar->pkt.stream_index];
740 
741  if (dts == AV_NOPTS_VALUE) {
742  minvariant = i;
743  } else if (mindts != AV_NOPTS_VALUE) {
744  if (st->start_time != AV_NOPTS_VALUE)
745  dts -= st->start_time;
746  if (minst->start_time != AV_NOPTS_VALUE)
747  mindts -= minst->start_time;
748 
749  if (av_compare_ts(dts, st->time_base,
750  mindts, minst->time_base) < 0)
751  minvariant = i;
752  }
753  }
754  }
755  }
756  if (c->end_of_segment) {
757  if (recheck_discard_flags(s, 0))
758  goto start;
759  }
760  /* If we got a packet, return it */
761  if (minvariant >= 0) {
762  *pkt = c->variants[minvariant]->pkt;
763  pkt->stream_index += c->variants[minvariant]->stream_offset;
764  reset_packet(&c->variants[minvariant]->pkt);
765  return 0;
766  }
767  return AVERROR_EOF;
768 }
769 
771 {
772  HLSContext *c = s->priv_data;
773 
775 
776  av_dict_free(&c->avio_opts);
777 
778  return 0;
779 }
780 
781 static int hls_read_seek(AVFormatContext *s, int stream_index,
782  int64_t timestamp, int flags)
783 {
784  HLSContext *c = s->priv_data;
785  int i, j, ret;
786 
787  if ((flags & AVSEEK_FLAG_BYTE) || !c->variants[0]->finished)
788  return AVERROR(ENOSYS);
789 
790  c->seek_flags = flags;
791  c->seek_timestamp = stream_index < 0 ? timestamp :
792  av_rescale_rnd(timestamp, AV_TIME_BASE,
793  s->streams[stream_index]->time_base.den,
794  flags & AVSEEK_FLAG_BACKWARD ?
796  timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE, stream_index >= 0 ?
797  s->streams[stream_index]->time_base.den :
799  AV_ROUND_DOWN : AV_ROUND_UP);
800  if (s->duration < c->seek_timestamp) {
802  return AVERROR(EIO);
803  }
804 
805  ret = AVERROR(EIO);
806  for (i = 0; i < c->n_variants; i++) {
807  /* Reset reading */
808  struct variant *var = c->variants[i];
809  int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
810  0 : c->first_timestamp;
811  if (var->input)
812  ff_format_io_close(s, &var->input);
813  av_packet_unref(&var->pkt);
814  reset_packet(&var->pkt);
815  var->pb.eof_reached = 0;
816  /* Clear any buffered data */
817  var->pb.buf_end = var->pb.buf_ptr = var->pb.buffer;
818  /* Reset the pos, to let the mpegts demuxer know we've seeked. */
819  var->pb.pos = 0;
820 
821  /* Locate the segment that contains the target timestamp */
822  for (j = 0; j < var->n_segments; j++) {
823  if (timestamp >= pos &&
824  timestamp < pos + var->segments[j]->duration) {
825  var->cur_seq_no = var->start_seq_no + j;
826  ret = 0;
827  break;
828  }
829  pos += var->segments[j]->duration;
830  }
831  if (ret)
833  }
834  return ret;
835 }
836 
837 static int hls_probe(AVProbeData *p)
838 {
839  /* Require #EXTM3U at the start, and either one of the ones below
840  * somewhere for a proper match. */
841  if (strncmp(p->buf, "#EXTM3U", 7))
842  return 0;
843  if (strstr(p->buf, "#EXT-X-STREAM-INF:") ||
844  strstr(p->buf, "#EXT-X-TARGETDURATION:") ||
845  strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:"))
846  return AVPROBE_SCORE_MAX;
847  return 0;
848 }
849 
851  .name = "hls,applehttp",
852  .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
853  .priv_data_size = sizeof(HLSContext),
859 };
#define AVSEEK_FLAG_BACKWARD
Definition: avformat.h:1657
int(* io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options)
A callback for opening new IO streams.
Definition: avformat.h:1270
int needed
Definition: hls.c:87
void * av_malloc(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:62
void ff_make_absolute_url(char *buf, int size, const char *base, const char *rel)
Definition: url.c:80
Bytestream IO Context.
Definition: avio.h:104
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:54
int bandwidth
Definition: hls.c:71
int cur_needed
Definition: hls.c:87
AVIOInterruptCB interrupt_callback
Custom interrupt callbacks for the I/O layer.
Definition: avformat.h:1181
static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, const AVDictionary *opts)
Definition: hls.c:216
char key_url[MAX_URL_SIZE]
Definition: hls.c:91
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
Rescale a 64-bit integer with specified rounding.
Definition: mathematics.c:40
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:130
unsigned char * buf_ptr
Current position in the buffer.
Definition: avio.h:120
unsigned char * buf_end
End of the data, may be less than buffer+buffer_size if the read function returned less data than req...
Definition: avio.h:121
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
Open an input stream and read the header.
Definition: utils.c:284
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: utils.c:2986
#define AVSEEK_FLAG_ANY
seek to any frame, even non-keyframes
Definition: avformat.h:1659
static int open_in(HLSContext *c, AVIOContext **in, const char *url)
Definition: hls.c:203
static int read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, int flags)
Definition: libcdio.c:153
int finished
Definition: hls.c:82
int num
numerator
Definition: rational.h:44
av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (%s)\, len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt), use_generic ? ac->func_descr_generic :ac->func_descr)
#define AVIO_FLAG_READ
read-only
Definition: avio.h:368
static av_const int av_isspace(int c)
Locale-independent conversion of ASCII isspace.
Definition: avstring.h:173
unsigned char * buffer
Start of the buffer.
Definition: avio.h:118
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
Copy entries from one AVDictionary struct into another.
Definition: dict.c:189
int n_segments
Definition: hls.c:85
int64_t last_load_time
Definition: hls.c:89
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition: time.c:68
discard all
Definition: avcodec.h:689
char url[MAX_URL_SIZE]
Definition: hls.c:59
int ctx_flags
Flags signalling stream properties.
Definition: avformat.h:989
static void reset_packet(AVPacket *pkt)
Definition: hls.c:150
#define AV_WB32(p, val)
Definition: intreadwrite.h:246
void av_freep(void *arg)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
Definition: mem.c:202
Format I/O context.
Definition: avformat.h:940
#define MAX_URL_SIZE
Definition: internal.h:28
Public dictionary API.
uint8_t iv[16]
Definition: hls.c:62
uint8_t
Round toward +infinity.
Definition: mathematics.h:53
int n_variants
Definition: hls.c:97
int start_seq_no
Definition: hls.c:84
#define AVFMTCTX_NOHEADER
signal that no header is present (streams are added dynamically)
Definition: avformat.h:919
AVOptions.
int id
Format-specific stream ID.
Definition: avformat.h:712
void ff_format_io_close(AVFormatContext *s, AVIOContext **pb)
A wrapper around AVFormatContext.io_close that should be used instead of calling the pointer directly...
Definition: utils.c:3317
AVInputFormat ff_hls_demuxer
Definition: hls.c:850
AVStream * avformat_new_stream(AVFormatContext *s, const AVCodec *c)
Add a new stream to a media file.
Definition: utils.c:2648
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1008
AVFormatContext * avformat_alloc_context(void)
Allocate an AVFormatContext.
Definition: options.c:136
uint8_t * data
Definition: avcodec.h:1346
AVProgram * av_new_program(AVFormatContext *s, int id)
Definition: utils.c:2735
static int read_data(void *opaque, uint8_t *buf, int buf_size)
Definition: hls.c:405
static int flags
Definition: log.c:50
int64_t seek_timestamp
Definition: hls.c:103
#define AVERROR_EOF
End of file.
Definition: error.h:51
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:145
int end_of_segment
Definition: hls.c:100
char method[10]
Definition: hls.c:184
static int open_input(struct variant *var)
Definition: hls.c:361
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:545
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition: avcodec.h:1378
struct variant ** variants
Definition: hls.c:98
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:99
Callback for checking whether to abort blocking functions.
Definition: avio.h:51
struct segment ** segments
Definition: hls.c:86
int index
Definition: hls.c:77
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src)
Copy the contents of src to dst.
Definition: utils.c:2819
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:124
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
Definition: mem.c:190
AVIOInterruptCB * interrupt_callback
Definition: hls.c:105
char bandwidth[20]
Definition: hls.c:170
static struct variant * new_variant(HLSContext *c, int bandwidth, const char *url, const char *base)
Definition: hls.c:156
static void handle_key_args(struct key_info *info, const char *key, int key_len, char **dest, int *dest_len)
Definition: hls.c:188
#define AVERROR(e)
Definition: error.h:43
AVIOContext pb
Definition: hls.c:73
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:148
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values. ...
Definition: dict.c:175
Definition: graph2dot.c:48
void ff_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx)
int first_packet
Definition: hls.c:101
New fields can be added to the end with minor version bumps.
Definition: avformat.h:910
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:81
#define fail()
Definition: checkasm.h:80
AVIOContext * input
Definition: hls.c:75
int flags
A combination of AV_PKT_FLAG values.
Definition: avcodec.h:1352
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b)
Compare 2 timestamps each in its own timebases.
Definition: mathematics.c:104
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:400
Definition: hls.c:57
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:996
char url[MAX_URL_SIZE]
Definition: hls.c:72
AVDictionary * opts
Definition: movenc.c:50
int seekable
A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
Definition: avio.h:153
#define dynarray_add(tab, nb_ptr, elem)
Definition: internal.h:134
char filename[1024]
input or output filename
Definition: avformat.h:1016
static int nested_io_open(AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **opts)
Definition: hls.c:498
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:241
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:242
#define AV_OPT_SEARCH_CHILDREN
Search in possible children of the given object first.
Definition: opt.h:383
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that&#39;s been allocated with av_malloc() and children.
Definition: dict.h:64
static int read_probe(AVProbeData *pd)
Definition: jvdec.c:55
int av_probe_input_buffer(AVIOContext *pb, AVInputFormat **fmt, const char *filename, void *logctx, unsigned int offset, unsigned int max_probe_size)
Probe a bytestream to determine the input format.
Definition: format.c:238
Definition: hls.c:95
static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: hls.c:673
int64_t target_duration
Definition: hls.c:83
Definition: hls.c:182
AVDictionary * metadata
Definition: avformat.h:772
int ff_get_line(AVIOContext *s, char *buf, int maxlen)
Read a whole line of text from AVIOContext.
Definition: aviobuf.c:704
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition: error.h:52
uint8_t * read_buffer
Definition: hls.c:74
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:546
enum KeyType key_type
Definition: hls.c:61
static void free_segment_list(struct variant *var)
Definition: hls.c:117
Stream structure.
Definition: avformat.h:705
AVFormatContext * ctx
Definition: hls.c:96
static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
Definition: hls.c:109
NULL
Definition: eval.c:55
int64_t first_timestamp
Definition: hls.c:102
#define AV_LOG_INFO
Standard information.
Definition: log.h:135
int ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
Definition: avio.c:374
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:247
AVIOContext * pb
I/O context.
Definition: avformat.h:982
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:347
int seek_flags
Definition: hls.c:104
static int hls_read_header(AVFormatContext *s)
Definition: hls.c:508
AVFormatContext * parent
Definition: hls.c:76
static int read_packet(AVFormatContext *ctx, AVPacket *pkt)
Definition: libcdio.c:114
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:68
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)\", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
static int save_avio_options(AVFormatContext *s)
Definition: hls.c:477
#define AVSEEK_FLAG_BYTE
seeking based on position in bytes
Definition: avformat.h:1658
char key[MAX_URL_SIZE]
Definition: hls.c:60
void avformat_free_context(AVFormatContext *s)
Free an AVFormatContext and all its streams.
Definition: utils.c:2594
This structure contains the data a format has to probe a file.
Definition: avformat.h:398
int ff_hex_to_data(uint8_t *data, const char *p)
Parse a string of hexadecimal strings.
Definition: utils.c:2958
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
Return the next frame of a stream.
Definition: utils.c:1021
Round toward -infinity.
Definition: mathematics.h:52
static void handle_variant_args(struct variant_info *info, const char *key, int key_len, char **dest, int *dest_len)
Definition: hls.c:173
AVDictionary * metadata
Definition: avformat.h:916
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition: time.c:57
KeyType
Definition: hls.c:52
static int recheck_discard_flags(AVFormatContext *s, int first)
Definition: hls.c:639
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:3009
char iv[35]
Definition: hls.c:185
#define AVPROBE_SCORE_MAX
maximum score
Definition: avformat.h:407
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:32
Main libavformat public API header.
char uri[MAX_URL_SIZE]
Definition: hls.c:183
#define INITIAL_BUFFER_SIZE
Definition: hls.c:38
int ffio_init_context(AVIOContext *s, unsigned char *buffer, int buffer_size, int write_flag, void *opaque, int(*read_packet)(void *opaque, uint8_t *buf, int buf_size), int(*write_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t(*seek)(void *opaque, int64_t offset, int whence))
Definition: aviobuf.c:109
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
Read packets of a media file to get stream information.
Definition: utils.c:2092
int64_t start_time
Decoding: pts of the first frame of the stream, in stream time base.
Definition: avformat.h:750
static int parse_playlist(HLSContext *c, const char *url, struct variant *var, AVIOContext *in)
Definition: hls.c:231
int cur_seq_no
Definition: hls.c:99
int stream_offset
Definition: hls.c:80
int64_t pos
position in the file of the current buffer
Definition: avio.h:130
int pts_wrap_bits
number of bits in pts (used for wrapping control)
Definition: avformat.h:859
AVDictionary * avio_opts
Definition: hls.c:106
void av_init_packet(AVPacket *pkt)
Initialize optional fields of a packet with default values.
Definition: avpacket.c:31
int den
denominator
Definition: rational.h:45
int cur_seq_no
Definition: hls.c:88
void avformat_close_input(AVFormatContext **s)
Close an opened input AVFormatContext.
Definition: utils.c:2626
static int hls_probe(AVProbeData *p)
Definition: hls.c:837
static int hls_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
Definition: hls.c:781
int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val)
Definition: opt.c:370
int eof_reached
true if eof reached
Definition: avio.h:132
int len
static uint8_t tmp[8]
Definition: des.c:38
void * priv_data
Format private data.
Definition: avformat.h:968
Definition: hls.c:70
int64_t dts
Decompression timestamp in AVStream->time_base units; the time at which the packet is decompressed...
Definition: avcodec.h:1345
int64_t duration
Duration of the stream, in AV_TIME_BASE fractional seconds.
Definition: avformat.h:1035
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:529
uint8_t key[16]
Definition: hls.c:92
AVCodecParameters * codecpar
Definition: avformat.h:831
int stream_index
Definition: avcodec.h:1348
AVRational time_base
This is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented...
Definition: avformat.h:742
AVPacket pkt
Definition: hls.c:79
int64_t duration
Definition: hls.c:58
static void free_variant_list(HLSContext *c)
Definition: hls.c:126
enum AVDiscard discard
Selects which packets can be discarded at will and do not need to be demuxed.
Definition: avformat.h:763
static int hls_close(AVFormatContext *s)
Definition: hls.c:770
char * ff_data_to_hex(char *buf, const uint8_t *src, int size, int lowercase)
Definition: utils.c:2937
This structure stores compressed data.
Definition: avcodec.h:1323
Definition: hls.c:53
void * av_mallocz(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:211
for(j=16;j >0;--j)
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:235
AVFormatContext * ctx
Definition: hls.c:78