FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
segment.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011, Luca Barbato
3  *
4  * This file is part of Libav.
5  *
6  * Libav is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * Libav is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with Libav; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file generic segmenter
23  * M3U8 specification can be find here:
24  * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming-08.txt}
25  */
26 
27 /* #define DEBUG */
28 
29 #include <float.h>
30 
31 #include "avformat.h"
32 #include "internal.h"
33 
34 #include "libavutil/log.h"
35 #include "libavutil/opt.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/parseutils.h"
38 #include "libavutil/mathematics.h"
39 #include "libavutil/timestamp.h"
40 
41 typedef enum {
46  LIST_TYPE_EXT, ///< deprecated
48 } ListType;
49 
50 #define SEGMENT_LIST_FLAG_CACHE 1
51 #define SEGMENT_LIST_FLAG_LIVE 2
52 
53 typedef struct {
54  const AVClass *class; /**< Class for private options. */
55  int segment_idx; ///< index of the segment file to write, starting from 0
56  int segment_idx_wrap; ///< number after which the index wraps
57  int segment_count; ///< number of segment files already written
60  char *format; ///< format to use for output segment files
61  char *list; ///< filename for the segment list file
62  int list_flags; ///< flags affecting list generation
63  int list_size; ///< number of entries for the segment list file
64  double list_max_segment_time; ///< max segment time in the current list
65  ListType list_type; ///< set the list type
66  AVIOContext *list_pb; ///< list file put-byte context
67  char *time_str; ///< segment duration specification string
68  int64_t time; ///< segment duration
69 
70  char *times_str; ///< segment times specification string
71  int64_t *times; ///< list of segment interval specification
72  int nb_times; ///< number of elments in the times array
73 
74  char *frames_str; ///< segment frame numbers specification string
75  int *frames; ///< list of frame number specification
76  int nb_frames; ///< number of elments in the frames array
78 
79  char *time_delta_str; ///< approximation value duration used for the segment times
80  int64_t time_delta;
81  int individual_header_trailer; /**< Set by a private option. */
82  int write_header_trailer; /**< Set by a private option. */
83 
84  int reset_timestamps; ///< reset timestamps at the begin of each segment
85  char *reference_stream_specifier; ///< reference stream specifier
87 
88  double start_time, end_time;
89  int64_t start_pts, start_dts;
90  int is_first_pkt; ///< tells if it is the first packet in the segment
92 
93 static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
94 {
95  int needs_quoting = !!str[strcspn(str, "\",\n\r")];
96 
97  if (needs_quoting)
98  avio_w8(ctx, '"');
99 
100  for (; *str; str++) {
101  if (*str == '"')
102  avio_w8(ctx, '"');
103  avio_w8(ctx, *str);
104  }
105  if (needs_quoting)
106  avio_w8(ctx, '"');
107 }
108 
110 {
111  SegmentContext *seg = s->priv_data;
112  AVFormatContext *oc;
113  int i;
114 
115  seg->avf = oc = avformat_alloc_context();
116  if (!oc)
117  return AVERROR(ENOMEM);
118 
119  oc->oformat = seg->oformat;
121 
122  for (i = 0; i < s->nb_streams; i++) {
123  AVStream *st;
124  AVCodecContext *icodec, *ocodec;
125 
126  if (!(st = avformat_new_stream(oc, NULL)))
127  return AVERROR(ENOMEM);
128  icodec = s->streams[i]->codec;
129  ocodec = st->codec;
130  avcodec_copy_context(ocodec, icodec);
131  if (!oc->oformat->codec_tag ||
132  av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
133  av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
134  ocodec->codec_tag = icodec->codec_tag;
135  } else {
136  ocodec->codec_tag = 0;
137  }
139  }
140 
141  return 0;
142 }
143 
145 {
146  SegmentContext *seg = s->priv_data;
147  AVFormatContext *oc = seg->avf;
148 
149  if (seg->segment_idx_wrap)
150  seg->segment_idx %= seg->segment_idx_wrap;
151  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
152  s->filename, seg->segment_idx) < 0) {
153  av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
154  return AVERROR(EINVAL);
155  }
156  return 0;
157 }
158 
160 {
161  SegmentContext *seg = s->priv_data;
162  AVFormatContext *oc = seg->avf;
163  int err = 0;
164 
165  if (write_header) {
167  seg->avf = NULL;
168  if ((err = segment_mux_init(s)) < 0)
169  return err;
170  oc = seg->avf;
171  }
172 
173  seg->segment_idx++;
174  if ((err = set_segment_filename(s)) < 0)
175  return err;
176  seg->segment_count++;
177 
178  if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
179  &s->interrupt_callback, NULL)) < 0)
180  return err;
181 
182  if (oc->oformat->priv_class && oc->priv_data)
183  av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
184 
185  if (write_header) {
186  if ((err = avformat_write_header(oc, NULL)) < 0)
187  return err;
188  }
189 
190  seg->is_first_pkt = 1;
191  return 0;
192 }
193 
195 {
196  SegmentContext *seg = s->priv_data;
197  int ret;
198 
199  ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
200  &s->interrupt_callback, NULL);
201  if (ret < 0)
202  return ret;
203  seg->list_max_segment_time = 0;
204 
205  if (seg->list_type == LIST_TYPE_M3U8) {
206  avio_printf(seg->list_pb, "#EXTM3U\n");
207  avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
208  avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_idx);
209  avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n",
212  avio_printf(seg->list_pb,
213  "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000);
214  }
215 
216  return ret;
217 }
218 
220 {
221  SegmentContext *seg = s->priv_data;
222 
223  if (seg->list_type == LIST_TYPE_M3U8) {
224  if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE))
225  avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n",
226  (int)ceil(seg->list_max_segment_time));
227  avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
228  }
229 
230  avio_close(seg->list_pb);
231 }
232 
234 {
235  SegmentContext *seg = s->priv_data;
236  AVFormatContext *oc = seg->avf;
237  int ret = 0;
238 
239  av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
240  if (write_trailer)
241  ret = av_write_trailer(oc);
242 
243  if (ret < 0)
244  av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
245  oc->filename);
246 
247  if (seg->list) {
248  if (seg->list_size && !(seg->segment_count % seg->list_size)) {
250  if ((ret = segment_list_open(s)) < 0)
251  goto end;
252  }
253 
254  if (seg->list_type == LIST_TYPE_FLAT) {
255  avio_printf(seg->list_pb, "%s\n", oc->filename);
256  } else if (seg->list_type == LIST_TYPE_CSV || seg->list_type == LIST_TYPE_EXT) {
258  avio_printf(seg->list_pb, ",%f,%f\n", seg->start_time, seg->end_time);
259  } else if (seg->list_type == LIST_TYPE_M3U8) {
260  avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n",
261  seg->end_time - seg->start_time, oc->filename);
262  }
264  avio_flush(seg->list_pb);
265  }
266 
267 end:
268  avio_close(oc->pb);
269 
270  return ret;
271 }
272 
273 static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
274  const char *times_str)
275 {
276  char *p;
277  int i, ret = 0;
278  char *times_str1 = av_strdup(times_str);
279  char *saveptr = NULL;
280 
281  if (!times_str1)
282  return AVERROR(ENOMEM);
283 
284 #define FAIL(err) ret = err; goto end
285 
286  *nb_times = 1;
287  for (p = times_str1; *p; p++)
288  if (*p == ',')
289  (*nb_times)++;
290 
291  *times = av_malloc(sizeof(**times) * *nb_times);
292  if (!*times) {
293  av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
294  FAIL(AVERROR(ENOMEM));
295  }
296 
297  p = times_str1;
298  for (i = 0; i < *nb_times; i++) {
299  int64_t t;
300  char *tstr = av_strtok(p, ",", &saveptr);
301  p = NULL;
302 
303  if (!tstr || !tstr[0]) {
304  av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
305  times_str);
306  FAIL(AVERROR(EINVAL));
307  }
308 
309  ret = av_parse_time(&t, tstr, 1);
310  if (ret < 0) {
311  av_log(log_ctx, AV_LOG_ERROR,
312  "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
313  FAIL(AVERROR(EINVAL));
314  }
315  (*times)[i] = t;
316 
317  /* check on monotonicity */
318  if (i && (*times)[i-1] > (*times)[i]) {
319  av_log(log_ctx, AV_LOG_ERROR,
320  "Specified time %f is greater than the following time %f\n",
321  (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
322  FAIL(AVERROR(EINVAL));
323  }
324  }
325 
326 end:
327  av_free(times_str1);
328  return ret;
329 }
330 
331 static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
332  const char *frames_str)
333 {
334  char *p;
335  int i, ret = 0;
336  char *frames_str1 = av_strdup(frames_str);
337  char *saveptr = NULL;
338 
339  if (!frames_str1)
340  return AVERROR(ENOMEM);
341 
342 #define FAIL(err) ret = err; goto end
343 
344  *nb_frames = 1;
345  for (p = frames_str1; *p; p++)
346  if (*p == ',')
347  (*nb_frames)++;
348 
349  *frames = av_malloc(sizeof(**frames) * *nb_frames);
350  if (!*frames) {
351  av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
352  FAIL(AVERROR(ENOMEM));
353  }
354 
355  p = frames_str1;
356  for (i = 0; i < *nb_frames; i++) {
357  long int f;
358  char *tailptr;
359  char *fstr = av_strtok(p, ",", &saveptr);
360 
361  p = NULL;
362  if (!fstr) {
363  av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
364  frames_str);
365  FAIL(AVERROR(EINVAL));
366  }
367  f = strtol(fstr, &tailptr, 10);
368  if (*tailptr || f <= 0 || f >= INT_MAX) {
369  av_log(log_ctx, AV_LOG_ERROR,
370  "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
371  fstr);
372  FAIL(AVERROR(EINVAL));
373  }
374  (*frames)[i] = f;
375 
376  /* check on monotonicity */
377  if (i && (*frames)[i-1] > (*frames)[i]) {
378  av_log(log_ctx, AV_LOG_ERROR,
379  "Specified frame %d is greater than the following frame %d\n",
380  (*frames)[i], (*frames)[i-1]);
381  FAIL(AVERROR(EINVAL));
382  }
383  }
384 
385 end:
386  av_free(frames_str1);
387  return ret;
388 }
389 
390 static int open_null_ctx(AVIOContext **ctx)
391 {
392  int buf_size = 32768;
393  uint8_t *buf = av_malloc(buf_size);
394  if (!buf)
395  return AVERROR(ENOMEM);
396  *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
397  if (!*ctx) {
398  av_free(buf);
399  return AVERROR(ENOMEM);
400  }
401  return 0;
402 }
403 
404 static void close_null_ctx(AVIOContext *pb)
405 {
406  av_free(pb->buffer);
407  av_free(pb);
408 }
409 
411 {
412  SegmentContext *seg = s->priv_data;
413  AVFormatContext *oc = NULL;
414  int ret, i;
415 
416  seg->segment_count = 0;
417  if (!seg->write_header_trailer)
418  seg->individual_header_trailer = 0;
419 
420  if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
421  av_log(s, AV_LOG_ERROR,
422  "segment_time, segment_times, and segment_frames options "
423  "are mutually exclusive, select just one of them\n");
424  return AVERROR(EINVAL);
425  }
426 
427  if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && (seg->times_str || seg->frames_str)) {
428  av_log(s, AV_LOG_ERROR,
429  "segment_flags +live and segment_times or segment_frames options are mutually exclusive: "
430  "specify segment_time option if you want a live-friendly list\n");
431  return AVERROR(EINVAL);
432  }
433 
434  if (seg->times_str) {
435  if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
436  return ret;
437  } else if (seg->frames_str) {
438  if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
439  return ret;
440  } else {
441  /* set default value if not specified */
442  if (!seg->time_str)
443  seg->time_str = av_strdup("2");
444  if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
445  av_log(s, AV_LOG_ERROR,
446  "Invalid time duration specification '%s' for segment_time option\n",
447  seg->time_str);
448  return ret;
449  }
450  }
451 
452  if (seg->time_delta_str) {
453  if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
454  av_log(s, AV_LOG_ERROR,
455  "Invalid time duration specification '%s' for delta option\n",
456  seg->time_delta_str);
457  return ret;
458  }
459  }
460 
461  if (seg->list) {
462  if (seg->list_type == LIST_TYPE_UNDEFINED) {
463  if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
464  else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
465  else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
466  else seg->list_type = LIST_TYPE_FLAT;
467  }
468  if ((ret = segment_list_open(s)) < 0)
469  goto fail;
470  }
471  if (seg->list_type == LIST_TYPE_EXT)
472  av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
473 
474  seg->reference_stream_index = -1;
475  if (!strcmp(seg->reference_stream_specifier, "auto")) {
476  /* select first index of type with highest priority */
477  int type_index_map[AVMEDIA_TYPE_NB];
478  static const enum AVMediaType type_priority_list[] = {
484  };
485  enum AVMediaType type;
486 
487  for (i = 0; i < AVMEDIA_TYPE_NB; i++)
488  type_index_map[i] = -1;
489 
490  /* select first index for each type */
491  for (i = 0; i < s->nb_streams; i++) {
492  type = s->streams[i]->codec->codec_type;
493  if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1)
494  type_index_map[type] = i;
495  }
496 
497  for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
498  type = type_priority_list[i];
499  if ((seg->reference_stream_index = type_index_map[type]) >= 0)
500  break;
501  }
502  } else {
503  for (i = 0; i < s->nb_streams; i++) {
506  if (ret < 0)
507  goto fail;
508  if (ret > 0) {
509  seg->reference_stream_index = i;
510  break;
511  }
512  }
513  }
514 
515  if (seg->reference_stream_index < 0) {
516  av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
518  ret = AVERROR(EINVAL);
519  goto fail;
520  }
521 
522  av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
525 
526  seg->oformat = av_guess_format(seg->format, s->filename, NULL);
527 
528  if (!seg->oformat) {
530  goto fail;
531  }
532  if (seg->oformat->flags & AVFMT_NOFILE) {
533  av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
534  seg->oformat->name);
535  ret = AVERROR(EINVAL);
536  goto fail;
537  }
538 
539  if ((ret = segment_mux_init(s)) < 0)
540  goto fail;
541  oc = seg->avf;
542 
543  if ((ret = set_segment_filename(s)) < 0)
544  goto fail;
545  seg->segment_count++;
546 
547  if (seg->write_header_trailer) {
548  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
549  &s->interrupt_callback, NULL)) < 0)
550  goto fail;
551  } else {
552  if ((ret = open_null_ctx(&oc->pb)) < 0)
553  goto fail;
554  }
555 
556  if ((ret = avformat_write_header(oc, NULL)) < 0) {
557  avio_close(oc->pb);
558  goto fail;
559  }
560  seg->is_first_pkt = 1;
561 
562  if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
563  s->avoid_negative_ts = 1;
564 
565  if (!seg->write_header_trailer) {
566  close_null_ctx(oc->pb);
567  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
568  &s->interrupt_callback, NULL)) < 0)
569  goto fail;
570  }
571 
572 fail:
573  if (ret) {
574  if (seg->list)
576  if (seg->avf)
578  }
579  return ret;
580 }
581 
583 {
584  SegmentContext *seg = s->priv_data;
585  AVFormatContext *oc = seg->avf;
586  AVStream *st = s->streams[pkt->stream_index];
587  int64_t end_pts = INT64_MAX;
588  int start_frame = INT_MAX;
589  int ret;
590 
591  if (seg->times) {
592  end_pts = seg->segment_count <= seg->nb_times ?
593  seg->times[seg->segment_count-1] : INT64_MAX;
594  } else if (seg->frames) {
595  start_frame = seg->segment_count <= seg->nb_frames ?
596  seg->frames[seg->segment_count-1] : INT_MAX;
597  } else {
598  end_pts = seg->time * seg->segment_count;
599  }
600 
601  av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
602  pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
603  pkt->flags & AV_PKT_FLAG_KEY,
604  pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);
605 
606  if (pkt->stream_index == seg->reference_stream_index &&
607  pkt->flags & AV_PKT_FLAG_KEY &&
608  (seg->frame_count >= start_frame ||
609  (pkt->pts != AV_NOPTS_VALUE &&
610  av_compare_ts(pkt->pts, st->time_base,
611  end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
612  ret = segment_end(s, seg->individual_header_trailer);
613 
614  if (!ret)
616 
617  if (ret)
618  goto fail;
619 
620  oc = seg->avf;
621 
622  seg->start_time = (double)pkt->pts * av_q2d(st->time_base);
624  seg->start_dts = pkt->dts != AV_NOPTS_VALUE ?
626  } else if (pkt->pts != AV_NOPTS_VALUE) {
627  seg->end_time = FFMAX(seg->end_time,
628  (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
629  }
630 
631  if (seg->is_first_pkt) {
632  av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
633  seg->avf->filename, pkt->stream_index,
634  av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
635  seg->is_first_pkt = 0;
636  }
637 
638  if (seg->reset_timestamps) {
639  av_log(s, AV_LOG_DEBUG, "start_pts:%s pts:%s start_dts:%s dts:%s",
642 
643  /* compute new timestamps */
644  if (pkt->pts != AV_NOPTS_VALUE)
645  pkt->pts -= av_rescale_q(seg->start_pts, AV_TIME_BASE_Q, st->time_base);
646  if (pkt->dts != AV_NOPTS_VALUE)
647  pkt->dts -= av_rescale_q(seg->start_dts, AV_TIME_BASE_Q, st->time_base);
648 
649  av_log(s, AV_LOG_DEBUG, " -> pts:%s dts:%s\n",
650  av_ts2timestr(pkt->pts, &st->time_base), av_ts2timestr(pkt->dts, &st->time_base));
651  }
652 
653  ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
654 
655 fail:
656  if (pkt->stream_index == seg->reference_stream_index)
657  seg->frame_count++;
658 
659  if (ret < 0) {
660  if (seg->list)
661  avio_close(seg->list_pb);
663  }
664 
665  return ret;
666 }
667 
668 static int seg_write_trailer(struct AVFormatContext *s)
669 {
670  SegmentContext *seg = s->priv_data;
671  AVFormatContext *oc = seg->avf;
672  int ret;
673  if (!seg->write_header_trailer) {
674  if ((ret = segment_end(s, 0)) < 0)
675  goto fail;
676  open_null_ctx(&oc->pb);
677  ret = av_write_trailer(oc);
678  close_null_ctx(oc->pb);
679  } else {
680  ret = segment_end(s, 1);
681  }
682 fail:
683  if (seg->list)
685 
686  av_opt_free(seg);
687  av_freep(&seg->times);
688  av_freep(&seg->frames);
689 
691  return ret;
692 }
693 
694 #define OFFSET(x) offsetof(SegmentContext, x)
695 #define E AV_OPT_FLAG_ENCODING_PARAM
696 static const AVOption options[] = {
697  { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
698  { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
699  { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
700 
701  { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"},
702  { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"},
703  { "live", "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX, E, "list_flags"},
704 
705  { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
706 
707  { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" },
708  { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
709  { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, E, "list_type" },
710  { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, E, "list_type" },
711  { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
712  { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
713 
714  { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
715  { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
716  { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
717  { "segment_frames", "set segment split frame numbers", OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
718  { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
719  { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
720 
721  { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
722  { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
723  { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
724  { NULL },
725 };
726 
727 static const AVClass seg_class = {
728  .class_name = "segment muxer",
729  .item_name = av_default_item_name,
730  .option = options,
731  .version = LIBAVUTIL_VERSION_INT,
732 };
733 
735  .name = "segment",
736  .long_name = NULL_IF_CONFIG_SMALL("segment"),
737  .priv_data_size = sizeof(SegmentContext),
742  .priv_class = &seg_class,
743 };
744 
745 static const AVClass sseg_class = {
746  .class_name = "stream_segment muxer",
747  .item_name = av_default_item_name,
748  .option = options,
749  .version = LIBAVUTIL_VERSION_INT,
750 };
751 
753  .name = "stream_segment,ssegment",
754  .long_name = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
755  .priv_data_size = sizeof(SegmentContext),
756  .flags = AVFMT_NOFILE,
760  .priv_class = &sseg_class,
761 };