1 /*
2 * Copyright (c) 2011, Luca Barbato
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file generic segmenter
23 * M3U8 specification can be find here:
24 * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming}
25 */
26
27 /* #define DEBUG */
28
31
34
43
53
63
64 #define SEGMENT_LIST_FLAG_CACHE 1
65 #define SEGMENT_LIST_FLAG_LIVE 2
66
68 const AVClass *
class;
/**< Class for private options. */
69 int segment_idx;
///< index of the segment file to write, starting from 0
75 char *
format;
///< format to use for output segment files
78 char *
list;
///< filename for the segment list file
80 int list_size;
///< number of entries for the segment list file
81
83 int64_t
last_val;
///< remember last time for wrap around detection
86
90 char *
time_str;
///< segment duration specification string
91 int64_t
time;
///< segment duration
92
93 char *
times_str;
///< segment times specification string
94 int64_t *
times;
///< list of segment interval specification
95 int nb_times;
///< number of elments in the times array
96
97 char *
frames_str;
///< segment frame numbers specification string
98 int *
frames;
///< list of frame number specification
99 int nb_frames;
///< number of elments in the frames array
102
106
108 int64_t
initial_offset;
///< initial timestamps offset, expressed in microseconds
111
116
118 {
119 int needs_quoting = !!str[strcspn(str, "\",\n\r")];
120
121 if (needs_quoting)
123
124 for (; *str; str++) {
125 if (*str == '"')
128 }
129 if (needs_quoting)
131 }
132
134 {
137 int i;
139
141 if (ret < 0)
144
147
151
161 } else {
163 }
166 }
167
168 return 0;
169 }
170
172 {
176
183 }
184
185 /* copy modified name in list entry */
189
196
197 return 0;
198 }
199
201 {
204 int err = 0;
205
206 if (write_header) {
210 return err;
212 }
213
217
219 return err;
220
224 return err;
225 }
226
229
230 if (write_header) {
232 return err;
233 }
234
236 return 0;
237 }
238
240 {
243
246 if (ret < 0) {
249 }
250
253 double max_duration = 0;
254
260
263
266 avio_printf(seg->
list_pb,
"#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration));
269 }
270
272 }
273
277 void *log_ctx)
278 {
279 switch (list_type) {
282 break;
287 break;
291 break;
293 {
297 "Error writing list entry '%s' in list file\n", list_entry->
filename);
298 return;
299 }
302 break;
303 }
304 default:
306 }
307 }
308
310 {
314
315 av_write_frame(oc, NULL);
/* Flush any buffered data (fragmented mp4) */
316 if (write_trailer)
318
319 if (ret < 0)
322
326 if (!entry) {
329 }
330
331 /* append new element */
332 memcpy(entry, &seg->
cur_entry,
sizeof(*entry));
335 else
338
339 /* drop first item */
345 }
346
354 } else {
356 }
358 }
359
363
366
368 }
369
370 static int parse_times(
void *log_ctx, int64_t **times,
int *nb_times,
371 const char *times_str)
372 {
373 char *p;
376 char *saveptr = NULL;
377
378 if (!times_str1)
380
381 #define FAIL(err) ret = err; goto end
382
383 *nb_times = 1;
384 for (p = times_str1; *p; p++)
385 if (*p == ',')
386 (*nb_times)++;
387
389 if (!*times) {
392 }
393
394 p = times_str1;
395 for (i = 0; i < *nb_times; i++) {
396 int64_t t;
397 char *tstr =
av_strtok(p,
",", &saveptr);
398 p = NULL;
399
400 if (!tstr || !tstr[0]) {
402 times_str);
404 }
405
407 if (ret < 0) {
409 "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
411 }
412 (*times)[i] = t;
413
414 /* check on monotonicity */
415 if (i && (*times)[i-1] > (*times)[i]) {
417 "Specified time %f is greater than the following time %f\n",
418 (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
420 }
421 }
422
426 }
427
429 const char *frames_str)
430 {
431 char *p;
433 char *frames_str1 =
av_strdup(frames_str);
434 char *saveptr = NULL;
435
436 if (!frames_str1)
438
439 #define FAIL(err) ret = err; goto end
440
441 *nb_frames = 1;
442 for (p = frames_str1; *p; p++)
443 if (*p == ',')
444 (*nb_frames)++;
445
447 if (!*frames) {
450 }
451
452 p = frames_str1;
453 for (i = 0; i < *nb_frames; i++) {
454 long int f;
455 char *tailptr;
456 char *fstr =
av_strtok(p,
",", &saveptr);
457
458 p = NULL;
459 if (!fstr) {
461 frames_str);
463 }
464 f = strtol(fstr, &tailptr, 10);
465 if (*tailptr || f <= 0 || f >= INT_MAX) {
467 "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
468 fstr);
470 }
471 (*frames)[i] = f;
472
473 /* check on monotonicity */
474 if (i && (*frames)[i-1] > (*frames)[i]) {
476 "Specified frame %d is greater than the following frame %d\n",
477 (*frames)[i], (*frames)[i-1]);
479 }
480 }
481
485 }
486
488 {
489 int buf_size = 32768;
491 if (!buf)
494 if (!*ctx) {
497 }
498 return 0;
499 }
500
502 {
505 }
506
508 {
511
514 /* select first index of type with highest priority */
516 static const enum AVMediaType type_priority_list[] = {
522 };
524
526 type_index_map[i] = -1;
527
528 /* select first index for each type */
531 if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
532 /* ignore attached pictures/cover art streams */
534 type_index_map[
type] = i;
535 }
536
538 type = type_priority_list[i];
540 break;
541 }
542 } else {
546 if (ret < 0)
548 if (ret > 0) {
550 break;
551 }
552 }
553 }
554
559 }
560
561 return 0;
562 }
563
565 {
570
574
577 "segment_time, segment_times, and segment_frames options "
578 "are mutually exclusive, select just one of them\n");
580 }
581
588 } else {
589 /* set default value if not specified */
594 "Invalid time duration specification '%s' for segment_time option\n",
597 }
598 }
599
602 if (ret < 0) {
605 goto fail;
606 }
607 }
608
616 }
618 goto fail;
619 }
622
624 goto fail;
628
630
633 goto fail;
634 }
639 goto fail;
640 }
641
643 goto fail;
645
647 goto fail;
648
653 goto fail;
654 }
655 } else {
657 goto fail;
658 }
659
664 "Some of the provided format options in '%s' are not recognized\n", seg->
format_options_str);
666 goto fail;
667 }
668
669 if (ret < 0) {
671 goto fail;
672 }
674
677
682 goto fail;
683 }
684
685 fail:
687 if (ret) {
692 }
694 }
695
697 {
700 int64_t end_pts = INT64_MAX,
offset;
701 int start_frame = INT_MAX;
703 struct tm ti;
704 int64_t usecs;
705 int64_t wrapped_val;
706
713 } else {
716 time_t sec = avgt / 1000000;
717 #if HAVE_LOCALTIME_R
719 #else
720 ti = *localtime(&sec);
721 #endif
722 usecs = (int64_t)(ti.tm_hour*3600 + ti.tm_min*60 + ti.tm_sec) * 1000000 + (avgt % 1000000);
723 wrapped_val = usecs % seg->
time;
724 if (seg->
last_cut != usecs && wrapped_val < seg->last_val) {
727 }
729 } else {
731 }
732 }
733
734 av_dlog(s,
"packet stream:%d pts:%s pts_time:%s duration_time:%s is_key:%d frame:%d\n",
739
747 /* sanitize end time in case last packet didn't have a defined duration */
750
752 goto fail;
753
755 goto fail;
756
767 }
768
770 av_log(s,
AV_LOG_VERBOSE,
"segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
773 }
774
775 av_log(s,
AV_LOG_DEBUG,
"stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
780
781 /* compute new timestamps */
788
792
794
795 fail:
799 }
800
802 }
803
805 {
809
813 goto fail;
817 } else {
819 }
820 fail:
823
828
830 while (cur) {
834 cur = next;
835 }
836
839 }
840
841 #define OFFSET(x) offsetof(SegmentContext, x)
842 #define E AV_OPT_FLAG_ENCODING_PARAM
844 {
"reference_stream",
"set reference stream",
OFFSET(reference_stream_specifier),
AV_OPT_TYPE_STRING, {.str =
"auto"}, CHAR_MIN, CHAR_MAX,
E },
845 {
"segment_format",
"set container format used for the segments",
OFFSET(format),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
E },
846 {
"segment_format_options",
"set list of options for the container format used for the segments",
OFFSET(format_options_str),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
E },
848
852
853 {
"segment_list_size",
"set the maximum number of playlist entries",
OFFSET(list_size),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
854
862
863 {
"segment_atclocktime",
"set segment to be cut at clocktime",
OFFSET(use_clocktime),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1,
E},
865 {
"segment_time_delta",
"set approximation value used for the segment times",
OFFSET(time_delta),
AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0,
E },
868 {
"segment_wrap",
"set number after which the index wraps",
OFFSET(segment_idx_wrap),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
869 {
"segment_list_entry_prefix",
"set base url prefix for segments",
OFFSET(entry_prefix),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
E },
870 {
"segment_start_number",
"set the sequence number of the first segment",
OFFSET(segment_idx),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
871 {
"segment_wrap_number",
"set the number of wrap before the first segment",
OFFSET(segment_idx_wrap_nb),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
872
873 {
"individual_header_trailer",
"write header/trailer to each segment",
OFFSET(individual_header_trailer),
AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1,
E },
874 {
"write_header_trailer",
"write a header to the first segment and a trailer to the last one",
OFFSET(write_header_trailer),
AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1,
E },
875 {
"reset_timestamps",
"reset timestamps at the begin of each segment",
OFFSET(reset_timestamps),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1,
E },
876 {
"initial_offset",
"set initial timestamp offset",
OFFSET(initial_offset),
AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX,
E },
877 { NULL },
878 };
879
885 };
886
895 .priv_class = &seg_class,
896 };
897
903 };
904
906 .
name =
"stream_segment,ssegment",
913 .priv_class = &sseg_class,
914 };