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}
27 #include "config_components.h"
35 #include "libavutil/avassert.h"
36 #include "libavutil/internal.h"
37 #include "libavutil/log.h"
38 #include "libavutil/opt.h"
39 #include "libavutil/avstring.h"
40 #include "libavutil/parseutils.h"
41 #include "libavutil/mathematics.h"
42 #include "libavutil/time.h"
43 #include "libavutil/timecode.h"
44 #include "libavutil/time_internal.h"
45 #include "libavutil/timestamp.h"
47 typedef struct SegmentListEntry
{
49 double start_time
, end_time
;
53 struct SegmentListEntry
*next
;
54 int64_t last_duration
;
58 LIST_TYPE_UNDEFINED
= -1,
62 LIST_TYPE_EXT
, ///< deprecated
67 #define SEGMENT_LIST_FLAG_CACHE 1
68 #define SEGMENT_LIST_FLAG_LIVE 2
70 typedef struct SegmentContext
{
71 const AVClass
*class; /**< Class for private options. */
72 int segment_idx
; ///< index of the segment file to write, starting from 0
73 int segment_idx_wrap
; ///< number after which the index wraps
74 int segment_idx_wrap_nb
; ///< number of time the index has wraped
75 int segment_count
; ///< number of segment files already written
76 const AVOutputFormat
*oformat
;
78 char *format
; ///< format to use for output segment files
79 AVDictionary
*format_options
;
80 char *list
; ///< filename for the segment list file
81 int list_flags
; ///< flags affecting list generation
82 int list_size
; ///< number of entries for the segment list file
84 int is_nullctx
; ///< whether avf->pb is a nullctx
85 int use_clocktime
; ///< flag to cut segments at regular clock time
86 int64_t clocktime_offset
; //< clock offset for cutting the segments at regular clock time
87 int64_t clocktime_wrap_duration
; //< wrapping duration considered for starting a new segment
88 int64_t last_val
; ///< remember last time for wrap around detection
90 int header_written
; ///< whether we've already called avformat_write_header
92 char *entry_prefix
; ///< prefix to add to list entry filenames
93 int list_type
; ///< set the list type
94 AVIOContext
*list_pb
; ///< list file put-byte context
95 int64_t time
; ///< segment duration
96 int use_strftime
; ///< flag to expand filename with strftime
97 int increment_tc
; ///< flag to increment timecode if found
99 char *times_str
; ///< segment times specification string
100 int64_t *times
; ///< list of segment interval specification
101 int nb_times
; ///< number of elments in the times array
103 char *frames_str
; ///< segment frame numbers specification string
104 int *frames
; ///< list of frame number specification
105 int nb_frames
; ///< number of elments in the frames array
106 int frame_count
; ///< total number of reference frames
107 int segment_frame_count
; ///< number of reference frames in the segment
110 int individual_header_trailer
; /**< Set by a private option. */
111 int write_header_trailer
; /**< Set by a private option. */
112 char *header_filename
; ///< filename to write the output header to
114 int reset_timestamps
; ///< reset timestamps at the beginning of each segment
115 int64_t initial_offset
; ///< initial timestamps offset, expressed in microseconds
116 char *reference_stream_specifier
; ///< reference stream specifier
117 int reference_stream_index
;
118 int break_non_keyframes
;
122 char temp_list_filename
[1024];
124 SegmentListEntry cur_entry
;
125 SegmentListEntry
*segment_list_entries
;
126 SegmentListEntry
*segment_list_entries_end
;
129 static void print_csv_escaped_str(AVIOContext
*ctx
, const char *str
)
131 int needs_quoting
= !!str
[strcspn(str
, "\",\n\r")];
136 for (; *str
; str
++) {
145 static int segment_mux_init(AVFormatContext
*s
)
147 SegmentContext
*seg
= s
->priv_data
;
152 ret
= avformat_alloc_output_context2(&seg
->avf
, seg
->oformat
, NULL
, NULL
);
157 oc
->interrupt_callback
= s
->interrupt_callback
;
158 oc
->max_delay
= s
->max_delay
;
159 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
160 oc
->opaque
= s
->opaque
;
161 oc
->io_close
= s
->io_close
;
162 oc
->io_close2
= s
->io_close2
;
163 oc
->io_open
= s
->io_open
;
164 oc
->flags
= s
->flags
;
166 for (i
= 0; i
< s
->nb_streams
; i
++) {
167 AVStream
*st
, *ist
= s
->streams
[i
];
168 AVCodecParameters
*ipar
= ist
->codecpar
, *opar
;
170 if (!(st
= avformat_new_stream(oc
, NULL
)))
171 return AVERROR(ENOMEM
);
172 ret
= ff_stream_encode_params_copy(st
, ist
);
176 if (!oc
->oformat
->codec_tag
||
177 av_codec_get_id (oc
->oformat
->codec_tag
, ipar
->codec_tag
) == opar
->codec_id
||
178 av_codec_get_tag(oc
->oformat
->codec_tag
, ipar
->codec_id
) <= 0) {
179 opar
->codec_tag
= ipar
->codec_tag
;
188 static int set_segment_filename(AVFormatContext
*s
)
190 SegmentContext
*seg
= s
->priv_data
;
191 AVFormatContext
*oc
= seg
->avf
;
197 if (seg
->segment_idx_wrap
)
198 seg
->segment_idx
%= seg
->segment_idx_wrap
;
199 if (seg
->use_strftime
) {
201 struct tm
*tm
, tmpbuf
;
203 tm
= localtime_r(&now0
, &tmpbuf
);
204 if (!strftime(buf
, sizeof(buf
), s
->url
, tm
)) {
205 av_log(oc
, AV_LOG_ERROR
, "Could not get segment filename with strftime\n");
206 return AVERROR(EINVAL
);
208 } else if (av_get_frame_filename(buf
, sizeof(buf
),
209 s
->url
, seg
->segment_idx
) < 0) {
210 av_log(oc
, AV_LOG_ERROR
, "Invalid segment filename template '%s'\n", s
->url
);
211 return AVERROR(EINVAL
);
213 new_name
= av_strdup(buf
);
215 return AVERROR(ENOMEM
);
216 ff_format_set_url(oc
, new_name
);
218 /* copy modified name in list entry */
219 size
= strlen(av_basename(oc
->url
)) + 1;
220 if (seg
->entry_prefix
)
221 size
+= strlen(seg
->entry_prefix
);
223 if ((ret
= av_reallocp(&seg
->cur_entry
.filename
, size
)) < 0)
225 snprintf(seg
->cur_entry
.filename
, size
, "%s%s",
226 seg
->entry_prefix
? seg
->entry_prefix
: "",
227 av_basename(oc
->url
));
232 static int segment_start(AVFormatContext
*s
, int write_header
)
234 SegmentContext
*seg
= s
->priv_data
;
235 AVFormatContext
*oc
= seg
->avf
;
239 avformat_free_context(oc
);
241 if ((err
= segment_mux_init(s
)) < 0)
247 if ((seg
->segment_idx_wrap
) && (seg
->segment_idx
% seg
->segment_idx_wrap
== 0))
248 seg
->segment_idx_wrap_nb
++;
250 if ((err
= set_segment_filename(s
)) < 0)
253 if ((err
= s
->io_open(s
, &oc
->pb
, oc
->url
, AVIO_FLAG_WRITE
, NULL
)) < 0) {
254 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->url
);
257 if (!seg
->individual_header_trailer
)
258 oc
->pb
->seekable
= 0;
260 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
261 av_opt_set(oc
->priv_data
, "mpegts_flags", "+resend_headers", 0);
264 AVDictionary
*options
= NULL
;
265 av_dict_copy(&options
, seg
->format_options
, 0);
266 av_dict_set(&options
, "fflags", "-autobsf", 0);
267 err
= avformat_write_header(oc
, &options
);
268 av_dict_free(&options
);
273 seg
->segment_frame_count
= 0;
277 static int segment_list_open(AVFormatContext
*s
)
279 SegmentContext
*seg
= s
->priv_data
;
282 snprintf(seg
->temp_list_filename
, sizeof(seg
->temp_list_filename
), seg
->use_rename
? "%s.tmp" : "%s", seg
->list
);
283 ret
= s
->io_open(s
, &seg
->list_pb
, seg
->temp_list_filename
, AVIO_FLAG_WRITE
, NULL
);
285 av_log(s
, AV_LOG_ERROR
, "Failed to open segment list '%s'\n", seg
->list
);
289 if (seg
->list_type
== LIST_TYPE_M3U8
&& seg
->segment_list_entries
) {
290 SegmentListEntry
*entry
;
291 double max_duration
= 0;
293 avio_printf(seg
->list_pb
, "#EXTM3U\n");
294 avio_printf(seg
->list_pb
, "#EXT-X-VERSION:3\n");
295 avio_printf(seg
->list_pb
, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg
->segment_list_entries
->index
);
296 avio_printf(seg
->list_pb
, "#EXT-X-ALLOW-CACHE:%s\n",
297 seg
->list_flags
& SEGMENT_LIST_FLAG_CACHE
? "YES" : "NO");
299 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%d\n",
300 seg
->segment_list_entries
->index
);
302 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
303 max_duration
= FFMAX(max_duration
, entry
->end_time
- entry
->start_time
);
304 avio_printf(seg
->list_pb
, "#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration
));
305 } else if (seg
->list_type
== LIST_TYPE_FFCONCAT
) {
306 avio_printf(seg
->list_pb
, "ffconcat version 1.0\n");
312 static void segment_list_print_entry(AVIOContext
*list_ioctx
,
314 const SegmentListEntry
*list_entry
,
319 avio_printf(list_ioctx
, "%s\n", list_entry
->filename
);
323 print_csv_escaped_str(list_ioctx
, list_entry
->filename
);
324 avio_printf(list_ioctx
, ",%f,%f\n", list_entry
->start_time
, list_entry
->end_time
);
327 avio_printf(list_ioctx
, "#EXTINF:%f,\n%s\n",
328 list_entry
->end_time
- list_entry
->start_time
, list_entry
->filename
);
330 case LIST_TYPE_FFCONCAT
:
333 if (av_escape(&buf
, list_entry
->filename
, NULL
, AV_ESCAPE_MODE_AUTO
, AV_ESCAPE_FLAG_WHITESPACE
) < 0) {
334 av_log(log_ctx
, AV_LOG_WARNING
,
335 "Error writing list entry '%s' in list file\n", list_entry
->filename
);
338 avio_printf(list_ioctx
, "file %s\n", buf
);
343 av_assert0(!"Invalid list type");
347 static int segment_end(AVFormatContext
*s
, int write_trailer
, int is_last
)
349 SegmentContext
*seg
= s
->priv_data
;
350 AVFormatContext
*oc
= seg
->avf
;
354 AVDictionaryEntry
*tcr
;
355 char buf
[AV_TIMECODE_STR_SIZE
];
360 return AVERROR(EINVAL
);
362 av_write_frame(oc
, NULL
); /* Flush any buffered data (fragmented mp4) */
364 ret
= av_write_trailer(oc
);
367 av_log(s
, AV_LOG_ERROR
, "Failure occurred when ending segment '%s'\n",
371 if (seg
->list_size
|| seg
->list_type
== LIST_TYPE_M3U8
) {
372 SegmentListEntry
*entry
= av_mallocz(sizeof(*entry
));
374 ret
= AVERROR(ENOMEM
);
378 /* append new element */
379 memcpy(entry
, &seg
->cur_entry
, sizeof(*entry
));
380 entry
->filename
= av_strdup(entry
->filename
);
381 if (!seg
->segment_list_entries
)
382 seg
->segment_list_entries
= seg
->segment_list_entries_end
= entry
;
384 seg
->segment_list_entries_end
->next
= entry
;
385 seg
->segment_list_entries_end
= entry
;
387 /* drop first item */
388 if (seg
->list_size
&& seg
->segment_count
>= seg
->list_size
) {
389 entry
= seg
->segment_list_entries
;
390 seg
->segment_list_entries
= seg
->segment_list_entries
->next
;
391 av_freep(&entry
->filename
);
395 if ((ret
= segment_list_open(s
)) < 0)
397 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
398 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, entry
, s
);
399 if (seg
->list_type
== LIST_TYPE_M3U8
&& is_last
)
400 avio_printf(seg
->list_pb
, "#EXT-X-ENDLIST\n");
401 ff_format_io_close(s
, &seg
->list_pb
);
403 ff_rename(seg
->temp_list_filename
, seg
->list
, s
);
405 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, &seg
->cur_entry
, s
);
406 avio_flush(seg
->list_pb
);
410 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' count:%d ended\n",
411 seg
->avf
->url
, seg
->segment_count
);
412 seg
->segment_count
++;
414 if (seg
->increment_tc
) {
415 tcr
= av_dict_get(s
->metadata
, "timecode", NULL
, 0);
417 /* search the first video stream */
418 for (i
= 0; i
< s
->nb_streams
; i
++) {
419 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
420 rate
= s
->streams
[i
]->avg_frame_rate
;/* Get fps from the video stream */
421 err
= av_timecode_init_from_string(&tc
, rate
, tcr
->value
, s
);
423 av_log(s
, AV_LOG_WARNING
, "Could not increment global timecode, error occurred during timecode creation.\n");
426 tc
.start
+= (int)((seg
->cur_entry
.end_time
- seg
->cur_entry
.start_time
) * av_q2d(rate
));/* increment timecode */
427 av_dict_set(&s
->metadata
, "timecode",
428 av_timecode_make_string(&tc
, buf
, 0), 0);
433 av_log(s
, AV_LOG_WARNING
, "Could not increment global timecode, no global timecode metadata found.\n");
435 for (i
= 0; i
< s
->nb_streams
; i
++) {
436 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
437 char st_buf
[AV_TIMECODE_STR_SIZE
];
439 AVRational st_rate
= s
->streams
[i
]->avg_frame_rate
;
440 AVDictionaryEntry
*st_tcr
= av_dict_get(s
->streams
[i
]->metadata
, "timecode", NULL
, 0);
442 if ((av_timecode_init_from_string(&st_tc
, st_rate
, st_tcr
->value
, s
) < 0)) {
443 av_log(s
, AV_LOG_WARNING
, "Could not increment stream %d timecode, error occurred during timecode creation.\n", i
);
446 st_tc
.start
+= (int)((seg
->cur_entry
.end_time
- seg
->cur_entry
.start_time
) * av_q2d(st_rate
)); // increment timecode
447 av_dict_set(&s
->streams
[i
]->metadata
, "timecode", av_timecode_make_string(&st_tc
, st_buf
, 0), 0);
454 ff_format_io_close(oc
, &oc
->pb
);
459 static int parse_times(void *log_ctx
, int64_t **times
, int *nb_times
,
460 const char *times_str
)
464 char *times_str1
= av_strdup(times_str
);
465 char *saveptr
= NULL
;
468 return AVERROR(ENOMEM
);
470 #define FAIL(err) ret = err; goto end
473 for (p
= times_str1
; *p
; p
++)
477 *times
= av_malloc_array(*nb_times
, sizeof(**times
));
479 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced times array\n");
480 FAIL(AVERROR(ENOMEM
));
484 for (i
= 0; i
< *nb_times
; i
++) {
486 char *tstr
= av_strtok(p
, ",", &saveptr
);
489 if (!tstr
|| !tstr
[0]) {
490 av_log(log_ctx
, AV_LOG_ERROR
, "Empty time specification in times list %s\n",
492 FAIL(AVERROR(EINVAL
));
495 ret
= av_parse_time(&t
, tstr
, 1);
497 av_log(log_ctx
, AV_LOG_ERROR
,
498 "Invalid time duration specification '%s' in times list %s\n", tstr
, times_str
);
499 FAIL(AVERROR(EINVAL
));
503 /* check on monotonicity */
504 if (i
&& (*times
)[i
-1] > (*times
)[i
]) {
505 av_log(log_ctx
, AV_LOG_ERROR
,
506 "Specified time %f is smaller than the last time %f\n",
507 (float)((*times
)[i
])/1000000, (float)((*times
)[i
-1])/1000000);
508 FAIL(AVERROR(EINVAL
));
517 static int parse_frames(void *log_ctx
, int **frames
, int *nb_frames
,
518 const char *frames_str
)
524 for (p
= frames_str
; *p
; p
++)
528 *frames
= av_malloc_array(*nb_frames
, sizeof(**frames
));
530 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced frames array\n");
531 return AVERROR(ENOMEM
);
535 for (i
= 0; i
< *nb_frames
; i
++) {
539 if (*p
== '\0' || *p
== ',') {
540 av_log(log_ctx
, AV_LOG_ERROR
, "Empty frame specification in frame list %s\n",
542 return AVERROR(EINVAL
);
544 f
= strtol(p
, &tailptr
, 10);
545 if (*tailptr
!= '\0' && *tailptr
!= ',' || f
<= 0 || f
>= INT_MAX
) {
546 av_log(log_ctx
, AV_LOG_ERROR
,
547 "Invalid argument '%s', must be a positive integer < INT_MAX\n",
549 return AVERROR(EINVAL
);
556 /* check on monotonicity */
557 if (i
&& (*frames
)[i
-1] > (*frames
)[i
]) {
558 av_log(log_ctx
, AV_LOG_ERROR
,
559 "Specified frame %d is smaller than the last frame %d\n",
560 (*frames
)[i
], (*frames
)[i
-1]);
561 return AVERROR(EINVAL
);
568 static int open_null_ctx(AVIOContext
**ctx
)
570 int buf_size
= 32768;
571 uint8_t *buf
= av_malloc(buf_size
);
573 return AVERROR(ENOMEM
);
574 *ctx
= avio_alloc_context(buf
, buf_size
, 1, NULL
, NULL
, NULL
, NULL
);
577 return AVERROR(ENOMEM
);
582 static void close_null_ctxp(AVIOContext
**pb
)
584 av_freep(&(*pb
)->buffer
);
585 avio_context_free(pb
);
588 static int select_reference_stream(AVFormatContext
*s
)
590 SegmentContext
*seg
= s
->priv_data
;
593 seg
->reference_stream_index
= -1;
594 if (!strcmp(seg
->reference_stream_specifier
, "auto")) {
595 /* select first index of type with highest priority */
596 int type_index_map
[AVMEDIA_TYPE_NB
];
597 static const enum AVMediaType type_priority_list
[] = {
600 AVMEDIA_TYPE_SUBTITLE
,
602 AVMEDIA_TYPE_ATTACHMENT
604 enum AVMediaType type
;
606 for (i
= 0; i
< AVMEDIA_TYPE_NB
; i
++)
607 type_index_map
[i
] = -1;
609 /* select first index for each type */
610 for (i
= 0; i
< s
->nb_streams
; i
++) {
611 type
= s
->streams
[i
]->codecpar
->codec_type
;
612 if ((unsigned)type
< AVMEDIA_TYPE_NB
&& type_index_map
[type
] == -1
613 /* ignore attached pictures/cover art streams */
614 && !(s
->streams
[i
]->disposition
& AV_DISPOSITION_ATTACHED_PIC
))
615 type_index_map
[type
] = i
;
618 for (i
= 0; i
< FF_ARRAY_ELEMS(type_priority_list
); i
++) {
619 type
= type_priority_list
[i
];
620 if ((seg
->reference_stream_index
= type_index_map
[type
]) >= 0)
624 for (i
= 0; i
< s
->nb_streams
; i
++) {
625 ret
= avformat_match_stream_specifier(s
, s
->streams
[i
],
626 seg
->reference_stream_specifier
);
630 seg
->reference_stream_index
= i
;
636 if (seg
->reference_stream_index
< 0) {
637 av_log(s
, AV_LOG_ERROR
, "Could not select stream matching identifier '%s'\n",
638 seg
->reference_stream_specifier
);
639 return AVERROR(EINVAL
);
645 static void seg_free(AVFormatContext
*s
)
647 SegmentContext
*seg
= s
->priv_data
;
648 SegmentListEntry
*cur
;
650 ff_format_io_close(s
, &seg
->list_pb
);
653 close_null_ctxp(&seg
->avf
->pb
);
655 ff_format_io_close(s
, &seg
->avf
->pb
);
656 avformat_free_context(seg
->avf
);
659 av_freep(&seg
->times
);
660 av_freep(&seg
->frames
);
661 av_freep(&seg
->cur_entry
.filename
);
663 cur
= seg
->segment_list_entries
;
665 SegmentListEntry
*next
= cur
->next
;
666 av_freep(&cur
->filename
);
672 static int seg_init(AVFormatContext
*s
)
674 SegmentContext
*seg
= s
->priv_data
;
675 AVFormatContext
*oc
= seg
->avf
;
676 AVDictionary
*options
= NULL
;
680 seg
->segment_count
= 0;
681 if (!seg
->write_header_trailer
)
682 seg
->individual_header_trailer
= 0;
684 if (seg
->header_filename
) {
685 seg
->write_header_trailer
= 1;
686 seg
->individual_header_trailer
= 0;
689 if (seg
->initial_offset
> 0) {
690 av_log(s
, AV_LOG_WARNING
, "NOTE: the option initial_offset is deprecated,"
691 "you can use output_ts_offset instead of it\n");
694 if ((seg
->time
!= 2000000) + !!seg
->times_str
+ !!seg
->frames_str
> 1) {
695 av_log(s
, AV_LOG_ERROR
,
696 "segment_time, segment_times, and segment_frames options "
697 "are mutually exclusive, select just one of them\n");
698 return AVERROR(EINVAL
);
701 if (seg
->times_str
) {
702 if ((ret
= parse_times(s
, &seg
->times
, &seg
->nb_times
, seg
->times_str
)) < 0)
704 } else if (seg
->frames_str
) {
705 if ((ret
= parse_frames(s
, &seg
->frames
, &seg
->nb_frames
, seg
->frames_str
)) < 0)
708 if (seg
->use_clocktime
) {
709 if (seg
->time
<= 0) {
710 av_log(s
, AV_LOG_ERROR
, "Invalid negative segment_time with segment_atclocktime option set\n");
711 return AVERROR(EINVAL
);
713 seg
->clocktime_offset
= seg
->time
- (seg
->clocktime_offset
% seg
->time
);
718 if (seg
->list_type
== LIST_TYPE_UNDEFINED
) {
719 if (av_match_ext(seg
->list
, "csv" )) seg
->list_type
= LIST_TYPE_CSV
;
720 else if (av_match_ext(seg
->list
, "ext" )) seg
->list_type
= LIST_TYPE_EXT
;
721 else if (av_match_ext(seg
->list
, "m3u8")) seg
->list_type
= LIST_TYPE_M3U8
;
722 else if (av_match_ext(seg
->list
, "ffcat,ffconcat")) seg
->list_type
= LIST_TYPE_FFCONCAT
;
723 else seg
->list_type
= LIST_TYPE_FLAT
;
725 if (!seg
->list_size
&& seg
->list_type
!= LIST_TYPE_M3U8
) {
726 if ((ret
= segment_list_open(s
)) < 0)
729 const char *proto
= avio_find_protocol_name(seg
->list
);
730 seg
->use_rename
= proto
&& !strcmp(proto
, "file");
734 if (seg
->list_type
== LIST_TYPE_EXT
)
735 av_log(s
, AV_LOG_WARNING
, "'ext' list type option is deprecated in favor of 'csv'\n");
737 if ((ret
= select_reference_stream(s
)) < 0)
739 av_log(s
, AV_LOG_VERBOSE
, "Selected stream id:%d type:%s\n",
740 seg
->reference_stream_index
,
741 av_get_media_type_string(s
->streams
[seg
->reference_stream_index
]->codecpar
->codec_type
));
743 seg
->oformat
= av_guess_format(seg
->format
, s
->url
, NULL
);
746 return AVERROR_MUXER_NOT_FOUND
;
747 if (seg
->oformat
->flags
& AVFMT_NOFILE
) {
748 av_log(s
, AV_LOG_ERROR
, "format %s not supported.\n",
750 return AVERROR(EINVAL
);
753 if ((ret
= segment_mux_init(s
)) < 0)
756 if ((ret
= set_segment_filename(s
)) < 0)
760 if (seg
->write_header_trailer
) {
761 if ((ret
= s
->io_open(s
, &oc
->pb
,
762 seg
->header_filename
? seg
->header_filename
: oc
->url
,
763 AVIO_FLAG_WRITE
, NULL
)) < 0) {
764 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->url
);
767 if (!seg
->individual_header_trailer
)
768 oc
->pb
->seekable
= 0;
770 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
775 av_dict_copy(&options
, seg
->format_options
, 0);
776 av_dict_set(&options
, "fflags", "-autobsf", 0);
777 ret
= avformat_init_output(oc
, &options
);
778 if (av_dict_count(options
)) {
779 av_log(s
, AV_LOG_ERROR
,
780 "Some of the provided format options are not recognized\n");
781 av_dict_free(&options
);
782 return AVERROR(EINVAL
);
784 av_dict_free(&options
);
789 seg
->segment_frame_count
= 0;
791 av_assert0(s
->nb_streams
== oc
->nb_streams
);
792 if (ret
== AVSTREAM_INIT_IN_WRITE_HEADER
) {
793 ret
= avformat_write_header(oc
, NULL
);
796 seg
->header_written
= 1;
799 for (i
= 0; i
< s
->nb_streams
; i
++) {
800 AVStream
*inner_st
= oc
->streams
[i
];
801 AVStream
*outer_st
= s
->streams
[i
];
802 avpriv_set_pts_info(outer_st
, inner_st
->pts_wrap_bits
, inner_st
->time_base
.num
, inner_st
->time_base
.den
);
805 if (oc
->avoid_negative_ts
> 0 && s
->avoid_negative_ts
< 0)
806 s
->avoid_negative_ts
= 1;
811 static int seg_write_header(AVFormatContext
*s
)
813 SegmentContext
*seg
= s
->priv_data
;
814 AVFormatContext
*oc
= seg
->avf
;
817 if (!seg
->header_written
) {
818 ret
= avformat_write_header(oc
, NULL
);
823 if (!seg
->write_header_trailer
|| seg
->header_filename
) {
824 if (seg
->header_filename
) {
825 av_write_frame(oc
, NULL
);
826 ff_format_io_close(oc
, &oc
->pb
);
828 close_null_ctxp(&oc
->pb
);
831 if ((ret
= oc
->io_open(oc
, &oc
->pb
, oc
->url
, AVIO_FLAG_WRITE
, NULL
)) < 0)
833 if (!seg
->individual_header_trailer
)
834 oc
->pb
->seekable
= 0;
840 static int seg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
842 SegmentContext
*seg
= s
->priv_data
;
843 AVStream
*st
= s
->streams
[pkt
->stream_index
];
844 int64_t end_pts
= INT64_MAX
, offset
;
845 int start_frame
= INT_MAX
;
851 if (!seg
->avf
|| !seg
->avf
->pb
)
852 return AVERROR(EINVAL
);
854 if (!st
->codecpar
->extradata_size
) {
855 size_t pkt_extradata_size
;
856 uint8_t *pkt_extradata
= av_packet_get_side_data(pkt
, AV_PKT_DATA_NEW_EXTRADATA
, &pkt_extradata_size
);
857 if (pkt_extradata
&& pkt_extradata_size
> 0) {
858 ret
= ff_alloc_extradata(st
->codecpar
, pkt_extradata_size
);
860 av_log(s
, AV_LOG_WARNING
, "Unable to add extradata to stream. Output segments may be invalid.\n");
863 memcpy(st
->codecpar
->extradata
, pkt_extradata
, pkt_extradata_size
);
869 end_pts
= seg
->segment_count
< seg
->nb_times
?
870 seg
->times
[seg
->segment_count
] : INT64_MAX
;
871 } else if (seg
->frames
) {
872 start_frame
= seg
->segment_count
< seg
->nb_frames
?
873 seg
->frames
[seg
->segment_count
] : INT_MAX
;
875 if (seg
->use_clocktime
) {
876 int64_t avgt
= av_gettime();
877 time_t sec
= avgt
/ 1000000;
878 localtime_r(&sec
, &ti
);
879 usecs
= (int64_t)(ti
.tm_hour
* 3600 + ti
.tm_min
* 60 + ti
.tm_sec
) * 1000000 + (avgt
% 1000000);
880 wrapped_val
= (usecs
+ seg
->clocktime_offset
) % seg
->time
;
881 if (wrapped_val
< seg
->last_val
&& wrapped_val
< seg
->clocktime_wrap_duration
)
882 seg
->cut_pending
= 1;
883 seg
->last_val
= wrapped_val
;
885 end_pts
= seg
->time
* (seg
->segment_count
+ 1);
889 ff_dlog(s
, "packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
890 pkt
->stream_index
, av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
891 av_ts2timestr(pkt
->duration
, &st
->time_base
),
892 pkt
->flags
& AV_PKT_FLAG_KEY
,
893 pkt
->stream_index
== seg
->reference_stream_index
? seg
->frame_count
: -1);
895 if (pkt
->stream_index
== seg
->reference_stream_index
&&
896 (pkt
->flags
& AV_PKT_FLAG_KEY
|| seg
->break_non_keyframes
) &&
897 (seg
->segment_frame_count
> 0 || seg
->write_empty
) &&
898 (seg
->cut_pending
|| seg
->frame_count
>= start_frame
||
899 (pkt
->pts
!= AV_NOPTS_VALUE
&&
900 av_compare_ts(pkt
->pts
, st
->time_base
,
901 end_pts
- seg
->time_delta
, AV_TIME_BASE_Q
) >= 0))) {
902 /* sanitize end time in case last packet didn't have a defined duration */
903 if (seg
->cur_entry
.last_duration
== 0)
904 seg
->cur_entry
.end_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
906 if ((ret
= segment_end(s
, seg
->individual_header_trailer
, 0)) < 0)
909 if ((ret
= segment_start(s
, seg
->individual_header_trailer
)) < 0)
912 seg
->cut_pending
= 0;
913 seg
->cur_entry
.index
= seg
->segment_idx
+ seg
->segment_idx_wrap
* seg
->segment_idx_wrap_nb
;
914 seg
->cur_entry
.start_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
915 seg
->cur_entry
.start_pts
= av_rescale_q(pkt
->pts
, st
->time_base
, AV_TIME_BASE_Q
);
916 seg
->cur_entry
.end_time
= seg
->cur_entry
.start_time
;
918 if (seg
->times
|| (!seg
->frames
&& !seg
->use_clocktime
) && seg
->write_empty
)
922 if (pkt
->stream_index
== seg
->reference_stream_index
) {
923 if (pkt
->pts
!= AV_NOPTS_VALUE
)
924 seg
->cur_entry
.end_time
=
925 FFMAX(seg
->cur_entry
.end_time
, (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
));
926 seg
->cur_entry
.last_duration
= pkt
->duration
;
929 if (seg
->segment_frame_count
== 0) {
930 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
931 seg
->avf
->url
, pkt
->stream_index
,
932 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
), seg
->frame_count
);
935 av_log(s
, AV_LOG_DEBUG
, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
937 av_ts2timestr(seg
->cur_entry
.start_pts
, &AV_TIME_BASE_Q
),
938 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
939 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
941 /* compute new timestamps */
942 offset
= av_rescale_q(seg
->initial_offset
- (seg
->reset_timestamps
? seg
->cur_entry
.start_pts
: 0),
943 AV_TIME_BASE_Q
, st
->time_base
);
944 if (pkt
->pts
!= AV_NOPTS_VALUE
)
946 if (pkt
->dts
!= AV_NOPTS_VALUE
)
949 av_log(s
, AV_LOG_DEBUG
, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
950 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
951 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
953 ret
= ff_write_chained(seg
->avf
, pkt
->stream_index
, pkt
, s
,
954 seg
->initial_offset
|| seg
->reset_timestamps
|| seg
->avf
->oformat
->interleave_packet
);
957 /* Use st->index here as the packet returned from ff_write_chained()
958 * is blank if interleaving has been used. */
959 if (st
->index
== seg
->reference_stream_index
) {
961 seg
->segment_frame_count
++;
967 static int seg_write_trailer(struct AVFormatContext
*s
)
969 SegmentContext
*seg
= s
->priv_data
;
970 AVFormatContext
*oc
= seg
->avf
;
976 if (!seg
->write_header_trailer
) {
977 if ((ret
= segment_end(s
, 0, 1)) < 0)
979 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
982 ret
= av_write_trailer(oc
);
984 ret
= segment_end(s
, 1, 1);
989 static int seg_check_bitstream(AVFormatContext
*s
, AVStream
*st
,
992 SegmentContext
*seg
= s
->priv_data
;
993 AVFormatContext
*oc
= seg
->avf
;
994 if (oc
->oformat
->check_bitstream
) {
995 AVStream
*const ost
= oc
->streams
[st
->index
];
996 int ret
= oc
->oformat
->check_bitstream(oc
, ost
, pkt
);
998 FFStream
*const sti
= ffstream(st
);
999 FFStream
*const osti
= ffstream(ost
);
1000 sti
->bsfc
= osti
->bsfc
;
1008 #define OFFSET(x) offsetof(SegmentContext, x)
1009 #define E AV_OPT_FLAG_ENCODING_PARAM
1010 static const AVOption options
[] = {
1011 { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier
), AV_OPT_TYPE_STRING
, {.str
= "auto"}, 0, 0, E
},
1012 { "segment_format", "set container format used for the segments", OFFSET(format
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1013 { "segment_format_options", "set list of options for the container format used for the segments", OFFSET(format_options
), AV_OPT_TYPE_DICT
, {.str
= NULL
}, 0, 0, E
},
1014 { "segment_list", "set the segment list filename", OFFSET(list
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1015 { "segment_header_filename", "write a single file containing the header", OFFSET(header_filename
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1017 { "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"},
1018 { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_LIST_FLAG_CACHE
}, INT_MIN
, INT_MAX
, E
, "list_flags"},
1019 { "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"},
1021 { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1023 { "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" },
1024 { "flat", "flat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FLAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1025 { "csv", "csv format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_CSV
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1026 { "ext", "extended format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_EXT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1027 { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FFCONCAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1028 { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1029 { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1031 { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1032 { "segment_clocktime_offset", "set segment clocktime offset", OFFSET(clocktime_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, 86400000000LL, E
},
1033 { "segment_clocktime_wrap_duration", "set segment clocktime wrapping duration", OFFSET(clocktime_wrap_duration
), AV_OPT_TYPE_DURATION
, {.i64
= INT64_MAX
}, 0, INT64_MAX
, E
},
1034 { "segment_time", "set segment duration", OFFSET(time
),AV_OPT_TYPE_DURATION
, {.i64
= 2000000}, INT64_MIN
, INT64_MAX
, E
},
1035 { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, INT64_MAX
, E
},
1036 { "segment_times", "set segment split time points", OFFSET(times_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
1037 { "segment_frames", "set segment split frame numbers", OFFSET(frames_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
1038 { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1039 { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1040 { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1041 { "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
},
1042 { "strftime", "set filename expansion with strftime at segment creation", OFFSET(use_strftime
), AV_OPT_TYPE_BOOL
, {.i64
= 0 }, 0, 1, E
},
1043 { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc
), AV_OPT_TYPE_BOOL
, {.i64
= 0 }, 0, 1, E
},
1044 { "break_non_keyframes", "allow breaking segments on non-keyframes", OFFSET(break_non_keyframes
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1046 { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, E
},
1047 { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, E
},
1048 { "reset_timestamps", "reset timestamps at the beginning of each segment", OFFSET(reset_timestamps
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1049 { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, -INT64_MAX
, INT64_MAX
, E
},
1050 { "write_empty_segments", "allow writing empty 'filler' segments", OFFSET(write_empty
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1054 static const AVClass seg_class
= {
1055 .class_name
= "(stream) segment muxer",
1056 .item_name
= av_default_item_name
,
1058 .version
= LIBAVUTIL_VERSION_INT
,
1061 #if CONFIG_SEGMENT_MUXER
1062 const AVOutputFormat ff_segment_muxer
= {
1064 .long_name
= NULL_IF_CONFIG_SMALL("segment"),
1065 .priv_data_size
= sizeof(SegmentContext
),
1066 .flags
= AVFMT_NOFILE
|AVFMT_GLOBALHEADER
,
1068 .write_header
= seg_write_header
,
1069 .write_packet
= seg_write_packet
,
1070 .write_trailer
= seg_write_trailer
,
1072 .check_bitstream
= seg_check_bitstream
,
1073 .priv_class
= &seg_class
,
1077 #if CONFIG_STREAM_SEGMENT_MUXER
1078 const AVOutputFormat ff_stream_segment_muxer
= {
1079 .name
= "stream_segment,ssegment",
1080 .long_name
= NULL_IF_CONFIG_SMALL("streaming segment muxer"),
1081 .priv_data_size
= sizeof(SegmentContext
),
1082 .flags
= AVFMT_NOFILE
,
1084 .write_header
= seg_write_header
,
1085 .write_packet
= seg_write_packet
,
1086 .write_trailer
= seg_write_trailer
,
1088 .check_bitstream
= seg_check_bitstream
,
1089 .priv_class
= &seg_class
,