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