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}
25  */
26 
27 /* #define DEBUG */
28 
29 #include <float.h>
30 
31 #include "avformat.h"
32 #include "internal.h"
33 
34 #include "libavutil/avassert.h"
35 #include "libavutil/log.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/parseutils.h"
39 #include "libavutil/mathematics.h"
40 #include "libavutil/timestamp.h"
41 
42 typedef struct SegmentListEntry {
43  int index;
45  int64_t start_pts;
46  char filename[1024];
49 
50 typedef enum {
55  LIST_TYPE_EXT, ///< deprecated
58 } ListType;
59 
60 #define SEGMENT_LIST_FLAG_CACHE 1
61 #define SEGMENT_LIST_FLAG_LIVE 2
62 
63 typedef struct {
64  const AVClass *class; /**< Class for private options. */
65  int segment_idx; ///< index of the segment file to write, starting from 0
66  int segment_idx_wrap; ///< number after which the index wraps
67  int segment_count; ///< number of segment files already written
70  char *format; ///< format to use for output segment files
71  char *list; ///< filename for the segment list file
72  int list_flags; ///< flags affecting list generation
73  int list_size; ///< number of entries for the segment list file
74  ListType list_type; ///< set the list type
75  AVIOContext *list_pb; ///< list file put-byte context
76  char *time_str; ///< segment duration specification string
77  int64_t time; ///< segment duration
78 
79  char *times_str; ///< segment times specification string
80  int64_t *times; ///< list of segment interval specification
81  int nb_times; ///< number of elments in the times array
82 
83  char *frames_str; ///< segment frame numbers specification string
84  int *frames; ///< list of frame number specification
85  int nb_frames; ///< number of elments in the frames array
87 
88  char *time_delta_str; ///< approximation value duration used for the segment times
89  int64_t time_delta;
90  int individual_header_trailer; /**< Set by a private option. */
91  int write_header_trailer; /**< Set by a private option. */
92 
93  int reset_timestamps; ///< reset timestamps at the begin of each segment
94  char *reference_stream_specifier; ///< reference stream specifier
96 
100 
101  int is_first_pkt; ///< tells if it is the first packet in the segment
103 
104 static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
105 {
106  int needs_quoting = !!str[strcspn(str, "\",\n\r")];
107 
108  if (needs_quoting)
109  avio_w8(ctx, '"');
110 
111  for (; *str; str++) {
112  if (*str == '"')
113  avio_w8(ctx, '"');
114  avio_w8(ctx, *str);
115  }
116  if (needs_quoting)
117  avio_w8(ctx, '"');
118 }
119 
121 {
122  SegmentContext *seg = s->priv_data;
123  AVFormatContext *oc;
124  int i;
125 
126  seg->avf = oc = avformat_alloc_context();
127  if (!oc)
128  return AVERROR(ENOMEM);
129 
130  oc->oformat = seg->oformat;
132 
133  for (i = 0; i < s->nb_streams; i++) {
134  AVStream *st;
135  AVCodecContext *icodec, *ocodec;
136 
137  if (!(st = avformat_new_stream(oc, NULL)))
138  return AVERROR(ENOMEM);
139  icodec = s->streams[i]->codec;
140  ocodec = st->codec;
141  avcodec_copy_context(ocodec, icodec);
142  if (!oc->oformat->codec_tag ||
143  av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
144  av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
145  ocodec->codec_tag = icodec->codec_tag;
146  } else {
147  ocodec->codec_tag = 0;
148  }
150  }
151 
152  return 0;
153 }
154 
156 {
157  SegmentContext *seg = s->priv_data;
158  AVFormatContext *oc = seg->avf;
159 
160  if (seg->segment_idx_wrap)
161  seg->segment_idx %= seg->segment_idx_wrap;
162  if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
163  s->filename, seg->segment_idx) < 0) {
164  av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
165  return AVERROR(EINVAL);
166  }
167  av_strlcpy(seg->cur_entry.filename, oc->filename, sizeof(seg->cur_entry.filename));
168  return 0;
169 }
170 
172 {
173  SegmentContext *seg = s->priv_data;
174  AVFormatContext *oc = seg->avf;
175  int err = 0;
176 
177  if (write_header) {
179  seg->avf = NULL;
180  if ((err = segment_mux_init(s)) < 0)
181  return err;
182  oc = seg->avf;
183  }
184 
185  seg->segment_idx++;
186  if ((err = set_segment_filename(s)) < 0)
187  return err;
188  seg->segment_count++;
189 
190  if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
191  &s->interrupt_callback, NULL)) < 0)
192  return err;
193 
194  if (oc->oformat->priv_class && oc->priv_data)
195  av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
196 
197  if (write_header) {
198  if ((err = avformat_write_header(oc, NULL)) < 0)
199  return err;
200  }
201 
202  seg->is_first_pkt = 1;
203  return 0;
204 }
205 
207 {
208  SegmentContext *seg = s->priv_data;
209  int ret;
210 
211  ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
212  &s->interrupt_callback, NULL);
213  if (ret < 0)
214  return ret;
215 
216  if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
217  SegmentListEntry *entry;
218  double max_duration = 0;
219 
220  avio_printf(seg->list_pb, "#EXTM3U\n");
221  avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
222  avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
223  avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
224  seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
225 
226  for (entry = seg->segment_list_entries; entry; entry = entry->next)
227  max_duration = FFMAX(max_duration, entry->end_time - entry->start_time);
228  avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration));
229  } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
230  avio_printf(seg->list_pb, "ffconcat version 1.0\n");
231  }
232 
233  return ret;
234 }
235 
236 static void segment_list_print_entry(AVIOContext *list_ioctx,
237  ListType list_type,
238  const SegmentListEntry *list_entry,
239  void *log_ctx)
240 {
241  switch (list_type) {
242  case LIST_TYPE_FLAT:
243  avio_printf(list_ioctx, "%s\n", list_entry->filename);
244  break;
245  case LIST_TYPE_CSV:
246  case LIST_TYPE_EXT:
247  print_csv_escaped_str(list_ioctx, list_entry->filename);
248  avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time);
249  break;
250  case LIST_TYPE_M3U8:
251  avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n",
252  list_entry->end_time - list_entry->start_time, list_entry->filename);
253  break;
254  case LIST_TYPE_FFCONCAT:
255  {
256  char *buf;
257  if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) {
258  av_log(log_ctx, AV_LOG_WARNING,
259  "Error writing list entry '%s' in list file\n", list_entry->filename);
260  return;
261  }
262  avio_printf(list_ioctx, "file %s\n", buf);
263  av_free(buf);
264  break;
265  }
266  default:
267  av_assert0(!"Invalid list type");
268  }
269 }
270 
271 static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
272 {
273  SegmentContext *seg = s->priv_data;
274  AVFormatContext *oc = seg->avf;
275  int ret = 0;
276 
277  av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
278  if (write_trailer)
279  ret = av_write_trailer(oc);
280 
281  if (ret < 0)
282  av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
283  oc->filename);
284 
285  if (seg->list) {
286  if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
287  SegmentListEntry *entry = av_mallocz(sizeof(*entry));
288  if (!entry) {
289  ret = AVERROR(ENOMEM);
290  goto end;
291  }
292 
293  /* append new element */
294  memcpy(entry, &seg->cur_entry, sizeof(*entry));
295  if (!seg->segment_list_entries)
297  else
298  seg->segment_list_entries_end->next = entry;
299  seg->segment_list_entries_end = entry;
300 
301  /* drop first item */
302  if (seg->list_size && seg->segment_count > seg->list_size) {
303  entry = seg->segment_list_entries;
305  av_freep(&entry);
306  }
307 
308  avio_close(seg->list_pb);
309  if ((ret = segment_list_open(s)) < 0)
310  goto end;
311  for (entry = seg->segment_list_entries; entry; entry = entry->next)
312  segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
313  if (seg->list_type == LIST_TYPE_M3U8 && is_last)
314  avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
315  } else {
316  segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
317  }
318  avio_flush(seg->list_pb);
319  }
320 
321 end:
322  avio_close(oc->pb);
323 
324  return ret;
325 }
326 
327 static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
328  const char *times_str)
329 {
330  char *p;
331  int i, ret = 0;
332  char *times_str1 = av_strdup(times_str);
333  char *saveptr = NULL;
334 
335  if (!times_str1)
336  return AVERROR(ENOMEM);
337 
338 #define FAIL(err) ret = err; goto end
339 
340  *nb_times = 1;
341  for (p = times_str1; *p; p++)
342  if (*p == ',')
343  (*nb_times)++;
344 
345  *times = av_malloc(sizeof(**times) * *nb_times);
346  if (!*times) {
347  av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
348  FAIL(AVERROR(ENOMEM));
349  }
350 
351  p = times_str1;
352  for (i = 0; i < *nb_times; i++) {
353  int64_t t;
354  char *tstr = av_strtok(p, ",", &saveptr);
355  p = NULL;
356 
357  if (!tstr || !tstr[0]) {
358  av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
359  times_str);
360  FAIL(AVERROR(EINVAL));
361  }
362 
363  ret = av_parse_time(&t, tstr, 1);
364  if (ret < 0) {
365  av_log(log_ctx, AV_LOG_ERROR,
366  "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
367  FAIL(AVERROR(EINVAL));
368  }
369  (*times)[i] = t;
370 
371  /* check on monotonicity */
372  if (i && (*times)[i-1] > (*times)[i]) {
373  av_log(log_ctx, AV_LOG_ERROR,
374  "Specified time %f is greater than the following time %f\n",
375  (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
376  FAIL(AVERROR(EINVAL));
377  }
378  }
379 
380 end:
381  av_free(times_str1);
382  return ret;
383 }
384 
385 static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
386  const char *frames_str)
387 {
388  char *p;
389  int i, ret = 0;
390  char *frames_str1 = av_strdup(frames_str);
391  char *saveptr = NULL;
392 
393  if (!frames_str1)
394  return AVERROR(ENOMEM);
395 
396 #define FAIL(err) ret = err; goto end
397 
398  *nb_frames = 1;
399  for (p = frames_str1; *p; p++)
400  if (*p == ',')
401  (*nb_frames)++;
402 
403  *frames = av_malloc(sizeof(**frames) * *nb_frames);
404  if (!*frames) {
405  av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
406  FAIL(AVERROR(ENOMEM));
407  }
408 
409  p = frames_str1;
410  for (i = 0; i < *nb_frames; i++) {
411  long int f;
412  char *tailptr;
413  char *fstr = av_strtok(p, ",", &saveptr);
414 
415  p = NULL;
416  if (!fstr) {
417  av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
418  frames_str);
419  FAIL(AVERROR(EINVAL));
420  }
421  f = strtol(fstr, &tailptr, 10);
422  if (*tailptr || f <= 0 || f >= INT_MAX) {
423  av_log(log_ctx, AV_LOG_ERROR,
424  "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
425  fstr);
426  FAIL(AVERROR(EINVAL));
427  }
428  (*frames)[i] = f;
429 
430  /* check on monotonicity */
431  if (i && (*frames)[i-1] > (*frames)[i]) {
432  av_log(log_ctx, AV_LOG_ERROR,
433  "Specified frame %d is greater than the following frame %d\n",
434  (*frames)[i], (*frames)[i-1]);
435  FAIL(AVERROR(EINVAL));
436  }
437  }
438 
439 end:
440  av_free(frames_str1);
441  return ret;
442 }
443 
444 static int open_null_ctx(AVIOContext **ctx)
445 {
446  int buf_size = 32768;
447  uint8_t *buf = av_malloc(buf_size);
448  if (!buf)
449  return AVERROR(ENOMEM);
450  *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
451  if (!*ctx) {
452  av_free(buf);
453  return AVERROR(ENOMEM);
454  }
455  return 0;
456 }
457 
458 static void close_null_ctx(AVIOContext *pb)
459 {
460  av_free(pb->buffer);
461  av_free(pb);
462 }
463 
465 {
466  SegmentContext *seg = s->priv_data;
467  int ret, i;
468 
469  seg->reference_stream_index = -1;
470  if (!strcmp(seg->reference_stream_specifier, "auto")) {
471  /* select first index of type with highest priority */
472  int type_index_map[AVMEDIA_TYPE_NB];
473  static const enum AVMediaType type_priority_list[] = {
479  };
480  enum AVMediaType type;
481 
482  for (i = 0; i < AVMEDIA_TYPE_NB; i++)
483  type_index_map[i] = -1;
484 
485  /* select first index for each type */
486  for (i = 0; i < s->nb_streams; i++) {
487  type = s->streams[i]->codec->codec_type;
488  if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
489  /* ignore attached pictures/cover art streams */
491  type_index_map[type] = i;
492  }
493 
494  for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
495  type = type_priority_list[i];
496  if ((seg->reference_stream_index = type_index_map[type]) >= 0)
497  break;
498  }
499  } else {
500  for (i = 0; i < s->nb_streams; i++) {
503  if (ret < 0)
504  return ret;
505  if (ret > 0) {
506  seg->reference_stream_index = i;
507  break;
508  }
509  }
510  }
511 
512  if (seg->reference_stream_index < 0) {
513  av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
515  return AVERROR(EINVAL);
516  }
517 
518  return 0;
519 }
520 
522 {
523  SegmentContext *seg = s->priv_data;
524  AVFormatContext *oc = NULL;
525  int ret;
526 
527  seg->segment_count = 0;
528  if (!seg->write_header_trailer)
529  seg->individual_header_trailer = 0;
530 
531  if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
532  av_log(s, AV_LOG_ERROR,
533  "segment_time, segment_times, and segment_frames options "
534  "are mutually exclusive, select just one of them\n");
535  return AVERROR(EINVAL);
536  }
537 
538  if (seg->times_str) {
539  if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
540  return ret;
541  } else if (seg->frames_str) {
542  if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
543  return ret;
544  } else {
545  /* set default value if not specified */
546  if (!seg->time_str)
547  seg->time_str = av_strdup("2");
548  if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
549  av_log(s, AV_LOG_ERROR,
550  "Invalid time duration specification '%s' for segment_time option\n",
551  seg->time_str);
552  return ret;
553  }
554  }
555 
556  if (seg->time_delta_str) {
557  if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
558  av_log(s, AV_LOG_ERROR,
559  "Invalid time duration specification '%s' for delta option\n",
560  seg->time_delta_str);
561  return ret;
562  }
563  }
564 
565  if (seg->list) {
566  if (seg->list_type == LIST_TYPE_UNDEFINED) {
567  if (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
568  else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
569  else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
570  else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
571  else seg->list_type = LIST_TYPE_FLAT;
572  }
573  if ((ret = segment_list_open(s)) < 0)
574  goto fail;
575  }
576  if (seg->list_type == LIST_TYPE_EXT)
577  av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
578 
579  if ((ret = select_reference_stream(s)) < 0)
580  goto fail;
581  av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
584 
585  seg->oformat = av_guess_format(seg->format, s->filename, NULL);
586 
587  if (!seg->oformat) {
589  goto fail;
590  }
591  if (seg->oformat->flags & AVFMT_NOFILE) {
592  av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
593  seg->oformat->name);
594  ret = AVERROR(EINVAL);
595  goto fail;
596  }
597 
598  if ((ret = segment_mux_init(s)) < 0)
599  goto fail;
600  oc = seg->avf;
601 
602  if ((ret = set_segment_filename(s)) < 0)
603  goto fail;
604  seg->segment_count++;
605 
606  if (seg->write_header_trailer) {
607  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
608  &s->interrupt_callback, NULL)) < 0)
609  goto fail;
610  } else {
611  if ((ret = open_null_ctx(&oc->pb)) < 0)
612  goto fail;
613  }
614 
615  if ((ret = avformat_write_header(oc, NULL)) < 0) {
616  avio_close(oc->pb);
617  goto fail;
618  }
619  seg->is_first_pkt = 1;
620 
621  if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
622  s->avoid_negative_ts = 1;
623 
624  if (!seg->write_header_trailer) {
625  close_null_ctx(oc->pb);
626  if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
627  &s->interrupt_callback, NULL)) < 0)
628  goto fail;
629  }
630 
631 fail:
632  if (ret) {
633  if (seg->list)
634  avio_close(seg->list_pb);
635  if (seg->avf)
637  }
638  return ret;
639 }
640 
642 {
643  SegmentContext *seg = s->priv_data;
644  AVFormatContext *oc = seg->avf;
645  AVStream *st = s->streams[pkt->stream_index];
646  int64_t end_pts = INT64_MAX;
647  int start_frame = INT_MAX;
648  int ret;
649 
650  if (seg->times) {
651  end_pts = seg->segment_count <= seg->nb_times ?
652  seg->times[seg->segment_count-1] : INT64_MAX;
653  } else if (seg->frames) {
654  start_frame = seg->segment_count <= seg->nb_frames ?
655  seg->frames[seg->segment_count-1] : INT_MAX;
656  } else {
657  end_pts = seg->time * seg->segment_count;
658  }
659 
660  av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
661  pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
662  pkt->flags & AV_PKT_FLAG_KEY,
663  pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);
664 
665  if (pkt->stream_index == seg->reference_stream_index &&
666  pkt->flags & AV_PKT_FLAG_KEY &&
667  (seg->frame_count >= start_frame ||
668  (pkt->pts != AV_NOPTS_VALUE &&
669  av_compare_ts(pkt->pts, st->time_base,
670  end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
671  ret = segment_end(s, seg->individual_header_trailer, 0);
672 
673  if (!ret)
675 
676  if (ret)
677  goto fail;
678 
679  oc = seg->avf;
680 
681  seg->cur_entry.index = seg->segment_idx;
682  seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base);
684  } else if (pkt->pts != AV_NOPTS_VALUE) {
685  seg->cur_entry.end_time =
686  FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
687  }
688 
689  if (seg->is_first_pkt) {
690  av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
691  seg->avf->filename, pkt->stream_index,
692  av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
693  seg->is_first_pkt = 0;
694  }
695 
696  if (seg->reset_timestamps) {
697  av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
698  pkt->stream_index,
700  av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
701  av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
702 
703  /* compute new timestamps */
704  if (pkt->pts != AV_NOPTS_VALUE)
706  if (pkt->dts != AV_NOPTS_VALUE)
708 
709  av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
710  av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
711  av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
712  }
713 
714  ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
715 
716 fail:
717  if (pkt->stream_index == seg->reference_stream_index)
718  seg->frame_count++;
719 
720  if (ret < 0) {
721  if (seg->list)
722  avio_close(seg->list_pb);
724  }
725 
726  return ret;
727 }
728 
729 static int seg_write_trailer(struct AVFormatContext *s)
730 {
731  SegmentContext *seg = s->priv_data;
732  AVFormatContext *oc = seg->avf;
733  SegmentListEntry *cur, *next;
734 
735  int ret;
736  if (!seg->write_header_trailer) {
737  if ((ret = segment_end(s, 0, 1)) < 0)
738  goto fail;
739  open_null_ctx(&oc->pb);
740  ret = av_write_trailer(oc);
741  close_null_ctx(oc->pb);
742  } else {
743  ret = segment_end(s, 1, 1);
744  }
745 fail:
746  if (seg->list)
747  avio_close(seg->list_pb);
748 
749  av_opt_free(seg);
750  av_freep(&seg->times);
751  av_freep(&seg->frames);
752 
753  cur = seg->segment_list_entries;
754  while (cur) {
755  next = cur->next;
756  av_free(cur);
757  cur = next;
758  }
759 
761  return ret;
762 }
763 
764 #define OFFSET(x) offsetof(SegmentContext, x)
765 #define E AV_OPT_FLAG_ENCODING_PARAM
766 static const AVOption options[] = {
767  { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
768  { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
769  { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
770 
771  { "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"},
772  { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"},
773  { "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"},
774 
775  { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
776 
777  { "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" },
778  { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
779  { "csv", "csv format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV }, INT_MIN, INT_MAX, E, "list_type" },
780  { "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT }, INT_MIN, INT_MAX, E, "list_type" },
781  { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" },
782  { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
783  { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
784 
785  { "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E },
786  { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
787  { "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
788  { "segment_frames", "set segment split frame numbers", OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E },
789  { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
790  { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
791 
792  { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
793  { "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 },
794  { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
795  { NULL },
796 };
797 
798 static const AVClass seg_class = {
799  .class_name = "segment muxer",
800  .item_name = av_default_item_name,
801  .option = options,
802  .version = LIBAVUTIL_VERSION_INT,
803 };
804 
806  .name = "segment",
807  .long_name = NULL_IF_CONFIG_SMALL("segment"),
808  .priv_data_size = sizeof(SegmentContext),
813  .priv_class = &seg_class,
814 };
815 
816 static const AVClass sseg_class = {
817  .class_name = "stream_segment muxer",
818  .item_name = av_default_item_name,
819  .option = options,
820  .version = LIBAVUTIL_VERSION_INT,
821 };
822 
824  .name = "stream_segment,ssegment",
825  .long_name = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
826  .priv_data_size = sizeof(SegmentContext),
827  .flags = AVFMT_NOFILE,
831  .priv_class = &sseg_class,
832 };