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/time_internal.h"
43 #include "libavutil/timestamp.h"
45 typedef struct SegmentListEntry
{
47 double start_time
, end_time
;
51 struct SegmentListEntry
*next
;
52 int64_t last_duration
;
56 LIST_TYPE_UNDEFINED
= -1,
60 LIST_TYPE_EXT
, ///< deprecated
65 #define SEGMENT_LIST_FLAG_CACHE 1
66 #define SEGMENT_LIST_FLAG_LIVE 2
69 const AVClass
*class; /**< Class for private options. */
70 int segment_idx
; ///< index of the segment file to write, starting from 0
71 int segment_idx_wrap
; ///< number after which the index wraps
72 int segment_idx_wrap_nb
; ///< number of time the index has wraped
73 int segment_count
; ///< number of segment files already written
74 AVOutputFormat
*oformat
;
76 char *format
; ///< format to use for output segment files
77 char *format_options_str
; ///< format options 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 use_clocktime
; ///< flag to cut segments at regular clock time
84 int64_t last_val
; ///< remember last time for wrap around detection
85 int64_t last_cut
; ///< remember last cut
88 char *entry_prefix
; ///< prefix to add to list entry filenames
89 ListType list_type
; ///< set the list type
90 AVIOContext
*list_pb
; ///< list file put-byte context
91 char *time_str
; ///< segment duration specification string
92 int64_t time
; ///< segment duration
94 char *times_str
; ///< segment times specification string
95 int64_t *times
; ///< list of segment interval specification
96 int nb_times
; ///< number of elments in the times array
98 char *frames_str
; ///< segment frame numbers specification string
99 int *frames
; ///< list of frame number specification
100 int nb_frames
; ///< number of elments in the frames array
101 int frame_count
; ///< total number of reference frames
102 int segment_frame_count
; ///< number of reference frames in the segment
105 int individual_header_trailer
; /**< Set by a private option. */
106 int write_header_trailer
; /**< Set by a private option. */
108 int reset_timestamps
; ///< reset timestamps at the begin of each segment
109 int64_t initial_offset
; ///< initial timestamps offset, expressed in microseconds
110 char *reference_stream_specifier
; ///< reference stream specifier
111 int reference_stream_index
;
113 SegmentListEntry cur_entry
;
114 SegmentListEntry
*segment_list_entries
;
115 SegmentListEntry
*segment_list_entries_end
;
118 static void print_csv_escaped_str(AVIOContext
*ctx
, const char *str
)
120 int needs_quoting
= !!str
[strcspn(str
, "\",\n\r")];
125 for (; *str
; str
++) {
134 static int segment_mux_init(AVFormatContext
*s
)
136 SegmentContext
*seg
= s
->priv_data
;
141 ret
= avformat_alloc_output_context2(&seg
->avf
, seg
->oformat
, NULL
, NULL
);
146 oc
->interrupt_callback
= s
->interrupt_callback
;
147 oc
->max_delay
= s
->max_delay
;
148 av_dict_copy(&oc
->metadata
, s
->metadata
, 0);
150 for (i
= 0; i
< s
->nb_streams
; i
++) {
152 AVCodecContext
*icodec
, *ocodec
;
154 if (!(st
= avformat_new_stream(oc
, NULL
)))
155 return AVERROR(ENOMEM
);
156 icodec
= s
->streams
[i
]->codec
;
158 avcodec_copy_context(ocodec
, icodec
);
159 if (!oc
->oformat
->codec_tag
||
160 av_codec_get_id (oc
->oformat
->codec_tag
, icodec
->codec_tag
) == ocodec
->codec_id
||
161 av_codec_get_tag(oc
->oformat
->codec_tag
, icodec
->codec_id
) <= 0) {
162 ocodec
->codec_tag
= icodec
->codec_tag
;
164 ocodec
->codec_tag
= 0;
166 st
->sample_aspect_ratio
= s
->streams
[i
]->sample_aspect_ratio
;
167 st
->time_base
= s
->streams
[i
]->time_base
;
168 av_dict_copy(&st
->metadata
, s
->streams
[i
]->metadata
, 0);
174 static int set_segment_filename(AVFormatContext
*s
)
176 SegmentContext
*seg
= s
->priv_data
;
177 AVFormatContext
*oc
= seg
->avf
;
180 if (seg
->segment_idx_wrap
)
181 seg
->segment_idx
%= seg
->segment_idx_wrap
;
182 if (av_get_frame_filename(oc
->filename
, sizeof(oc
->filename
),
183 s
->filename
, seg
->segment_idx
) < 0) {
184 av_log(oc
, AV_LOG_ERROR
, "Invalid segment filename template '%s'\n", s
->filename
);
185 return AVERROR(EINVAL
);
188 /* copy modified name in list entry */
189 size
= strlen(av_basename(oc
->filename
)) + 1;
190 if (seg
->entry_prefix
)
191 size
+= strlen(seg
->entry_prefix
);
193 seg
->cur_entry
.filename
= av_mallocz(size
);
194 if (!seg
->cur_entry
.filename
)
195 return AVERROR(ENOMEM
);
196 snprintf(seg
->cur_entry
.filename
, size
, "%s%s",
197 seg
->entry_prefix
? seg
->entry_prefix
: "",
198 av_basename(oc
->filename
));
203 static int segment_start(AVFormatContext
*s
, int write_header
)
205 SegmentContext
*seg
= s
->priv_data
;
206 AVFormatContext
*oc
= seg
->avf
;
210 avformat_free_context(oc
);
212 if ((err
= segment_mux_init(s
)) < 0)
218 if ((seg
->segment_idx_wrap
) && (seg
->segment_idx
%seg
->segment_idx_wrap
== 0))
219 seg
->segment_idx_wrap_nb
++;
221 if ((err
= set_segment_filename(s
)) < 0)
224 if ((err
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
225 &s
->interrupt_callback
, NULL
)) < 0) {
226 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->filename
);
230 if (oc
->oformat
->priv_class
&& oc
->priv_data
)
231 av_opt_set(oc
->priv_data
, "mpegts_flags", "+resend_headers", 0);
234 if ((err
= avformat_write_header(oc
, NULL
)) < 0)
238 seg
->segment_frame_count
= 0;
242 static int segment_list_open(AVFormatContext
*s
)
244 SegmentContext
*seg
= s
->priv_data
;
247 ret
= avio_open2(&seg
->list_pb
, seg
->list
, AVIO_FLAG_WRITE
,
248 &s
->interrupt_callback
, NULL
);
250 av_log(s
, AV_LOG_ERROR
, "Failed to open segment list '%s'\n", seg
->list
);
254 if (seg
->list_type
== LIST_TYPE_M3U8
&& seg
->segment_list_entries
) {
255 SegmentListEntry
*entry
;
256 double max_duration
= 0;
258 avio_printf(seg
->list_pb
, "#EXTM3U\n");
259 avio_printf(seg
->list_pb
, "#EXT-X-VERSION:3\n");
260 avio_printf(seg
->list_pb
, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg
->segment_list_entries
->index
);
261 avio_printf(seg
->list_pb
, "#EXT-X-ALLOW-CACHE:%s\n",
262 seg
->list_flags
& SEGMENT_LIST_FLAG_CACHE
? "YES" : "NO");
264 av_log(s
, AV_LOG_VERBOSE
, "EXT-X-MEDIA-SEQUENCE:%d\n",
265 seg
->segment_list_entries
->index
);
267 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
268 max_duration
= FFMAX(max_duration
, entry
->end_time
- entry
->start_time
);
269 avio_printf(seg
->list_pb
, "#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration
));
270 } else if (seg
->list_type
== LIST_TYPE_FFCONCAT
) {
271 avio_printf(seg
->list_pb
, "ffconcat version 1.0\n");
277 static void segment_list_print_entry(AVIOContext
*list_ioctx
,
279 const SegmentListEntry
*list_entry
,
284 avio_printf(list_ioctx
, "%s\n", list_entry
->filename
);
288 print_csv_escaped_str(list_ioctx
, list_entry
->filename
);
289 avio_printf(list_ioctx
, ",%f,%f\n", list_entry
->start_time
, list_entry
->end_time
);
292 avio_printf(list_ioctx
, "#EXTINF:%f,\n%s\n",
293 list_entry
->end_time
- list_entry
->start_time
, list_entry
->filename
);
295 case LIST_TYPE_FFCONCAT
:
298 if (av_escape(&buf
, list_entry
->filename
, NULL
, AV_ESCAPE_MODE_AUTO
, AV_ESCAPE_FLAG_WHITESPACE
) < 0) {
299 av_log(log_ctx
, AV_LOG_WARNING
,
300 "Error writing list entry '%s' in list file\n", list_entry
->filename
);
303 avio_printf(list_ioctx
, "file %s\n", buf
);
308 av_assert0(!"Invalid list type");
312 static int segment_end(AVFormatContext
*s
, int write_trailer
, int is_last
)
314 SegmentContext
*seg
= s
->priv_data
;
315 AVFormatContext
*oc
= seg
->avf
;
318 av_write_frame(oc
, NULL
); /* Flush any buffered data (fragmented mp4) */
320 ret
= av_write_trailer(oc
);
323 av_log(s
, AV_LOG_ERROR
, "Failure occurred when ending segment '%s'\n",
327 if (seg
->list_size
|| seg
->list_type
== LIST_TYPE_M3U8
) {
328 SegmentListEntry
*entry
= av_mallocz(sizeof(*entry
));
330 ret
= AVERROR(ENOMEM
);
334 /* append new element */
335 memcpy(entry
, &seg
->cur_entry
, sizeof(*entry
));
336 if (!seg
->segment_list_entries
)
337 seg
->segment_list_entries
= seg
->segment_list_entries_end
= entry
;
339 seg
->segment_list_entries_end
->next
= entry
;
340 seg
->segment_list_entries_end
= entry
;
342 /* drop first item */
343 if (seg
->list_size
&& seg
->segment_count
>= seg
->list_size
) {
344 entry
= seg
->segment_list_entries
;
345 seg
->segment_list_entries
= seg
->segment_list_entries
->next
;
346 av_free(entry
->filename
);
350 avio_close(seg
->list_pb
);
351 if ((ret
= segment_list_open(s
)) < 0)
353 for (entry
= seg
->segment_list_entries
; entry
; entry
= entry
->next
)
354 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, entry
, s
);
355 if (seg
->list_type
== LIST_TYPE_M3U8
&& is_last
)
356 avio_printf(seg
->list_pb
, "#EXT-X-ENDLIST\n");
358 segment_list_print_entry(seg
->list_pb
, seg
->list_type
, &seg
->cur_entry
, s
);
360 avio_flush(seg
->list_pb
);
363 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' count:%d ended\n",
364 seg
->avf
->filename
, seg
->segment_count
);
365 seg
->segment_count
++;
373 static int parse_times(void *log_ctx
, int64_t **times
, int *nb_times
,
374 const char *times_str
)
378 char *times_str1
= av_strdup(times_str
);
379 char *saveptr
= NULL
;
382 return AVERROR(ENOMEM
);
384 #define FAIL(err) ret = err; goto end
387 for (p
= times_str1
; *p
; p
++)
391 *times
= av_malloc_array(*nb_times
, sizeof(**times
));
393 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced times array\n");
394 FAIL(AVERROR(ENOMEM
));
398 for (i
= 0; i
< *nb_times
; i
++) {
400 char *tstr
= av_strtok(p
, ",", &saveptr
);
403 if (!tstr
|| !tstr
[0]) {
404 av_log(log_ctx
, AV_LOG_ERROR
, "Empty time specification in times list %s\n",
406 FAIL(AVERROR(EINVAL
));
409 ret
= av_parse_time(&t
, tstr
, 1);
411 av_log(log_ctx
, AV_LOG_ERROR
,
412 "Invalid time duration specification '%s' in times list %s\n", tstr
, times_str
);
413 FAIL(AVERROR(EINVAL
));
417 /* check on monotonicity */
418 if (i
&& (*times
)[i
-1] > (*times
)[i
]) {
419 av_log(log_ctx
, AV_LOG_ERROR
,
420 "Specified time %f is greater than the following time %f\n",
421 (float)((*times
)[i
])/1000000, (float)((*times
)[i
-1])/1000000);
422 FAIL(AVERROR(EINVAL
));
431 static int parse_frames(void *log_ctx
, int **frames
, int *nb_frames
,
432 const char *frames_str
)
436 char *frames_str1
= av_strdup(frames_str
);
437 char *saveptr
= NULL
;
440 return AVERROR(ENOMEM
);
442 #define FAIL(err) ret = err; goto end
445 for (p
= frames_str1
; *p
; p
++)
449 *frames
= av_malloc_array(*nb_frames
, sizeof(**frames
));
451 av_log(log_ctx
, AV_LOG_ERROR
, "Could not allocate forced frames array\n");
452 FAIL(AVERROR(ENOMEM
));
456 for (i
= 0; i
< *nb_frames
; i
++) {
459 char *fstr
= av_strtok(p
, ",", &saveptr
);
463 av_log(log_ctx
, AV_LOG_ERROR
, "Empty frame specification in frame list %s\n",
465 FAIL(AVERROR(EINVAL
));
467 f
= strtol(fstr
, &tailptr
, 10);
468 if (*tailptr
|| f
<= 0 || f
>= INT_MAX
) {
469 av_log(log_ctx
, AV_LOG_ERROR
,
470 "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
472 FAIL(AVERROR(EINVAL
));
476 /* check on monotonicity */
477 if (i
&& (*frames
)[i
-1] > (*frames
)[i
]) {
478 av_log(log_ctx
, AV_LOG_ERROR
,
479 "Specified frame %d is greater than the following frame %d\n",
480 (*frames
)[i
], (*frames
)[i
-1]);
481 FAIL(AVERROR(EINVAL
));
486 av_free(frames_str1
);
490 static int open_null_ctx(AVIOContext
**ctx
)
492 int buf_size
= 32768;
493 uint8_t *buf
= av_malloc(buf_size
);
495 return AVERROR(ENOMEM
);
496 *ctx
= avio_alloc_context(buf
, buf_size
, AVIO_FLAG_WRITE
, NULL
, NULL
, NULL
, NULL
);
499 return AVERROR(ENOMEM
);
504 static void close_null_ctx(AVIOContext
*pb
)
510 static int select_reference_stream(AVFormatContext
*s
)
512 SegmentContext
*seg
= s
->priv_data
;
515 seg
->reference_stream_index
= -1;
516 if (!strcmp(seg
->reference_stream_specifier
, "auto")) {
517 /* select first index of type with highest priority */
518 int type_index_map
[AVMEDIA_TYPE_NB
];
519 static const enum AVMediaType type_priority_list
[] = {
522 AVMEDIA_TYPE_SUBTITLE
,
524 AVMEDIA_TYPE_ATTACHMENT
526 enum AVMediaType type
;
528 for (i
= 0; i
< AVMEDIA_TYPE_NB
; i
++)
529 type_index_map
[i
] = -1;
531 /* select first index for each type */
532 for (i
= 0; i
< s
->nb_streams
; i
++) {
533 type
= s
->streams
[i
]->codec
->codec_type
;
534 if ((unsigned)type
< AVMEDIA_TYPE_NB
&& type_index_map
[type
] == -1
535 /* ignore attached pictures/cover art streams */
536 && !(s
->streams
[i
]->disposition
& AV_DISPOSITION_ATTACHED_PIC
))
537 type_index_map
[type
] = i
;
540 for (i
= 0; i
< FF_ARRAY_ELEMS(type_priority_list
); i
++) {
541 type
= type_priority_list
[i
];
542 if ((seg
->reference_stream_index
= type_index_map
[type
]) >= 0)
546 for (i
= 0; i
< s
->nb_streams
; i
++) {
547 ret
= avformat_match_stream_specifier(s
, s
->streams
[i
],
548 seg
->reference_stream_specifier
);
552 seg
->reference_stream_index
= i
;
558 if (seg
->reference_stream_index
< 0) {
559 av_log(s
, AV_LOG_ERROR
, "Could not select stream matching identifier '%s'\n",
560 seg
->reference_stream_specifier
);
561 return AVERROR(EINVAL
);
567 static int seg_write_header(AVFormatContext
*s
)
569 SegmentContext
*seg
= s
->priv_data
;
570 AVFormatContext
*oc
= NULL
;
571 AVDictionary
*options
= NULL
;
575 seg
->segment_count
= 0;
576 if (!seg
->write_header_trailer
)
577 seg
->individual_header_trailer
= 0;
579 if (!!seg
->time_str
+ !!seg
->times_str
+ !!seg
->frames_str
> 1) {
580 av_log(s
, AV_LOG_ERROR
,
581 "segment_time, segment_times, and segment_frames options "
582 "are mutually exclusive, select just one of them\n");
583 return AVERROR(EINVAL
);
586 if (seg
->times_str
) {
587 if ((ret
= parse_times(s
, &seg
->times
, &seg
->nb_times
, seg
->times_str
)) < 0)
589 } else if (seg
->frames_str
) {
590 if ((ret
= parse_frames(s
, &seg
->frames
, &seg
->nb_frames
, seg
->frames_str
)) < 0)
593 /* set default value if not specified */
595 seg
->time_str
= av_strdup("2");
596 if ((ret
= av_parse_time(&seg
->time
, seg
->time_str
, 1)) < 0) {
597 av_log(s
, AV_LOG_ERROR
,
598 "Invalid time duration specification '%s' for segment_time option\n",
604 if (seg
->format_options_str
) {
605 ret
= av_dict_parse_string(&seg
->format_options
, seg
->format_options_str
, "=", ":", 0);
607 av_log(s
, AV_LOG_ERROR
, "Could not parse format options list '%s'\n",
608 seg
->format_options_str
);
614 if (seg
->list_type
== LIST_TYPE_UNDEFINED
) {
615 if (av_match_ext(seg
->list
, "csv" )) seg
->list_type
= LIST_TYPE_CSV
;
616 else if (av_match_ext(seg
->list
, "ext" )) seg
->list_type
= LIST_TYPE_EXT
;
617 else if (av_match_ext(seg
->list
, "m3u8")) seg
->list_type
= LIST_TYPE_M3U8
;
618 else if (av_match_ext(seg
->list
, "ffcat,ffconcat")) seg
->list_type
= LIST_TYPE_FFCONCAT
;
619 else seg
->list_type
= LIST_TYPE_FLAT
;
621 if ((ret
= segment_list_open(s
)) < 0)
624 if (seg
->list_type
== LIST_TYPE_EXT
)
625 av_log(s
, AV_LOG_WARNING
, "'ext' list type option is deprecated in favor of 'csv'\n");
627 if ((ret
= select_reference_stream(s
)) < 0)
629 av_log(s
, AV_LOG_VERBOSE
, "Selected stream id:%d type:%s\n",
630 seg
->reference_stream_index
,
631 av_get_media_type_string(s
->streams
[seg
->reference_stream_index
]->codec
->codec_type
));
633 seg
->oformat
= av_guess_format(seg
->format
, s
->filename
, NULL
);
636 ret
= AVERROR_MUXER_NOT_FOUND
;
639 if (seg
->oformat
->flags
& AVFMT_NOFILE
) {
640 av_log(s
, AV_LOG_ERROR
, "format %s not supported.\n",
642 ret
= AVERROR(EINVAL
);
646 if ((ret
= segment_mux_init(s
)) < 0)
650 if ((ret
= set_segment_filename(s
)) < 0)
653 if (seg
->write_header_trailer
) {
654 if ((ret
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
655 &s
->interrupt_callback
, NULL
)) < 0) {
656 av_log(s
, AV_LOG_ERROR
, "Failed to open segment '%s'\n", oc
->filename
);
660 if ((ret
= open_null_ctx(&oc
->pb
)) < 0)
664 av_dict_copy(&options
, seg
->format_options
, 0);
665 ret
= avformat_write_header(oc
, &options
);
666 if (av_dict_count(options
)) {
667 av_log(s
, AV_LOG_ERROR
,
668 "Some of the provided format options in '%s' are not recognized\n", seg
->format_options_str
);
669 ret
= AVERROR(EINVAL
);
677 seg
->segment_frame_count
= 0;
679 av_assert0(s
->nb_streams
== oc
->nb_streams
);
680 for (i
= 0; i
< s
->nb_streams
; i
++) {
681 AVStream
*inner_st
= oc
->streams
[i
];
682 AVStream
*outer_st
= s
->streams
[i
];
683 avpriv_set_pts_info(outer_st
, inner_st
->pts_wrap_bits
, inner_st
->time_base
.num
, inner_st
->time_base
.den
);
686 if (oc
->avoid_negative_ts
> 0 && s
->avoid_negative_ts
< 0)
687 s
->avoid_negative_ts
= 1;
689 if (!seg
->write_header_trailer
) {
690 close_null_ctx(oc
->pb
);
691 if ((ret
= avio_open2(&oc
->pb
, oc
->filename
, AVIO_FLAG_WRITE
,
692 &s
->interrupt_callback
, NULL
)) < 0)
697 av_dict_free(&options
);
700 avio_close(seg
->list_pb
);
702 avformat_free_context(seg
->avf
);
707 static int seg_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
709 SegmentContext
*seg
= s
->priv_data
;
710 AVStream
*st
= s
->streams
[pkt
->stream_index
];
711 int64_t end_pts
= INT64_MAX
, offset
;
712 int start_frame
= INT_MAX
;
719 end_pts
= seg
->segment_count
< seg
->nb_times
?
720 seg
->times
[seg
->segment_count
] : INT64_MAX
;
721 } else if (seg
->frames
) {
722 start_frame
= seg
->segment_count
< seg
->nb_frames
?
723 seg
->frames
[seg
->segment_count
] : INT_MAX
;
725 if (seg
->use_clocktime
) {
726 int64_t avgt
= av_gettime();
727 time_t sec
= avgt
/ 1000000;
728 localtime_r(&sec
, &ti
);
729 usecs
= (int64_t)(ti
.tm_hour
*3600 + ti
.tm_min
*60 + ti
.tm_sec
) * 1000000 + (avgt
% 1000000);
730 wrapped_val
= usecs
% seg
->time
;
731 if (seg
->last_cut
!= usecs
&& wrapped_val
< seg
->last_val
) {
732 seg
->cut_pending
= 1;
733 seg
->last_cut
= usecs
;
735 seg
->last_val
= wrapped_val
;
737 end_pts
= seg
->time
* (seg
->segment_count
+1);
741 av_dlog(s
, "packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
742 pkt
->stream_index
, av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
743 av_ts2timestr(pkt
->duration
, &st
->time_base
),
744 pkt
->flags
& AV_PKT_FLAG_KEY
,
745 pkt
->stream_index
== seg
->reference_stream_index
? seg
->frame_count
: -1);
747 if (pkt
->stream_index
== seg
->reference_stream_index
&&
748 pkt
->flags
& AV_PKT_FLAG_KEY
&&
749 seg
->segment_frame_count
> 0 &&
750 (seg
->cut_pending
|| seg
->frame_count
>= start_frame
||
751 (pkt
->pts
!= AV_NOPTS_VALUE
&&
752 av_compare_ts(pkt
->pts
, st
->time_base
,
753 end_pts
-seg
->time_delta
, AV_TIME_BASE_Q
) >= 0))) {
754 /* sanitize end time in case last packet didn't have a defined duration */
755 if (seg
->cur_entry
.last_duration
== 0)
756 seg
->cur_entry
.end_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
758 if ((ret
= segment_end(s
, seg
->individual_header_trailer
, 0)) < 0)
761 if ((ret
= segment_start(s
, seg
->individual_header_trailer
)) < 0)
764 seg
->cut_pending
= 0;
765 seg
->cur_entry
.index
= seg
->segment_idx
+ seg
->segment_idx_wrap
*seg
->segment_idx_wrap_nb
;
766 seg
->cur_entry
.start_time
= (double)pkt
->pts
* av_q2d(st
->time_base
);
767 seg
->cur_entry
.start_pts
= av_rescale_q(pkt
->pts
, st
->time_base
, AV_TIME_BASE_Q
);
768 seg
->cur_entry
.end_time
= seg
->cur_entry
.start_time
+
769 pkt
->pts
!= AV_NOPTS_VALUE
? (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
) : 0;
770 } else if (pkt
->pts
!= AV_NOPTS_VALUE
&& pkt
->stream_index
== seg
->reference_stream_index
) {
771 seg
->cur_entry
.end_time
=
772 FFMAX(seg
->cur_entry
.end_time
, (double)(pkt
->pts
+ pkt
->duration
) * av_q2d(st
->time_base
));
773 seg
->cur_entry
.last_duration
= pkt
->duration
;
776 if (seg
->segment_frame_count
== 0) {
777 av_log(s
, AV_LOG_VERBOSE
, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
778 seg
->avf
->filename
, pkt
->stream_index
,
779 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
), seg
->frame_count
);
782 av_log(s
, AV_LOG_DEBUG
, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
784 av_ts2timestr(seg
->cur_entry
.start_pts
, &AV_TIME_BASE_Q
),
785 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
786 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
788 /* compute new timestamps */
789 offset
= av_rescale_q(seg
->initial_offset
- (seg
->reset_timestamps
? seg
->cur_entry
.start_pts
: 0),
790 AV_TIME_BASE_Q
, st
->time_base
);
791 if (pkt
->pts
!= AV_NOPTS_VALUE
)
793 if (pkt
->dts
!= AV_NOPTS_VALUE
)
796 av_log(s
, AV_LOG_DEBUG
, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
797 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
798 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
800 ret
= ff_write_chained(seg
->avf
, pkt
->stream_index
, pkt
, s
, seg
->initial_offset
|| seg
->reset_timestamps
);
803 if (pkt
->stream_index
== seg
->reference_stream_index
) {
805 seg
->segment_frame_count
++;
811 static int seg_write_trailer(struct AVFormatContext
*s
)
813 SegmentContext
*seg
= s
->priv_data
;
814 AVFormatContext
*oc
= seg
->avf
;
815 SegmentListEntry
*cur
, *next
;
818 if (!seg
->write_header_trailer
) {
819 if ((ret
= segment_end(s
, 0, 1)) < 0)
821 open_null_ctx(&oc
->pb
);
822 ret
= av_write_trailer(oc
);
823 close_null_ctx(oc
->pb
);
825 ret
= segment_end(s
, 1, 1);
829 avio_close(seg
->list_pb
);
831 av_dict_free(&seg
->format_options
);
833 av_freep(&seg
->times
);
834 av_freep(&seg
->frames
);
836 cur
= seg
->segment_list_entries
;
839 av_free(cur
->filename
);
844 avformat_free_context(oc
);
848 #define OFFSET(x) offsetof(SegmentContext, x)
849 #define E AV_OPT_FLAG_ENCODING_PARAM
850 static const AVOption options
[] = {
851 { "reference_stream", "set reference stream", OFFSET(reference_stream_specifier
), AV_OPT_TYPE_STRING
, {.str
= "auto"}, CHAR_MIN
, CHAR_MAX
, E
},
852 { "segment_format", "set container format used for the segments", OFFSET(format
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
853 { "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
},
854 { "segment_list", "set the segment list filename", OFFSET(list
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
856 { "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"},
857 { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST
, {.i64
= SEGMENT_LIST_FLAG_CACHE
}, INT_MIN
, INT_MAX
, E
, "list_flags"},
858 { "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"},
860 { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
862 { "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" },
863 { "flat", "flat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FLAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
864 { "csv", "csv format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_CSV
}, INT_MIN
, INT_MAX
, E
, "list_type" },
865 { "ext", "extended format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_EXT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
866 { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_FFCONCAT
}, INT_MIN
, INT_MAX
, E
, "list_type" },
867 { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
868 { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST
, {.i64
=LIST_TYPE_M3U8
}, INT_MIN
, INT_MAX
, E
, "list_type" },
870 { "segment_atclocktime", "set segment to be cut at clocktime", OFFSET(use_clocktime
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, E
},
871 { "segment_time", "set segment duration", OFFSET(time_str
),AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
872 { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, 0, 0, E
},
873 { "segment_times", "set segment split time points", OFFSET(times_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
874 { "segment_frames", "set segment split frame numbers", OFFSET(frames_str
),AV_OPT_TYPE_STRING
,{.str
= NULL
}, 0, 0, E
},
875 { "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
876 { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, 0, 0, E
},
877 { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, E
},
878 { "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
},
880 { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, E
},
881 { "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
},
882 { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, E
},
883 { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset
), AV_OPT_TYPE_DURATION
, {.i64
= 0}, -INT64_MAX
, INT64_MAX
, E
},
887 static const AVClass seg_class
= {
888 .class_name
= "segment muxer",
889 .item_name
= av_default_item_name
,
891 .version
= LIBAVUTIL_VERSION_INT
,
894 AVOutputFormat ff_segment_muxer
= {
896 .long_name
= NULL_IF_CONFIG_SMALL("segment"),
897 .priv_data_size
= sizeof(SegmentContext
),
898 .flags
= AVFMT_NOFILE
|AVFMT_GLOBALHEADER
,
899 .write_header
= seg_write_header
,
900 .write_packet
= seg_write_packet
,
901 .write_trailer
= seg_write_trailer
,
902 .priv_class
= &seg_class
,
905 static const AVClass sseg_class
= {
906 .class_name
= "stream_segment muxer",
907 .item_name
= av_default_item_name
,
909 .version
= LIBAVUTIL_VERSION_INT
,
912 AVOutputFormat ff_stream_segment_muxer
= {
913 .name
= "stream_segment,ssegment",
914 .long_name
= NULL_IF_CONFIG_SMALL("streaming segment muxer"),
915 .priv_data_size
= sizeof(SegmentContext
),
916 .flags
= AVFMT_NOFILE
,
917 .write_header
= seg_write_header
,
918 .write_packet
= seg_write_packet
,
919 .write_trailer
= seg_write_trailer
,
920 .priv_class
= &sseg_class
,