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}
31 #include "avio_internal.h"
34 #include "libavutil/avassert.h"
35 #include "libavutil/internal.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/timecode.h"
43 #include "libavutil/time_internal.h"
44 #include "libavutil/timestamp.h"
46 typedef struct SegmentListEntry
{
48 double start_time
, end_time
;
52 struct SegmentListEntry
*next
;
53 int64_t last_duration
;
57 LIST_TYPE_UNDEFINED
= -1,
61 LIST_TYPE_EXT
, ///< deprecated
66 #define SEGMENT_LIST_FLAG_CACHE 1
67 #define SEGMENT_LIST_FLAG_LIVE 2
69 typedef struct SegmentContext
{
70 const AVClass
*class; /**< Class for private options. */
71 int segment_idx
; ///< index of the segment file to write, starting from 0
72 int segment_idx_wrap
; ///< number after which the index wraps
73 int segment_idx_wrap_nb
; ///< number of time the index has wraped
74 int segment_count
; ///< number of segment files already written
75 const AVOutputFormat
*oformat
;
77 char *format
; ///< format to use for output segment files
78 AVDictionary
*format_options
;
79 char *list
; ///< filename for the segment list file
80 int list_flags
; ///< flags affecting list generation
81 int list_size
; ///< number of entries for the segment list file
83 int is_nullctx
; ///< whether avf->pb is a nullctx
84 int use_clocktime
; ///< flag to cut segments at regular clock time
85 int64_t clocktime_offset
; //< clock offset for cutting the segments at regular clock time
86 int64_t clocktime_wrap_duration
; //< wrapping duration considered for starting a new segment
87 int64_t last_val
; ///< remember last time for wrap around detection
89 int header_written
; ///< whether we've already called avformat_write_header
91 char *entry_prefix
; ///< prefix to add to list entry filenames
92 int list_type
; ///< set the list type
93 AVIOContext
*list_pb
; ///< list file put-byte context
94 int64_t time
; ///< segment duration
95 int use_strftime
; ///< flag to expand filename with strftime
96 int increment_tc
; ///< flag to increment timecode if found
98 char *times_str
; ///< segment times specification string
99 int64_t *times
; ///< list of segment interval specification
100 int nb_times
; ///< number of elments in the times array
102 char *frames_str
; ///< segment frame numbers specification string
103 int *frames
; ///< list of frame number specification
104 int nb_frames
; ///< number of elments in the frames array
105 int frame_count
; ///< total number of reference frames
106 int segment_frame_count
; ///< number of reference frames in the segment
109 int individual_header_trailer
; /**< Set by a private option. */
110 int write_header_trailer
; /**< Set by a private option. */
111 char *header_filename
; ///< filename to write the output header to
113 int reset_timestamps
; ///< reset timestamps at the beginning of each segment
114 int64_t initial_offset
; ///< initial timestamps offset, expressed in microseconds
115 char *reference_stream_specifier
; ///< reference stream specifier
116 int reference_stream_index
;
117 int break_non_keyframes
;
121 char temp_list_filename
[1024];
123 SegmentListEntry cur_entry
;
124 SegmentListEntry
*segment_list_entries
;
125 SegmentListEntry
*segment_list_entries_end
;
128 static void print_csv_escaped_str(AVIOContext
*ctx
, const char *str
)
130 int needs_quoting
= !!str
[strcspn(str
, "\",\n\r")];
135 for (; *str
; str
++) {
144 static int segment_mux_init(AVFormatContext
*s
)
146 SegmentContext
*seg
= s
->priv_data
;
151 ret
= avformat_alloc_output_context2(&seg
->avf
, seg
->oformat
, NULL
, NULL
);
156 oc
->interrupt_callback
= s
->interrupt_callback
;
157 oc
->max_delay
= s
->max_delay
;
158 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
159 oc
->opaque
= s
->opaque
;
160 oc
->io_close
= s
->io_close
;
161 oc
->io_close2
= s
->io_close2
;
162 oc
->io_open
= s
->io_open
;
163 oc
->flags
= s
->flags
;
165 for (i
= 0; i
< s
->nb_streams
; i
++) {
166 AVStream
*st
, *ist
= s
->streams
[i
];
167 AVCodecParameters
*ipar
= ist
->codecpar
, *opar
;
169 if (!(st
= avformat_new_stream(oc
, NULL
)))
170 return AVERROR(ENOMEM
);
171 ret
= ff_stream_encode_params_copy(st
, ist
);
175 if (!oc
->oformat
->codec_tag
||
176 av_codec_get_id (oc
->oformat
->codec_tag
, ipar
->codec_tag
) == opar
->codec_id
||
177 av_codec_get_tag(oc
->oformat
->codec_tag
, ipar
->codec_id
) <= 0) {
178 opar
->codec_tag
= ipar
->codec_tag
;
187 static int set_segment_filename(AVFormatContext
*s
)
189 SegmentContext
*seg
= s
->priv_data
;
190 AVFormatContext
*oc
= seg
->avf
;
196 if (seg
->segment_idx_wrap
)
197 seg
->segment_idx
%= seg
->segment_idx_wrap
;
198 if (seg
->use_strftime
) {
200 struct tm
*tm
, tmpbuf
;
202 tm
= localtime_r(&now0
, &tmpbuf
);
203 if (!strftime(buf
, sizeof(buf
), s
->url
, tm
)) {
204 av_log(oc
, AV_LOG_ERROR
, "Could not get segment filename with strftime\n");
205 return AVERROR(EINVAL
);
207 } else if (av_get_frame_filename(buf
, sizeof(buf
),
208 s
->url
, seg
->segment_idx
) < 0) {
209 av_log(oc
, AV_LOG_ERROR
, "Invalid segment filename template '%s'\n", s
->url
);
210 return AVERROR(EINVAL
);
212 new_name
= av_strdup(buf
);
214 return AVERROR(ENOMEM
);
215 ff_format_set_url(oc
, new_name
);
217 /* copy modified name in list entry */
218 size
= strlen(av_basename(oc
->url
)) + 1;
219 if (seg
->entry_prefix
)
220 size
+= strlen(seg
->entry_prefix
);
222 if ((ret
= av_reallocp(&seg
->cur_entry
.filename
, size
)) < 0)
224 snprintf(seg
->cur_entry
.filename
, size
, "%s%s",
225 seg
->entry_prefix
? seg
->entry_prefix
: "",
226 av_basename(oc
->url
));
231 static int segment_start(AVFormatContext
*s
, int write_header
)
233 SegmentContext
*seg
= s
->priv_data
;
234 AVFormatContext
*oc
= seg
->avf
;
238 avformat_free_context(oc
);
240 if ((err
= segment_mux_init(s
)) < 0)
246 if ((seg
->segment_idx_wrap
) && (seg
->segment_idx
% seg
->segment_idx_wrap
== 0))
247 seg
->segment_idx_wrap_nb
++;
249 if ((err
= set_segment_filename(s
)) < 0)
252 if ((err
= s
->io_open(s
, &oc
->pb
, oc
->url
, AVIO_FLAG_WRITE
, NULL
)) < 0) {
253 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->url
);
256 if (!seg
->individual_header_trailer
)
257 oc
->pb
->seekable
= 0;
259 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
260 av_opt_set(oc
->priv_data
, "mpegts_flags", "+resend_headers", 0);
263 AVDictionary
*options
= NULL
;
264 av_dict_copy(&options
, seg
->format_options
, 0);
265 av_dict_set(&options
, "fflags", "-autobsf", 0);
266 err
= avformat_write_header(oc
, &options
);
267 av_dict_free(&options
);
272 seg
->segment_frame_count
= 0;
276 static int segment_list_open(AVFormatContext
*s
)
278 SegmentContext
*seg
= s
->priv_data
;
281 snprintf(seg
->temp_list_filename
, sizeof(seg
->temp_list_filename
), seg
->use_rename
? "%s.tmp" : "%s", seg
->list
);
282 ret
= s
->io_open(s
, &seg
->list_pb
, seg
->temp_list_filename
, AVIO_FLAG_WRITE
, NULL
);
284 av_log(s
, AV_LOG_ERROR
, "Failed to open segment list '%s'\n", seg
->list
);
288 if (seg
->list_type
== LIST_TYPE_M3U8
&& seg
->segment_list_entries
) {
289 SegmentListEntry
*entry
;
290 double max_duration
= 0;
292 avio_printf(seg
->list_pb
, "#EXTM3U\n");
293 avio_printf(seg
->list_pb
, "#EXT-X-VERSION:3\n");
294 avio_printf(seg
->list_pb
, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg
->segment_list_entries
->index
);
295 avio_printf(seg
->list_pb
, "#EXT-X-ALLOW-CACHE:%s\n",
296 seg
->list_flags
& SEGMENT_LIST_FLAG_CACHE
? "YES" : "NO");
298 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%d\n",
299 seg
->segment_list_entries
->index
);
301 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
302 max_duration
= FFMAX(max_duration
, entry
->end_time
- entry
->start_time
);
303 avio_printf(seg
->list_pb
, "#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration
));
304 } else if (seg
->list_type
== LIST_TYPE_FFCONCAT
) {
305 avio_printf(seg
->list_pb
, "ffconcat version 1.0\n");
311 static void segment_list_print_entry(AVIOContext
*list_ioctx
,
313 const SegmentListEntry
*list_entry
,
318 avio_printf(list_ioctx
, "%s\n", list_entry
->filename
);
322 print_csv_escaped_str(list_ioctx
, list_entry
->filename
);
323 avio_printf(list_ioctx
, ",%f,%f\n", list_entry
->start_time
, list_entry
->end_time
);
326 avio_printf(list_ioctx
, "#EXTINF:%f,\n%s\n",
327 list_entry
->end_time
- list_entry
->start_time
, list_entry
->filename
);
329 case LIST_TYPE_FFCONCAT
:
332 if (av_escape(&buf
, list_entry
->filename
, NULL
, AV_ESCAPE_MODE_AUTO
, AV_ESCAPE_FLAG_WHITESPACE
) < 0) {
333 av_log(log_ctx
, AV_LOG_WARNING
,
334 "Error writing list entry '%s' in list file\n", list_entry
->filename
);
337 avio_printf(list_ioctx
, "file %s\n", buf
);
342 av_assert0(!"Invalid list type");
346 static int segment_end(AVFormatContext
*s
, int write_trailer
, int is_last
)
348 SegmentContext
*seg
= s
->priv_data
;
349 AVFormatContext
*oc
= seg
->avf
;
353 AVDictionaryEntry
*tcr
;
354 char buf
[AV_TIMECODE_STR_SIZE
];
359 return AVERROR(EINVAL
);
361 av_write_frame(oc
, NULL
); /* Flush any buffered data (fragmented mp4) */
363 ret
= av_write_trailer(oc
);
366 av_log(s
, AV_LOG_ERROR
, "Failure occurred when ending segment '%s'\n",
370 if (seg
->list_size
|| seg
->list_type
== LIST_TYPE_M3U8
) {
371 SegmentListEntry
*entry
= av_mallocz(sizeof(*entry
));
373 ret
= AVERROR(ENOMEM
);
377 /* append new element */
378 memcpy(entry
, &seg
->cur_entry
, sizeof(*entry
));
379 entry
->filename
= av_strdup(entry
->filename
);
380 if (!seg
->segment_list_entries
)
381 seg
->segment_list_entries
= seg
->segment_list_entries_end
= entry
;
383 seg
->segment_list_entries_end
->next
= entry
;
384 seg
->segment_list_entries_end
= entry
;
386 /* drop first item */
387 if (seg
->list_size
&& seg
->segment_count
>= seg
->list_size
) {
388 entry
= seg
->segment_list_entries
;
389 seg
->segment_list_entries
= seg
->segment_list_entries
->next
;
390 av_freep(&entry
->filename
);
394 if ((ret
= segment_list_open(s
)) < 0)
396 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
397 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, entry
, s
);
398 if (seg
->list_type
== LIST_TYPE_M3U8
&& is_last
)
399 avio_printf(seg
->list_pb
, "#EXT-X-ENDLIST\n");
400 ff_format_io_close(s
, &seg
->list_pb
);
402 ff_rename(seg
->temp_list_filename
, seg
->list
, s
);
404 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, &seg
->cur_entry
, s
);
405 avio_flush(seg
->list_pb
);
409 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' count:%d ended\n",
410 seg
->avf
->url
, seg
->segment_count
);
411 seg
->segment_count
++;
413 if (seg
->increment_tc
) {
414 tcr
= av_dict_get(s
->metadata
, "timecode", NULL
, 0);
416 /* search the first video stream */
417 for (i
= 0; i
< s
->nb_streams
; i
++) {
418 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
419 rate
= s
->streams
[i
]->avg_frame_rate
;/* Get fps from the video stream */
420 err
= av_timecode_init_from_string(&tc
, rate
, tcr
->value
, s
);
422 av_log(s
, AV_LOG_WARNING
, "Could not increment global timecode, error occurred during timecode creation.\n");
425 tc
.start
+= (int)((seg
->cur_entry
.end_time
- seg
->cur_entry
.start_time
) * av_q2d(rate
));/* increment timecode */
426 av_dict_set(&s
->metadata
, "timecode",
427 av_timecode_make_string(&tc
, buf
, 0), 0);
432 av_log(s
, AV_LOG_WARNING
, "Could not increment global timecode, no global timecode metadata found.\n");
434 for (i
= 0; i
< s
->nb_streams
; i
++) {
435 if (s
->streams
[i
]->codecpar
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
436 char st_buf
[AV_TIMECODE_STR_SIZE
];
438 AVRational st_rate
= s
->streams
[i
]->avg_frame_rate
;
439 AVDictionaryEntry
*st_tcr
= av_dict_get(s
->streams
[i
]->metadata
, "timecode", NULL
, 0);
441 if ((av_timecode_init_from_string(&st_tc
, st_rate
, st_tcr
->value
, s
) < 0)) {
442 av_log(s
, AV_LOG_WARNING
, "Could not increment stream %d timecode, error occurred during timecode creation.\n", i
);
445 st_tc
.start
+= (int)((seg
->cur_entry
.end_time
- seg
->cur_entry
.start_time
) * av_q2d(st_rate
)); // increment timecode
446 av_dict_set(&s
->streams
[i
]->metadata
, "timecode", av_timecode_make_string(&st_tc
, st_buf
, 0), 0);
453 ff_format_io_close(oc
, &oc
->pb
);
458 static int parse_times(void *log_ctx
, int64_t **times
, int *nb_times
,
459 const char *times_str
)
463 char *times_str1
= av_strdup(times_str
);
464 char *saveptr
= NULL
;
467 return AVERROR(ENOMEM
);
469 #define FAIL(err) ret = err; goto end
472 for (p
= times_str1
; *p
; p
++)
476 *times
= av_malloc_array(*nb_times
, sizeof(**times
));
478 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced times array\n");
479 FAIL(AVERROR(ENOMEM
));
483 for (i
= 0; i
< *nb_times
; i
++) {
485 char *tstr
= av_strtok(p
, ",", &saveptr
);
488 if (!tstr
|| !tstr
[0]) {
489 av_log(log_ctx
, AV_LOG_ERROR
, "Empty time specification in times list %s\n",
491 FAIL(AVERROR(EINVAL
));
494 ret
= av_parse_time(&t
, tstr
, 1);
496 av_log(log_ctx
, AV_LOG_ERROR
,
497 "Invalid time duration specification '%s' in times list %s\n", tstr
, times_str
);
498 FAIL(AVERROR(EINVAL
));
502 /* check on monotonicity */
503 if (i
&& (*times
)[i
-1] > (*times
)[i
]) {
504 av_log(log_ctx
, AV_LOG_ERROR
,
505 "Specified time %f is smaller than the last time %f\n",
506 (float)((*times
)[i
])/1000000, (float)((*times
)[i
-1])/1000000);
507 FAIL(AVERROR(EINVAL
));
516 static int parse_frames(void *log_ctx
, int **frames
, int *nb_frames
,
517 const char *frames_str
)
523 for (p
= frames_str
; *p
; p
++)
527 *frames
= av_malloc_array(*nb_frames
, sizeof(**frames
));
529 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced frames array\n");
530 return AVERROR(ENOMEM
);
534 for (i
= 0; i
< *nb_frames
; i
++) {
538 if (*p
== '\0' || *p
== ',') {
539 av_log(log_ctx
, AV_LOG_ERROR
, "Empty frame specification in frame list %s\n",
541 return AVERROR(EINVAL
);
543 f
= strtol(p
, &tailptr
, 10);
544 if (*tailptr
!= '\0' && *tailptr
!= ',' || f
<= 0 || f
>= INT_MAX
) {
545 av_log(log_ctx
, AV_LOG_ERROR
,
546 "Invalid argument '%s', must be a positive integer < INT_MAX\n",
548 return AVERROR(EINVAL
);
555 /* check on monotonicity */
556 if (i
&& (*frames
)[i
-1] > (*frames
)[i
]) {
557 av_log(log_ctx
, AV_LOG_ERROR
,
558 "Specified frame %d is smaller than the last frame %d\n",
559 (*frames
)[i
], (*frames
)[i
-1]);
560 return AVERROR(EINVAL
);
567 static int open_null_ctx(AVIOContext
**ctx
)
569 int buf_size
= 32768;
570 uint8_t *buf
= av_malloc(buf_size
);
572 return AVERROR(ENOMEM
);
573 *ctx
= avio_alloc_context(buf
, buf_size
, 1, NULL
, NULL
, NULL
, NULL
);
576 return AVERROR(ENOMEM
);
581 static void close_null_ctxp(AVIOContext
**pb
)
583 av_freep(&(*pb
)->buffer
);
584 avio_context_free(pb
);
587 static int select_reference_stream(AVFormatContext
*s
)
589 SegmentContext
*seg
= s
->priv_data
;
592 seg
->reference_stream_index
= -1;
593 if (!strcmp(seg
->reference_stream_specifier
, "auto")) {
594 /* select first index of type with highest priority */
595 int type_index_map
[AVMEDIA_TYPE_NB
];
596 static const enum AVMediaType type_priority_list
[] = {
599 AVMEDIA_TYPE_SUBTITLE
,
601 AVMEDIA_TYPE_ATTACHMENT
603 enum AVMediaType type
;
605 for (i
= 0; i
< AVMEDIA_TYPE_NB
; i
++)
606 type_index_map
[i
] = -1;
608 /* select first index for each type */
609 for (i
= 0; i
< s
->nb_streams
; i
++) {
610 type
= s
->streams
[i
]->codecpar
->codec_type
;
611 if ((unsigned)type
< AVMEDIA_TYPE_NB
&& type_index_map
[type
] == -1
612 /* ignore attached pictures/cover art streams */
613 && !(s
->streams
[i
]->disposition
& AV_DISPOSITION_ATTACHED_PIC
))
614 type_index_map
[type
] = i
;
617 for (i
= 0; i
< FF_ARRAY_ELEMS(type_priority_list
); i
++) {
618 type
= type_priority_list
[i
];
619 if ((seg
->reference_stream_index
= type_index_map
[type
]) >= 0)
623 for (i
= 0; i
< s
->nb_streams
; i
++) {
624 ret
= avformat_match_stream_specifier(s
, s
->streams
[i
],
625 seg
->reference_stream_specifier
);
629 seg
->reference_stream_index
= i
;
635 if (seg
->reference_stream_index
< 0) {
636 av_log(s
, AV_LOG_ERROR
, "Could not select stream matching identifier '%s'\n",
637 seg
->reference_stream_specifier
);
638 return AVERROR(EINVAL
);
644 static void seg_free(AVFormatContext
*s
)
646 SegmentContext
*seg
= s
->priv_data
;
647 SegmentListEntry
*cur
;
649 ff_format_io_close(s
, &seg
->list_pb
);
652 close_null_ctxp(&seg
->avf
->pb
);
654 ff_format_io_close(s
, &seg
->avf
->pb
);
655 avformat_free_context(seg
->avf
);
658 av_freep(&seg
->times
);
659 av_freep(&seg
->frames
);
660 av_freep(&seg
->cur_entry
.filename
);
662 cur
= seg
->segment_list_entries
;
664 SegmentListEntry
*next
= cur
->next
;
665 av_freep(&cur
->filename
);
671 static int seg_init(AVFormatContext
*s
)
673 SegmentContext
*seg
= s
->priv_data
;
674 AVFormatContext
*oc
= seg
->avf
;
675 AVDictionary
*options
= NULL
;
679 seg
->segment_count
= 0;
680 if (!seg
->write_header_trailer
)
681 seg
->individual_header_trailer
= 0;
683 if (seg
->header_filename
) {
684 seg
->write_header_trailer
= 1;
685 seg
->individual_header_trailer
= 0;
688 if (seg
->initial_offset
> 0) {
689 av_log(s
, AV_LOG_WARNING
, "NOTE: the option initial_offset is deprecated,"
690 "you can use output_ts_offset instead of it\n");
693 if ((seg
->time
!= 2000000) + !!seg
->times_str
+ !!seg
->frames_str
> 1) {
694 av_log(s
, AV_LOG_ERROR
,
695 "segment_time, segment_times, and segment_frames options "
696 "are mutually exclusive, select just one of them\n");
697 return AVERROR(EINVAL
);
700 if (seg
->times_str
) {
701 if ((ret
= parse_times(s
, &seg
->times
, &seg
->nb_times
, seg
->times_str
)) < 0)
703 } else if (seg
->frames_str
) {
704 if ((ret
= parse_frames(s
, &seg
->frames
, &seg
->nb_frames
, seg
->frames_str
)) < 0)
707 if (seg
->use_clocktime
) {
708 if (seg
->time
<= 0) {
709 av_log(s
, AV_LOG_ERROR
, "Invalid negative segment_time with segment_atclocktime option set\n");
710 return AVERROR(EINVAL
);
712 seg
->clocktime_offset
= seg
->time
- (seg
->clocktime_offset
% seg
->time
);
717 if (seg
->list_type
== LIST_TYPE_UNDEFINED
) {
718 if (av_match_ext(seg
->list
, "csv" )) seg
->list_type
= LIST_TYPE_CSV
;
719 else if (av_match_ext(seg
->list
, "ext" )) seg
->list_type
= LIST_TYPE_EXT
;
720 else if (av_match_ext(seg
->list
, "m3u8")) seg
->list_type
= LIST_TYPE_M3U8
;
721 else if (av_match_ext(seg
->list
, "ffcat,ffconcat")) seg
->list_type
= LIST_TYPE_FFCONCAT
;
722 else seg
->list_type
= LIST_TYPE_FLAT
;
724 if (!seg
->list_size
&& seg
->list_type
!= LIST_TYPE_M3U8
) {
725 if ((ret
= segment_list_open(s
)) < 0)
728 const char *proto
= avio_find_protocol_name(seg
->list
);
729 seg
->use_rename
= proto
&& !strcmp(proto
, "file");
733 if (seg
->list_type
== LIST_TYPE_EXT
)
734 av_log(s
, AV_LOG_WARNING
, "'ext' list type option is deprecated in favor of 'csv'\n");
736 if ((ret
= select_reference_stream(s
)) < 0)
738 av_log(s
, AV_LOG_VERBOSE
, "Selected stream id:%d type:%s\n",
739 seg
->reference_stream_index
,
740 av_get_media_type_string(s
->streams
[seg
->reference_stream_index
]->codecpar
->codec_type
));
742 seg
->oformat
= av_guess_format(seg
->format
, s
->url
, NULL
);
745 return AVERROR_MUXER_NOT_FOUND
;
746 if (seg
->oformat
->flags
& AVFMT_NOFILE
) {
747 av_log(s
, AV_LOG_ERROR
, "format %s not supported.\n",
749 return AVERROR(EINVAL
);
752 if ((ret
= segment_mux_init(s
)) < 0)
755 if ((ret
= set_segment_filename(s
)) < 0)
759 if (seg
->write_header_trailer
) {
760 if ((ret
= s
->io_open(s
, &oc
->pb
,
761 seg
->header_filename
? seg
->header_filename
: oc
->url
,
762 AVIO_FLAG_WRITE
, NULL
)) < 0) {
763 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->url
);
766 if (!seg
->individual_header_trailer
)
767 oc
->pb
->seekable
= 0;
769 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
774 av_dict_copy(&options
, seg
->format_options
, 0);
775 av_dict_set(&options
, "fflags", "-autobsf", 0);
776 ret
= avformat_init_output(oc
, &options
);
777 if (av_dict_count(options
)) {
778 av_log(s
, AV_LOG_ERROR
,
779 "Some of the provided format options are not recognized\n");
780 av_dict_free(&options
);
781 return AVERROR(EINVAL
);
783 av_dict_free(&options
);
788 seg
->segment_frame_count
= 0;
790 av_assert0(s
->nb_streams
== oc
->nb_streams
);
791 if (ret
== AVSTREAM_INIT_IN_WRITE_HEADER
) {
792 ret
= avformat_write_header(oc
, NULL
);
795 seg
->header_written
= 1;
798 for (i
= 0; i
< s
->nb_streams
; i
++) {
799 AVStream
*inner_st
= oc
->streams
[i
];
800 AVStream
*outer_st
= s
->streams
[i
];
801 avpriv_set_pts_info(outer_st
, inner_st
->pts_wrap_bits
, inner_st
->time_base
.num
, inner_st
->time_base
.den
);
804 if (oc
->avoid_negative_ts
> 0 && s
->avoid_negative_ts
< 0)
805 s
->avoid_negative_ts
= 1;
810 static int seg_write_header(AVFormatContext
*s
)
812 SegmentContext
*seg
= s
->priv_data
;
813 AVFormatContext
*oc
= seg
->avf
;
816 if (!seg
->header_written
) {
817 ret
= avformat_write_header(oc
, NULL
);
822 if (!seg
->write_header_trailer
|| seg
->header_filename
) {
823 if (seg
->header_filename
) {
824 av_write_frame(oc
, NULL
);
825 ff_format_io_close(oc
, &oc
->pb
);
827 close_null_ctxp(&oc
->pb
);
830 if ((ret
= oc
->io_open(oc
, &oc
->pb
, oc
->url
, AVIO_FLAG_WRITE
, NULL
)) < 0)
832 if (!seg
->individual_header_trailer
)
833 oc
->pb
->seekable
= 0;
839 static int seg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
841 SegmentContext
*seg
= s
->priv_data
;
842 AVStream
*st
= s
->streams
[pkt
->stream_index
];
843 int64_t end_pts
= INT64_MAX
, offset
;
844 int start_frame
= INT_MAX
;
850 if (!seg
->avf
|| !seg
->avf
->pb
)
851 return AVERROR(EINVAL
);
853 if (!st
->codecpar
->extradata_size
) {
854 size_t pkt_extradata_size
;
855 uint8_t *pkt_extradata
= av_packet_get_side_data(pkt
, AV_PKT_DATA_NEW_EXTRADATA
, &pkt_extradata_size
);
856 if (pkt_extradata
&& pkt_extradata_size
> 0) {
857 ret
= ff_alloc_extradata(st
->codecpar
, pkt_extradata_size
);
859 av_log(s
, AV_LOG_WARNING
, "Unable to add extradata to stream. Output segments may be invalid.\n");
862 memcpy(st
->codecpar
->extradata
, pkt_extradata
, pkt_extradata_size
);
868 end_pts
= seg
->segment_count
< seg
->nb_times
?
869 seg
->times
[seg
->segment_count
] : INT64_MAX
;
870 } else if (seg
->frames
) {
871 start_frame
= seg
->segment_count
< seg
->nb_frames
?
872 seg
->frames
[seg
->segment_count
] : INT_MAX
;
874 if (seg
->use_clocktime
) {
875 int64_t avgt
= av_gettime();
876 time_t sec
= avgt
/ 1000000;
877 localtime_r(&sec
, &ti
);
878 usecs
= (int64_t)(ti
.tm_hour
* 3600 + ti
.tm_min
* 60 + ti
.tm_sec
) * 1000000 + (avgt
% 1000000);
879 wrapped_val
= (usecs
+ seg
->clocktime_offset
) % seg
->time
;
880 if (wrapped_val
< seg
->last_val
&& wrapped_val
< seg
->clocktime_wrap_duration
)
881 seg
->cut_pending
= 1;
882 seg
->last_val
= wrapped_val
;
884 end_pts
= seg
->time
* (seg
->segment_count
+ 1);
888 ff_dlog(s
, "packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
889 pkt
->stream_index
, av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
890 av_ts2timestr(pkt
->duration
, &st
->time_base
),
891 pkt
->flags
& AV_PKT_FLAG_KEY
,
892 pkt
->stream_index
== seg
->reference_stream_index
? seg
->frame_count
: -1);
894 if (pkt
->stream_index
== seg
->reference_stream_index
&&
895 (pkt
->flags
& AV_PKT_FLAG_KEY
|| seg
->break_non_keyframes
) &&
896 (seg
->segment_frame_count
> 0 || seg
->write_empty
) &&
897 (seg
->cut_pending
|| seg
->frame_count
>= start_frame
||
898 (pkt
->pts
!= AV_NOPTS_VALUE
&&
899 av_compare_ts(pkt
->pts
, st
->time_base
,
900 end_pts
- seg
->time_delta
, AV_TIME_BASE_Q
) >= 0))) {
901 /* sanitize end time in case last packet didn't have a defined duration */
902 if (seg
->cur_entry
.last_duration
== 0)
903 seg
->cur_entry
.end_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
905 if ((ret
= segment_end(s
, seg
->individual_header_trailer
, 0)) < 0)
908 if ((ret
= segment_start(s
, seg
->individual_header_trailer
)) < 0)
911 seg
->cut_pending
= 0;
912 seg
->cur_entry
.index
= seg
->segment_idx
+ seg
->segment_idx_wrap
* seg
->segment_idx_wrap_nb
;
913 seg
->cur_entry
.start_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
914 seg
->cur_entry
.start_pts
= av_rescale_q(pkt
->pts
, st
->time_base
, AV_TIME_BASE_Q
);
915 seg
->cur_entry
.end_time
= seg
->cur_entry
.start_time
;
917 if (seg
->times
|| (!seg
->frames
&& !seg
->use_clocktime
) && seg
->write_empty
)
921 if (pkt
->stream_index
== seg
->reference_stream_index
) {
922 if (pkt
->pts
!= AV_NOPTS_VALUE
)
923 seg
->cur_entry
.end_time
=
924 FFMAX(seg
->cur_entry
.end_time
, (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
));
925 seg
->cur_entry
.last_duration
= pkt
->duration
;
928 if (seg
->segment_frame_count
== 0) {
929 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
930 seg
->avf
->url
, pkt
->stream_index
,
931 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
), seg
->frame_count
);
934 av_log(s
, AV_LOG_DEBUG
, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
936 av_ts2timestr(seg
->cur_entry
.start_pts
, &AV_TIME_BASE_Q
),
937 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
938 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
940 /* compute new timestamps */
941 offset
= av_rescale_q(seg
->initial_offset
- (seg
->reset_timestamps
? seg
->cur_entry
.start_pts
: 0),
942 AV_TIME_BASE_Q
, st
->time_base
);
943 if (pkt
->pts
!= AV_NOPTS_VALUE
)
945 if (pkt
->dts
!= AV_NOPTS_VALUE
)
948 av_log(s
, AV_LOG_DEBUG
, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
949 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
950 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
952 ret
= ff_write_chained(seg
->avf
, pkt
->stream_index
, pkt
, s
,
953 seg
->initial_offset
|| seg
->reset_timestamps
|| seg
->avf
->oformat
->interleave_packet
);
956 /* Use st->index here as the packet returned from ff_write_chained()
957 * is blank if interleaving has been used. */
958 if (st
->index
== seg
->reference_stream_index
) {
960 seg
->segment_frame_count
++;
966 static int seg_write_trailer(struct AVFormatContext
*s
)
968 SegmentContext
*seg
= s
->priv_data
;
969 AVFormatContext
*oc
= seg
->avf
;
975 if (!seg
->write_header_trailer
) {
976 if ((ret
= segment_end(s
, 0, 1)) < 0)
978 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
981 ret
= av_write_trailer(oc
);
983 ret
= segment_end(s
, 1, 1);
988 static int seg_check_bitstream(AVFormatContext
*s
, AVStream
*st
,
991 SegmentContext
*seg
= s
->priv_data
;
992 AVFormatContext
*oc
= seg
->avf
;
993 if (oc
->oformat
->check_bitstream
) {
994 AVStream
*const ost
= oc
->streams
[st
->index
];
995 int ret
= oc
->oformat
->check_bitstream(oc
, ost
, pkt
);
997 FFStream
*const sti
= ffstream(st
);
998 FFStream
*const osti
= ffstream(ost
);
999 sti
->bsfc
= osti
->bsfc
;
1007 #define OFFSET(x) offsetof(SegmentContext, x)
1008 #define E AV_OPT_FLAG_ENCODING_PARAM
1009 static const AVOption options
[] = {
1010 { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier
), AV_OPT_TYPE_STRING
, {.str
= "auto"}, 0, 0, E
},
1011 { "segment_format", "set container format used for the segments", OFFSET(format
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1012 { "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
},
1013 { "segment_list", "set the segment list filename", OFFSET(list
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1014 { "segment_header_filename", "write a single file containing the header", OFFSET(header_filename
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1016 { "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"},
1017 { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_LIST_FLAG_CACHE
}, INT_MIN
, INT_MAX
, E
, "list_flags"},
1018 { "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"},
1020 { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1022 { "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" },
1023 { "flat", "flat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FLAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1024 { "csv", "csv format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_CSV
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1025 { "ext", "extended format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_EXT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1026 { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FFCONCAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1027 { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1028 { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
1030 { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1031 { "segment_clocktime_offset", "set segment clocktime offset", OFFSET(clocktime_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, 86400000000LL, E
},
1032 { "segment_clocktime_wrap_duration", "set segment clocktime wrapping duration", OFFSET(clocktime_wrap_duration
), AV_OPT_TYPE_DURATION
, {.i64
= INT64_MAX
}, 0, INT64_MAX
, E
},
1033 { "segment_time", "set segment duration", OFFSET(time
),AV_OPT_TYPE_DURATION
, {.i64
= 2000000}, INT64_MIN
, INT64_MAX
, E
},
1034 { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, INT64_MAX
, E
},
1035 { "segment_times", "set segment split time points", OFFSET(times_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
1036 { "segment_frames", "set segment split frame numbers", OFFSET(frames_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
1037 { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1038 { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
1039 { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
1040 { "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
},
1041 { "strftime", "set filename expansion with strftime at segment creation", OFFSET(use_strftime
), AV_OPT_TYPE_BOOL
, {.i64
= 0 }, 0, 1, E
},
1042 { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc
), AV_OPT_TYPE_BOOL
, {.i64
= 0 }, 0, 1, E
},
1043 { "break_non_keyframes", "allow breaking segments on non-keyframes", OFFSET(break_non_keyframes
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1045 { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, E
},
1046 { "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
},
1047 { "reset_timestamps", "reset timestamps at the beginning of each segment", OFFSET(reset_timestamps
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1048 { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, -INT64_MAX
, INT64_MAX
, E
},
1049 { "write_empty_segments", "allow writing empty 'filler' segments", OFFSET(write_empty
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, E
},
1053 static const AVClass seg_class
= {
1054 .class_name
= "(stream) segment muxer",
1055 .item_name
= av_default_item_name
,
1057 .version
= LIBAVUTIL_VERSION_INT
,
1060 #if CONFIG_SEGMENT_MUXER
1061 const AVOutputFormat ff_segment_muxer
= {
1063 .long_name
= NULL_IF_CONFIG_SMALL("segment"),
1064 .priv_data_size
= sizeof(SegmentContext
),
1065 .flags
= AVFMT_NOFILE
|AVFMT_GLOBALHEADER
,
1067 .write_header
= seg_write_header
,
1068 .write_packet
= seg_write_packet
,
1069 .write_trailer
= seg_write_trailer
,
1071 .check_bitstream
= seg_check_bitstream
,
1072 .priv_class
= &seg_class
,
1076 #if CONFIG_STREAM_SEGMENT_MUXER
1077 const AVOutputFormat ff_stream_segment_muxer
= {
1078 .name
= "stream_segment,ssegment",
1079 .long_name
= NULL_IF_CONFIG_SMALL("streaming segment muxer"),
1080 .priv_data_size
= sizeof(SegmentContext
),
1081 .flags
= AVFMT_NOFILE
,
1083 .write_header
= seg_write_header
,
1084 .write_packet
= seg_write_packet
,
1085 .write_trailer
= seg_write_trailer
,
1087 .check_bitstream
= seg_check_bitstream
,
1088 .priv_class
= &seg_class
,