FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hlsenc.c
Go to the documentation of this file.
1 /*
2  * Apple HTTP Live Streaming segmenter
3  * Copyright (c) 2012, Luca Barbato
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 #include <float.h>
23 #include <stdint.h>
24 
25 #include "libavutil/avassert.h"
26 #include "libavutil/mathematics.h"
27 #include "libavutil/parseutils.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/log.h"
31 
32 #include "avformat.h"
33 #include "internal.h"
34 
35 typedef struct HLSSegment {
36  char filename[1024];
37  double duration; /* in seconds */
38  int64_t pos;
39  int64_t size;
40 
41  struct HLSSegment *next;
42 } HLSSegment;
43 
44 typedef enum HLSFlags {
45  // Generate a single media file and use byte ranges in the playlist.
46  HLS_SINGLE_FILE = (1 << 0),
47 } HLSFlags;
48 
49 typedef struct HLSContext {
50  const AVClass *class; // Class for private options.
51  unsigned number;
52  int64_t sequence;
53  int64_t start_sequence;
55 
57 
58  float time; // Set by a private option.
59  int max_nb_segments; // Set by a private option.
60  int wrap; // Set by a private option.
61  uint32_t flags; // enum HLSFlags
62 
64 
65  int64_t recording_time;
66  int has_video;
67  int64_t start_pts;
68  int64_t end_pts;
69  double duration; // last segment duration computed so far, in seconds
70  int64_t start_pos; // last segment starting position
71  int64_t size; // last segment size
73 
76 
77  char *basename;
78  char *baseurl;
81 
83 } HLSContext;
84 
86 {
87  HLSContext *hls = s->priv_data;
88  AVFormatContext *oc;
89  int i;
90 
91  hls->avf = oc = avformat_alloc_context();
92  if (!oc)
93  return AVERROR(ENOMEM);
94 
95  oc->oformat = hls->oformat;
97  av_dict_copy(&oc->metadata, s->metadata, 0);
98 
99  for (i = 0; i < s->nb_streams; i++) {
100  AVStream *st;
101  if (!(st = avformat_new_stream(oc, NULL)))
102  return AVERROR(ENOMEM);
105  }
106  hls->start_pos = 0;
107 
108  return 0;
109 }
110 
111 /* Create a new segment and append it to the segment list */
112 static int hls_append_segment(HLSContext *hls, double duration, int64_t pos,
113  int64_t size)
114 {
115  HLSSegment *en = av_malloc(sizeof(*en));
116 
117  if (!en)
118  return AVERROR(ENOMEM);
119 
120  av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename));
121 
122  en->duration = duration;
123  en->pos = pos;
124  en->size = size;
125  en->next = NULL;
126 
127  if (!hls->segments)
128  hls->segments = en;
129  else
130  hls->last_segment->next = en;
131 
132  hls->last_segment = en;
133 
134  if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
135  en = hls->segments;
136  hls->segments = en->next;
137  av_free(en);
138  } else
139  hls->nb_entries++;
140 
141  hls->sequence++;
142 
143  return 0;
144 }
145 
146 static void hls_free_segments(HLSContext *hls)
147 {
148  HLSSegment *p = hls->segments, *en;
149 
150  while(p) {
151  en = p;
152  p = p->next;
153  av_free(en);
154  }
155 }
156 
157 static int hls_window(AVFormatContext *s, int last)
158 {
159  HLSContext *hls = s->priv_data;
160  HLSSegment *en;
161  int target_duration = 0;
162  int ret = 0;
163  int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
164  int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3;
165 
166  if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE,
167  &s->interrupt_callback, NULL)) < 0)
168  goto fail;
169 
170  for (en = hls->segments; en; en = en->next) {
171  if (target_duration < en->duration)
172  target_duration = ceil(en->duration);
173  }
174 
175  avio_printf(hls->pb, "#EXTM3U\n");
176  avio_printf(hls->pb, "#EXT-X-VERSION:%d\n", version);
177  if (hls->allowcache == 0 || hls->allowcache == 1) {
178  avio_printf(hls->pb, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
179  }
180  avio_printf(hls->pb, "#EXT-X-TARGETDURATION:%d\n", target_duration);
181  avio_printf(hls->pb, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
182 
183  av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
184  sequence);
185 
186  for (en = hls->segments; en; en = en->next) {
187  avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration);
188  if (hls->flags & HLS_SINGLE_FILE)
189  avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
190  en->size, en->pos);
191  if (hls->baseurl)
192  avio_printf(hls->pb, "%s", hls->baseurl);
193  avio_printf(hls->pb, "%s\n", en->filename);
194  }
195 
196  if (last)
197  avio_printf(hls->pb, "#EXT-X-ENDLIST\n");
198 
199 fail:
200  avio_closep(&hls->pb);
201  return ret;
202 }
203 
205 {
206  HLSContext *c = s->priv_data;
207  AVFormatContext *oc = c->avf;
208  int err = 0;
209 
210  if (c->flags & HLS_SINGLE_FILE)
211  av_strlcpy(oc->filename, c->basename,
212  sizeof(oc->filename));
213  else
214  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
215  c->basename, c->wrap ? c->sequence % c->wrap : c->sequence) < 0) {
216  av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->basename);
217  return AVERROR(EINVAL);
218  }
219  c->number++;
220 
221  if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
222  &s->interrupt_callback, NULL)) < 0)
223  return err;
224 
225  if (oc->oformat->priv_class && oc->priv_data)
226  av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
227 
228  return 0;
229 }
230 
232 {
233  HLSContext *hls = s->priv_data;
234  int ret, i;
235  char *p;
236  const char *pattern = "%d.ts";
237  AVDictionary *options = NULL;
238  int basename_size = strlen(s->filename) + strlen(pattern) + 1;
239 
240  hls->sequence = hls->start_sequence;
241  hls->recording_time = hls->time * AV_TIME_BASE;
242  hls->start_pts = AV_NOPTS_VALUE;
243 
244  if (hls->flags & HLS_SINGLE_FILE)
245  pattern = ".ts";
246 
247  if (hls->format_options_str) {
248  ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0);
249  if (ret < 0) {
250  av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str);
251  goto fail;
252  }
253  }
254 
255  for (i = 0; i < s->nb_streams; i++)
256  hls->has_video +=
258 
259  if (hls->has_video > 1)
261  "More than a single video stream present, "
262  "expect issues decoding it.\n");
263 
264  hls->oformat = av_guess_format("mpegts", NULL, NULL);
265 
266  if (!hls->oformat) {
268  goto fail;
269  }
270 
271  hls->basename = av_malloc(basename_size);
272 
273  if (!hls->basename) {
274  ret = AVERROR(ENOMEM);
275  goto fail;
276  }
277 
278  strcpy(hls->basename, s->filename);
279 
280  p = strrchr(hls->basename, '.');
281 
282  if (p)
283  *p = '\0';
284 
285  av_strlcat(hls->basename, pattern, basename_size);
286 
287  if ((ret = hls_mux_init(s)) < 0)
288  goto fail;
289 
290  if ((ret = hls_start(s)) < 0)
291  goto fail;
292 
293  av_dict_copy(&options, hls->format_options, 0);
294  ret = avformat_write_header(hls->avf, &options);
295  if (av_dict_count(options)) {
296  av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
297  ret = AVERROR(EINVAL);
298  goto fail;
299  }
300  av_assert0(s->nb_streams == hls->avf->nb_streams);
301  for (i = 0; i < s->nb_streams; i++) {
302  AVStream *inner_st = hls->avf->streams[i];
303  AVStream *outer_st = s->streams[i];
304  avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
305  }
306 fail:
307 
308  av_dict_free(&options);
309  if (ret) {
310  av_free(hls->basename);
311  if (hls->avf)
313  }
314  return ret;
315 }
316 
318 {
319  HLSContext *hls = s->priv_data;
320  AVFormatContext *oc = hls->avf;
321  AVStream *st = s->streams[pkt->stream_index];
322  int64_t end_pts = hls->recording_time * hls->number;
323  int is_ref_pkt = 1;
324  int ret, can_split = 1;
325 
326  if (hls->start_pts == AV_NOPTS_VALUE) {
327  hls->start_pts = pkt->pts;
328  hls->end_pts = pkt->pts;
329  }
330 
331  if (hls->has_video) {
332  can_split = st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
333  pkt->flags & AV_PKT_FLAG_KEY;
334  is_ref_pkt = st->codec->codec_type == AVMEDIA_TYPE_VIDEO;
335  }
336  if (pkt->pts == AV_NOPTS_VALUE)
337  is_ref_pkt = can_split = 0;
338 
339  if (is_ref_pkt)
340  hls->duration = (double)(pkt->pts - hls->end_pts)
341  * st->time_base.num / st->time_base.den;
342 
343  if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
344  end_pts, AV_TIME_BASE_Q) >= 0) {
345  int64_t new_start_pos;
346  av_write_frame(oc, NULL); /* Flush any buffered data */
347 
348  new_start_pos = avio_tell(hls->avf->pb);
349  hls->size = new_start_pos - hls->start_pos;
350  ret = hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
351  hls->start_pos = new_start_pos;
352  if (ret)
353  return ret;
354 
355  hls->end_pts = pkt->pts;
356  hls->duration = 0;
357 
358  if (hls->flags & HLS_SINGLE_FILE) {
359  if (hls->avf->oformat->priv_class && hls->avf->priv_data)
360  av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
361  hls->number++;
362  } else {
363  avio_close(oc->pb);
364 
365  ret = hls_start(s);
366  }
367 
368  if (ret)
369  return ret;
370 
371  oc = hls->avf;
372 
373  if ((ret = hls_window(s, 0)) < 0)
374  return ret;
375  }
376 
377  ret = ff_write_chained(oc, pkt->stream_index, pkt, s, 0);
378 
379  return ret;
380 }
381 
383 {
384  HLSContext *hls = s->priv_data;
385  AVFormatContext *oc = hls->avf;
386 
387  av_write_trailer(oc);
388  hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
389  avio_closep(&oc->pb);
391  av_free(hls->basename);
392  hls_append_segment(hls, hls->duration, hls->start_pos, hls->size);
393  hls_window(s, 1);
394 
395  hls_free_segments(hls);
396  avio_close(hls->pb);
397  return 0;
398 }
399 
400 #define OFFSET(x) offsetof(HLSContext, x)
401 #define E AV_OPT_FLAG_ENCODING_PARAM
402 static const AVOption options[] = {
403  {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E},
404  {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E},
405  {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E},
406  {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
407  {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E},
408  {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E},
409  {"hls_base_url", "url to prepend to each playlist entry", OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
410  {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
411  {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, "flags"},
412 
413  { NULL },
414 };
415 
416 static const AVClass hls_class = {
417  .class_name = "hls muxer",
418  .item_name = av_default_item_name,
419  .option = options,
420  .version = LIBAVUTIL_VERSION_INT,
421 };
422 
423 
425  .name = "hls",
426  .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
427  .extensions = "m3u8",
428  .priv_data_size = sizeof(HLSContext),
429  .audio_codec = AV_CODEC_ID_AAC,
430  .video_codec = AV_CODEC_ID_H264,
435  .priv_class = &hls_class,
436 };