2 * Copyright (c) 2011, Luca Barbato
4 * This file is part of FFmpeg.
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.
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.
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
22 * @file generic segmenter
23 * M3U8 specification can be find here:
24 * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming}
35 #include "libavutil/avassert.h"
36 #include "libavutil/log.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/avstring.h"
39 #include "libavutil/parseutils.h"
40 #include "libavutil/mathematics.h"
41 #include "libavutil/time.h"
42 #include "libavutil/timestamp.h"
44 typedef struct SegmentListEntry
{
46 double start_time
, end_time
;
50 struct SegmentListEntry
*next
;
51 int64_t last_duration
;
55 LIST_TYPE_UNDEFINED
= -1,
59 LIST_TYPE_EXT
, ///< deprecated
64 #define SEGMENT_LIST_FLAG_CACHE 1
65 #define SEGMENT_LIST_FLAG_LIVE 2
68 const AVClass
*class; /**< Class for private options. */
69 int segment_idx
; ///< index of the segment file to write, starting from 0
70 int segment_idx_wrap
; ///< number after which the index wraps
71 int segment_idx_wrap_nb
; ///< number of time the index has wraped
72 int segment_count
; ///< number of segment files already written
73 AVOutputFormat
*oformat
;
75 char *format
; ///< format to use for output segment files
76 char *format_options_str
; ///< format options to use for output segment files
77 AVDictionary
*format_options
;
78 char *list
; ///< filename for the segment list file
79 int list_flags
; ///< flags affecting list generation
80 int list_size
; ///< number of entries for the segment list file
82 int use_clocktime
; ///< flag to cut segments at regular clock time
83 int64_t last_val
; ///< remember last time for wrap around detection
84 int64_t last_cut
; ///< remember last cut
87 char *entry_prefix
; ///< prefix to add to list entry filenames
88 ListType list_type
; ///< set the list type
89 AVIOContext
*list_pb
; ///< list file put-byte context
90 char *time_str
; ///< segment duration specification string
91 int64_t time
; ///< segment duration
93 char *times_str
; ///< segment times specification string
94 int64_t *times
; ///< list of segment interval specification
95 int nb_times
; ///< number of elments in the times array
97 char *frames_str
; ///< segment frame numbers specification string
98 int *frames
; ///< list of frame number specification
99 int nb_frames
; ///< number of elments in the frames array
100 int frame_count
; ///< total number of reference frames
101 int segment_frame_count
; ///< number of reference frames in the segment
104 int individual_header_trailer
; /**< Set by a private option. */
105 int write_header_trailer
; /**< Set by a private option. */
107 int reset_timestamps
; ///< reset timestamps at the begin of each segment
108 int64_t initial_offset
; ///< initial timestamps offset, expressed in microseconds
109 char *reference_stream_specifier
; ///< reference stream specifier
110 int reference_stream_index
;
112 SegmentListEntry cur_entry
;
113 SegmentListEntry
*segment_list_entries
;
114 SegmentListEntry
*segment_list_entries_end
;
117 static void print_csv_escaped_str(AVIOContext
*ctx
, const char *str
)
119 int needs_quoting
= !!str
[strcspn(str
, "\",\n\r")];
124 for (; *str
; str
++) {
133 static int segment_mux_init(AVFormatContext
*s
)
135 SegmentContext
*seg
= s
->priv_data
;
140 ret
= avformat_alloc_output_context2(&seg
->avf
, seg
->oformat
, NULL
, NULL
);
145 oc
->interrupt_callback
= s
->interrupt_callback
;
146 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
148 for (i
= 0; i
< s
->nb_streams
; i
++) {
150 AVCodecContext
*icodec
, *ocodec
;
152 if (!(st
= avformat_new_stream(oc
, NULL
)))
153 return AVERROR(ENOMEM
);
154 icodec
= s
->streams
[i
]->codec
;
156 avcodec_copy_context(ocodec
, icodec
);
157 if (!oc
->oformat
->codec_tag
||
158 av_codec_get_id (oc
->oformat
->codec_tag
, icodec
->codec_tag
) == ocodec
->codec_id
||
159 av_codec_get_tag(oc
->oformat
->codec_tag
, icodec
->codec_id
) <= 0) {
160 ocodec
->codec_tag
= icodec
->codec_tag
;
162 ocodec
->codec_tag
= 0;
164 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
165 av_dict_copy(&st
->metadata
, s
->streams
[i
]->metadata
, 0);
171 static int set_segment_filename(AVFormatContext
*s
)
173 SegmentContext
*seg
= s
->priv_data
;
174 AVFormatContext
*oc
= seg
->avf
;
177 if (seg
->segment_idx_wrap
)
178 seg
->segment_idx
%= seg
->segment_idx_wrap
;
179 if (av_get_frame_filename(oc
->filename
, sizeof(oc
->filename
),
180 s
->filename
, seg
->segment_idx
) < 0) {
181 av_log(oc
, AV_LOG_ERROR
, "Invalid segment filename template '%s'\n", s
->filename
);
182 return AVERROR(EINVAL
);
185 /* copy modified name in list entry */
186 size
= strlen(av_basename(oc
->filename
)) + 1;
187 if (seg
->entry_prefix
)
188 size
+= strlen(seg
->entry_prefix
);
190 seg
->cur_entry
.filename
= av_mallocz(size
);
191 if (!seg
->cur_entry
.filename
)
192 return AVERROR(ENOMEM
);
193 snprintf(seg
->cur_entry
.filename
, size
, "%s%s",
194 seg
->entry_prefix
? seg
->entry_prefix
: "",
195 av_basename(oc
->filename
));
200 static int segment_start(AVFormatContext
*s
, int write_header
)
202 SegmentContext
*seg
= s
->priv_data
;
203 AVFormatContext
*oc
= seg
->avf
;
207 avformat_free_context(oc
);
209 if ((err
= segment_mux_init(s
)) < 0)
215 if ((seg
->segment_idx_wrap
) && (seg
->segment_idx
%seg
->segment_idx_wrap
== 0))
216 seg
->segment_idx_wrap_nb
++;
218 if ((err
= set_segment_filename(s
)) < 0)
221 if ((err
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
222 &s
->interrupt_callback
, NULL
)) < 0) {
223 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->filename
);
227 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
228 av_opt_set(oc
->priv_data
, "mpegts_flags", "+resend_headers", 0);
231 if ((err
= avformat_write_header(oc
, NULL
)) < 0)
235 seg
->segment_frame_count
= 0;
239 static int segment_list_open(AVFormatContext
*s
)
241 SegmentContext
*seg
= s
->priv_data
;
244 ret
= avio_open2(&seg
->list_pb
, seg
->list
, AVIO_FLAG_WRITE
,
245 &s
->interrupt_callback
, NULL
);
247 av_log(s
, AV_LOG_ERROR
, "Failed to open segment list '%s'\n", seg
->list
);
251 if (seg
->list_type
== LIST_TYPE_M3U8
&& seg
->segment_list_entries
) {
252 SegmentListEntry
*entry
;
253 double max_duration
= 0;
255 avio_printf(seg
->list_pb
, "#EXTM3U\n");
256 avio_printf(seg
->list_pb
, "#EXT-X-VERSION:3\n");
257 avio_printf(seg
->list_pb
, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg
->segment_list_entries
->index
);
258 avio_printf(seg
->list_pb
, "#EXT-X-ALLOW-CACHE:%s\n",
259 seg
->list_flags
& SEGMENT_LIST_FLAG_CACHE
? "YES" : "NO");
261 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%d\n",
262 seg
->segment_list_entries
->index
);
264 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
265 max_duration
= FFMAX(max_duration
, entry
->end_time
- entry
->start_time
);
266 avio_printf(seg
->list_pb
, "#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration
));
267 } else if (seg
->list_type
== LIST_TYPE_FFCONCAT
) {
268 avio_printf(seg
->list_pb
, "ffconcat version 1.0\n");
274 static void segment_list_print_entry(AVIOContext
*list_ioctx
,
276 const SegmentListEntry
*list_entry
,
281 avio_printf(list_ioctx
, "%s\n", list_entry
->filename
);
285 print_csv_escaped_str(list_ioctx
, list_entry
->filename
);
286 avio_printf(list_ioctx
, ",%f,%f\n", list_entry
->start_time
, list_entry
->end_time
);
289 avio_printf(list_ioctx
, "#EXTINF:%f,\n%s\n",
290 list_entry
->end_time
- list_entry
->start_time
, list_entry
->filename
);
292 case LIST_TYPE_FFCONCAT
:
295 if (av_escape(&buf
, list_entry
->filename
, NULL
, AV_ESCAPE_MODE_AUTO
, AV_ESCAPE_FLAG_WHITESPACE
) < 0) {
296 av_log(log_ctx
, AV_LOG_WARNING
,
297 "Error writing list entry '%s' in list file\n", list_entry
->filename
);
300 avio_printf(list_ioctx
, "file %s\n", buf
);
305 av_assert0(!"Invalid list type");
309 static int segment_end(AVFormatContext
*s
, int write_trailer
, int is_last
)
311 SegmentContext
*seg
= s
->priv_data
;
312 AVFormatContext
*oc
= seg
->avf
;
315 av_write_frame(oc
, NULL
); /* Flush any buffered data (fragmented mp4) */
317 ret
= av_write_trailer(oc
);
320 av_log(s
, AV_LOG_ERROR
, "Failure occurred when ending segment '%s'\n",
324 if (seg
->list_size
|| seg
->list_type
== LIST_TYPE_M3U8
) {
325 SegmentListEntry
*entry
= av_mallocz(sizeof(*entry
));
327 ret
= AVERROR(ENOMEM
);
331 /* append new element */
332 memcpy(entry
, &seg
->cur_entry
, sizeof(*entry
));
333 if (!seg
->segment_list_entries
)
334 seg
->segment_list_entries
= seg
->segment_list_entries_end
= entry
;
336 seg
->segment_list_entries_end
->next
= entry
;
337 seg
->segment_list_entries_end
= entry
;
339 /* drop first item */
340 if (seg
->list_size
&& seg
->segment_count
>= seg
->list_size
) {
341 entry
= seg
->segment_list_entries
;
342 seg
->segment_list_entries
= seg
->segment_list_entries
->next
;
343 av_free(entry
->filename
);
347 avio_close(seg
->list_pb
);
348 if ((ret
= segment_list_open(s
)) < 0)
350 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
351 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, entry
, s
);
352 if (seg
->list_type
== LIST_TYPE_M3U8
&& is_last
)
353 avio_printf(seg
->list_pb
, "#EXT-X-ENDLIST\n");
355 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, &seg
->cur_entry
, s
);
357 avio_flush(seg
->list_pb
);
360 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' count:%d ended\n",
361 seg
->avf
->filename
, seg
->segment_count
);
362 seg
->segment_count
++;
370 static int parse_times(void *log_ctx
, int64_t **times
, int *nb_times
,
371 const char *times_str
)
375 char *times_str1
= av_strdup(times_str
);
376 char *saveptr
= NULL
;
379 return AVERROR(ENOMEM
);
381 #define FAIL(err) ret = err; goto end
384 for (p
= times_str1
; *p
; p
++)
388 *times
= av_malloc_array(*nb_times
, sizeof(**times
));
390 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced times array\n");
391 FAIL(AVERROR(ENOMEM
));
395 for (i
= 0; i
< *nb_times
; i
++) {
397 char *tstr
= av_strtok(p
, ",", &saveptr
);
400 if (!tstr
|| !tstr
[0]) {
401 av_log(log_ctx
, AV_LOG_ERROR
, "Empty time specification in times list %s\n",
403 FAIL(AVERROR(EINVAL
));
406 ret
= av_parse_time(&t
, tstr
, 1);
408 av_log(log_ctx
, AV_LOG_ERROR
,
409 "Invalid time duration specification '%s' in times list %s\n", tstr
, times_str
);
410 FAIL(AVERROR(EINVAL
));
414 /* check on monotonicity */
415 if (i
&& (*times
)[i
-1] > (*times
)[i
]) {
416 av_log(log_ctx
, AV_LOG_ERROR
,
417 "Specified time %f is greater than the following time %f\n",
418 (float)((*times
)[i
])/1000000, (float)((*times
)[i
-1])/1000000);
419 FAIL(AVERROR(EINVAL
));
428 static int parse_frames(void *log_ctx
, int **frames
, int *nb_frames
,
429 const char *frames_str
)
433 char *frames_str1
= av_strdup(frames_str
);
434 char *saveptr
= NULL
;
437 return AVERROR(ENOMEM
);
439 #define FAIL(err) ret = err; goto end
442 for (p
= frames_str1
; *p
; p
++)
446 *frames
= av_malloc_array(*nb_frames
, sizeof(**frames
));
448 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced frames array\n");
449 FAIL(AVERROR(ENOMEM
));
453 for (i
= 0; i
< *nb_frames
; i
++) {
456 char *fstr
= av_strtok(p
, ",", &saveptr
);
460 av_log(log_ctx
, AV_LOG_ERROR
, "Empty frame specification in frame list %s\n",
462 FAIL(AVERROR(EINVAL
));
464 f
= strtol(fstr
, &tailptr
, 10);
465 if (*tailptr
|| f
<= 0 || f
>= INT_MAX
) {
466 av_log(log_ctx
, AV_LOG_ERROR
,
467 "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
469 FAIL(AVERROR(EINVAL
));
473 /* check on monotonicity */
474 if (i
&& (*frames
)[i
-1] > (*frames
)[i
]) {
475 av_log(log_ctx
, AV_LOG_ERROR
,
476 "Specified frame %d is greater than the following frame %d\n",
477 (*frames
)[i
], (*frames
)[i
-1]);
478 FAIL(AVERROR(EINVAL
));
483 av_free(frames_str1
);
487 static int open_null_ctx(AVIOContext
**ctx
)
489 int buf_size
= 32768;
490 uint8_t *buf
= av_malloc(buf_size
);
492 return AVERROR(ENOMEM
);
493 *ctx
= avio_alloc_context(buf
, buf_size
, AVIO_FLAG_WRITE
, NULL
, NULL
, NULL
, NULL
);
496 return AVERROR(ENOMEM
);
501 static void close_null_ctx(AVIOContext
*pb
)
507 static int select_reference_stream(AVFormatContext
*s
)
509 SegmentContext
*seg
= s
->priv_data
;
512 seg
->reference_stream_index
= -1;
513 if (!strcmp(seg
->reference_stream_specifier
, "auto")) {
514 /* select first index of type with highest priority */
515 int type_index_map
[AVMEDIA_TYPE_NB
];
516 static const enum AVMediaType type_priority_list
[] = {
519 AVMEDIA_TYPE_SUBTITLE
,
521 AVMEDIA_TYPE_ATTACHMENT
523 enum AVMediaType type
;
525 for (i
= 0; i
< AVMEDIA_TYPE_NB
; i
++)
526 type_index_map
[i
] = -1;
528 /* select first index for each type */
529 for (i
= 0; i
< s
->nb_streams
; i
++) {
530 type
= s
->streams
[i
]->codec
->codec_type
;
531 if ((unsigned)type
< AVMEDIA_TYPE_NB
&& type_index_map
[type
] == -1
532 /* ignore attached pictures/cover art streams */
533 && !(s
->streams
[i
]->disposition
& AV_DISPOSITION_ATTACHED_PIC
))
534 type_index_map
[type
] = i
;
537 for (i
= 0; i
< FF_ARRAY_ELEMS(type_priority_list
); i
++) {
538 type
= type_priority_list
[i
];
539 if ((seg
->reference_stream_index
= type_index_map
[type
]) >= 0)
543 for (i
= 0; i
< s
->nb_streams
; i
++) {
544 ret
= avformat_match_stream_specifier(s
, s
->streams
[i
],
545 seg
->reference_stream_specifier
);
549 seg
->reference_stream_index
= i
;
555 if (seg
->reference_stream_index
< 0) {
556 av_log(s
, AV_LOG_ERROR
, "Could not select stream matching identifier '%s'\n",
557 seg
->reference_stream_specifier
);
558 return AVERROR(EINVAL
);
564 static int seg_write_header(AVFormatContext
*s
)
566 SegmentContext
*seg
= s
->priv_data
;
567 AVFormatContext
*oc
= NULL
;
568 AVDictionary
*options
= NULL
;
571 seg
->segment_count
= 0;
572 if (!seg
->write_header_trailer
)
573 seg
->individual_header_trailer
= 0;
575 if (!!seg
->time_str
+ !!seg
->times_str
+ !!seg
->frames_str
> 1) {
576 av_log(s
, AV_LOG_ERROR
,
577 "segment_time, segment_times, and segment_frames options "
578 "are mutually exclusive, select just one of them\n");
579 return AVERROR(EINVAL
);
582 if (seg
->times_str
) {
583 if ((ret
= parse_times(s
, &seg
->times
, &seg
->nb_times
, seg
->times_str
)) < 0)
585 } else if (seg
->frames_str
) {
586 if ((ret
= parse_frames(s
, &seg
->frames
, &seg
->nb_frames
, seg
->frames_str
)) < 0)
589 /* set default value if not specified */
591 seg
->time_str
= av_strdup("2");
592 if ((ret
= av_parse_time(&seg
->time
, seg
->time_str
, 1)) < 0) {
593 av_log(s
, AV_LOG_ERROR
,
594 "Invalid time duration specification '%s' for segment_time option\n",
600 if (seg
->format_options_str
) {
601 ret
= av_dict_parse_string(&seg
->format_options
, seg
->format_options_str
, "=", ":", 0);
603 av_log(s
, AV_LOG_ERROR
, "Could not parse format options list '%s'\n",
604 seg
->format_options_str
);
610 if (seg
->list_type
== LIST_TYPE_UNDEFINED
) {
611 if (av_match_ext(seg
->list
, "csv" )) seg
->list_type
= LIST_TYPE_CSV
;
612 else if (av_match_ext(seg
->list
, "ext" )) seg
->list_type
= LIST_TYPE_EXT
;
613 else if (av_match_ext(seg
->list
, "m3u8")) seg
->list_type
= LIST_TYPE_M3U8
;
614 else if (av_match_ext(seg
->list
, "ffcat,ffconcat")) seg
->list_type
= LIST_TYPE_FFCONCAT
;
615 else seg
->list_type
= LIST_TYPE_FLAT
;
617 if ((ret
= segment_list_open(s
)) < 0)
620 if (seg
->list_type
== LIST_TYPE_EXT
)
621 av_log(s
, AV_LOG_WARNING
, "'ext' list type option is deprecated in favor of 'csv'\n");
623 if ((ret
= select_reference_stream(s
)) < 0)
625 av_log(s
, AV_LOG_VERBOSE
, "Selected stream id:%d type:%s\n",
626 seg
->reference_stream_index
,
627 av_get_media_type_string(s
->streams
[seg
->reference_stream_index
]->codec
->codec_type
));
629 seg
->oformat
= av_guess_format(seg
->format
, s
->filename
, NULL
);
632 ret
= AVERROR_MUXER_NOT_FOUND
;
635 if (seg
->oformat
->flags
& AVFMT_NOFILE
) {
636 av_log(s
, AV_LOG_ERROR
, "format %s not supported.\n",
638 ret
= AVERROR(EINVAL
);
642 if ((ret
= segment_mux_init(s
)) < 0)
646 if ((ret
= set_segment_filename(s
)) < 0)
649 if (seg
->write_header_trailer
) {
650 if ((ret
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
651 &s
->interrupt_callback
, NULL
)) < 0) {
652 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->filename
);
656 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
660 av_dict_copy(&options
, seg
->format_options
, 0);
661 ret
= avformat_write_header(oc
, &options
);
662 if (av_dict_count(options
)) {
663 av_log(s
, AV_LOG_ERROR
,
664 "Some of the provided format options in '%s' are not recognized\n", seg
->format_options_str
);
665 ret
= AVERROR(EINVAL
);
673 seg
->segment_frame_count
= 0;
675 if (oc
->avoid_negative_ts
> 0 && s
->avoid_negative_ts
< 0)
676 s
->avoid_negative_ts
= 1;
678 if (!seg
->write_header_trailer
) {
679 close_null_ctx(oc
->pb
);
680 if ((ret
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
681 &s
->interrupt_callback
, NULL
)) < 0)
686 av_dict_free(&options
);
689 avio_close(seg
->list_pb
);
691 avformat_free_context(seg
->avf
);
696 static int seg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
698 SegmentContext
*seg
= s
->priv_data
;
699 AVStream
*st
= s
->streams
[pkt
->stream_index
];
700 int64_t end_pts
= INT64_MAX
, offset
;
701 int start_frame
= INT_MAX
;
708 end_pts
= seg
->segment_count
< seg
->nb_times
?
709 seg
->times
[seg
->segment_count
] : INT64_MAX
;
710 } else if (seg
->frames
) {
711 start_frame
= seg
->segment_count
< seg
->nb_frames
?
712 seg
->frames
[seg
->segment_count
] : INT_MAX
;
714 if (seg
->use_clocktime
) {
715 int64_t avgt
= av_gettime();
716 time_t sec
= avgt
/ 1000000;
718 localtime_r(&sec
, &ti
);
720 ti
= *localtime(&sec
);
722 usecs
= (int64_t)(ti
.tm_hour
*3600 + ti
.tm_min
*60 + ti
.tm_sec
) * 1000000 + (avgt
% 1000000);
723 wrapped_val
= usecs
% seg
->time
;
724 if (seg
->last_cut
!= usecs
&& wrapped_val
< seg
->last_val
) {
725 seg
->cut_pending
= 1;
726 seg
->last_cut
= usecs
;
728 seg
->last_val
= wrapped_val
;
730 end_pts
= seg
->time
* (seg
->segment_count
+1);
734 av_dlog(s
, "packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
735 pkt
->stream_index
, av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
736 av_ts2timestr(pkt
->duration
, &st
->time_base
),
737 pkt
->flags
& AV_PKT_FLAG_KEY
,
738 pkt
->stream_index
== seg
->reference_stream_index
? seg
->frame_count
: -1);
740 if (pkt
->stream_index
== seg
->reference_stream_index
&&
741 pkt
->flags
& AV_PKT_FLAG_KEY
&&
742 seg
->segment_frame_count
> 0 &&
743 (seg
->cut_pending
|| seg
->frame_count
>= start_frame
||
744 (pkt
->pts
!= AV_NOPTS_VALUE
&&
745 av_compare_ts(pkt
->pts
, st
->time_base
,
746 end_pts
-seg
->time_delta
, AV_TIME_BASE_Q
) >= 0))) {
747 /* sanitize end time in case last packet didn't have a defined duration */
748 if (seg
->cur_entry
.last_duration
== 0)
749 seg
->cur_entry
.end_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
751 if ((ret
= segment_end(s
, seg
->individual_header_trailer
, 0)) < 0)
754 if ((ret
= segment_start(s
, seg
->individual_header_trailer
)) < 0)
757 seg
->cut_pending
= 0;
758 seg
->cur_entry
.index
= seg
->segment_idx
+ seg
->segment_idx_wrap
*seg
->segment_idx_wrap_nb
;
759 seg
->cur_entry
.start_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
760 seg
->cur_entry
.start_pts
= av_rescale_q(pkt
->pts
, st
->time_base
, AV_TIME_BASE_Q
);
761 seg
->cur_entry
.end_time
= seg
->cur_entry
.start_time
+
762 pkt
->pts
!= AV_NOPTS_VALUE
? (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
) : 0;
763 } else if (pkt
->pts
!= AV_NOPTS_VALUE
&& pkt
->stream_index
== seg
->reference_stream_index
) {
764 seg
->cur_entry
.end_time
=
765 FFMAX(seg
->cur_entry
.end_time
, (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
));
766 seg
->cur_entry
.last_duration
= pkt
->duration
;
769 if (seg
->segment_frame_count
== 0) {
770 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
771 seg
->avf
->filename
, pkt
->stream_index
,
772 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
), seg
->frame_count
);
775 av_log(s
, AV_LOG_DEBUG
, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
777 av_ts2timestr(seg
->cur_entry
.start_pts
, &AV_TIME_BASE_Q
),
778 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
779 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
781 /* compute new timestamps */
782 offset
= av_rescale_q(seg
->initial_offset
- (seg
->reset_timestamps
? seg
->cur_entry
.start_pts
: 0),
783 AV_TIME_BASE_Q
, st
->time_base
);
784 if (pkt
->pts
!= AV_NOPTS_VALUE
)
786 if (pkt
->dts
!= AV_NOPTS_VALUE
)
789 av_log(s
, AV_LOG_DEBUG
, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
790 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
791 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
793 ret
= ff_write_chained(seg
->avf
, pkt
->stream_index
, pkt
, s
, seg
->initial_offset
|| seg
->reset_timestamps
);
796 if (pkt
->stream_index
== seg
->reference_stream_index
) {
798 seg
->segment_frame_count
++;
804 static int seg_write_trailer(struct AVFormatContext
*s
)
806 SegmentContext
*seg
= s
->priv_data
;
807 AVFormatContext
*oc
= seg
->avf
;
808 SegmentListEntry
*cur
, *next
;
811 if (!seg
->write_header_trailer
) {
812 if ((ret
= segment_end(s
, 0, 1)) < 0)
814 open_null_ctx(&oc
->pb
);
815 ret
= av_write_trailer(oc
);
816 close_null_ctx(oc
->pb
);
818 ret
= segment_end(s
, 1, 1);
822 avio_close(seg
->list_pb
);
824 av_dict_free(&seg
->format_options
);
826 av_freep(&seg
->times
);
827 av_freep(&seg
->frames
);
829 cur
= seg
->segment_list_entries
;
832 av_free(cur
->filename
);
837 avformat_free_context(oc
);
841 #define OFFSET(x) offsetof(SegmentContext, x)
842 #define E AV_OPT_FLAG_ENCODING_PARAM
843 static const AVOption options
[] = {
844 { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier
), AV_OPT_TYPE_STRING
, {.str
= "auto"}, CHAR_MIN
, CHAR_MAX
, E
},
845 { "segment_format", "set container format used for the segments", OFFSET(format
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
846 { "segment_format_options", "set list of options for the container format used for the segments", OFFSET(format_options_str
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
847 { "segment_list", "set the segment list filename", OFFSET(list
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
849 { "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"},
850 { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_LIST_FLAG_CACHE
}, INT_MIN
, INT_MAX
, E
, "list_flags"},
851 { "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"},
853 { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
855 { "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" },
856 { "flat", "flat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FLAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
857 { "csv", "csv format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_CSV
}, INT_MIN
, INT_MAX
, E
, "list_type" },
858 { "ext", "extended format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_EXT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
859 { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FFCONCAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
860 { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
861 { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
863 { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, E
},
864 { "segment_time", "set segment duration", OFFSET(time_str
),AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
865 { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, 0, E
},
866 { "segment_times", "set segment split time points", OFFSET(times_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
867 { "segment_frames", "set segment split frame numbers", OFFSET(frames_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
868 { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
869 { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
870 { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
871 { "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
873 { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, E
},
874 { "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
},
875 { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, E
},
876 { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, -INT64_MAX
, INT64_MAX
, E
},
880 static const AVClass seg_class
= {
881 .class_name
= "segment muxer",
882 .item_name
= av_default_item_name
,
884 .version
= LIBAVUTIL_VERSION_INT
,
887 AVOutputFormat ff_segment_muxer
= {
889 .long_name
= NULL_IF_CONFIG_SMALL("segment"),
890 .priv_data_size
= sizeof(SegmentContext
),
891 .flags
= AVFMT_NOFILE
|AVFMT_GLOBALHEADER
,
892 .write_header
= seg_write_header
,
893 .write_packet
= seg_write_packet
,
894 .write_trailer
= seg_write_trailer
,
895 .priv_class
= &seg_class
,
898 static const AVClass sseg_class
= {
899 .class_name
= "stream_segment muxer",
900 .item_name
= av_default_item_name
,
902 .version
= LIBAVUTIL_VERSION_INT
,
905 AVOutputFormat ff_stream_segment_muxer
= {
906 .name
= "stream_segment,ssegment",
907 .long_name
= NULL_IF_CONFIG_SMALL("streaming segment muxer"),
908 .priv_data_size
= sizeof(SegmentContext
),
909 .flags
= AVFMT_NOFILE
,
910 .write_header
= seg_write_header
,
911 .write_packet
= seg_write_packet
,
912 .write_trailer
= seg_write_trailer
,
913 .priv_class
= &sseg_class
,