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
29 #include <float.h>
30
33
41
50
60
61 #define SEGMENT_LIST_FLAG_CACHE 1
62 #define SEGMENT_LIST_FLAG_LIVE 2
63
65 const AVClass *
class;
/**< Class for private options. */
66 int segment_idx;
///< index of the segment file to write, starting from 0
71 char *
format;
///< format to use for output segment files
72 char *
list;
///< filename for the segment list file
74 int list_size;
///< number of entries for the segment list file
77 char *
time_str;
///< segment duration specification string
78 int64_t
time;
///< segment duration
79
80 char *
times_str;
///< segment times specification string
81 int64_t *
times;
///< list of segment interval specification
82 int nb_times;
///< number of elments in the times array
83
84 char *
frames_str;
///< segment frame numbers specification string
85 int *
frames;
///< list of frame number specification
86 int nb_frames;
///< number of elments in the frames array
88
92
94 int64_t
initial_offset;
///< initial timestamps offset, expressed in microseconds
97
101
104
106 {
107 int needs_quoting = !!str[strcspn(str, "\",\n\r")];
108
109 if (needs_quoting)
111
112 for (; *str; str++) {
113 if (*str == '"')
116 }
117 if (needs_quoting)
119 }
120
122 {
125 int i;
126
128 if (!oc)
130
134
138
148 } else {
150 }
152 }
153
154 return 0;
155 }
156
158 {
161
168 }
170 return 0;
171 }
172
174 {
177 int err = 0;
178
179 if (write_header) {
183 return err;
185 }
186
189 return err;
190
193 return err;
194
197
198 if (write_header) {
200 return err;
201 }
202
204 return 0;
205 }
206
208 {
211
214 if (ret < 0)
216
219 double max_duration = 0;
220
226
229 avio_printf(seg->
list_pb,
"#EXT-X-TARGETDURATION:%"PRId64
"\n", (int64_t)ceil(max_duration));
232 }
233
235 }
236
240 void *log_ctx)
241 {
242 switch (list_type) {
245 break;
250 break;
254 break;
256 {
260 "Error writing list entry '%s' in list file\n", list_entry->
filename);
261 return;
262 }
265 break;
266 }
267 default:
269 }
270 }
271
273 {
277
278 av_write_frame(oc, NULL);
/* Flush any buffered data (fragmented mp4) */
279 if (write_trailer)
281
282 if (ret < 0)
285
289 if (!entry) {
292 }
293
294 /* append new element */
295 memcpy(entry, &seg->
cur_entry,
sizeof(*entry));
298 else
301
302 /* drop first item */
307 }
308
316 } else {
318 }
320 }
321
325
328
330 }
331
332 static int parse_times(
void *log_ctx, int64_t **times,
int *nb_times,
333 const char *times_str)
334 {
335 char *p;
338 char *saveptr = NULL;
339
340 if (!times_str1)
342
343 #define FAIL(err) ret = err; goto end
344
345 *nb_times = 1;
346 for (p = times_str1; *p; p++)
347 if (*p == ',')
348 (*nb_times)++;
349
350 *times =
av_malloc(
sizeof(**times) * *nb_times);
351 if (!*times) {
354 }
355
356 p = times_str1;
357 for (i = 0; i < *nb_times; i++) {
359 char *tstr =
av_strtok(p,
",", &saveptr);
360 p = NULL;
361
362 if (!tstr || !tstr[0]) {
364 times_str);
366 }
367
369 if (ret < 0) {
371 "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
373 }
375
376 /* check on monotonicity */
377 if (i && (*times)[i-1] > (*times)[i]) {
379 "Specified time %f is greater than the following time %f\n",
380 (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
382 }
383 }
384
388 }
389
391 const char *frames_str)
392 {
393 char *p;
395 char *frames_str1 =
av_strdup(frames_str);
396 char *saveptr = NULL;
397
398 if (!frames_str1)
400
401 #define FAIL(err) ret = err; goto end
402
403 *nb_frames = 1;
404 for (p = frames_str1; *p; p++)
405 if (*p == ',')
406 (*nb_frames)++;
407
408 *frames =
av_malloc(
sizeof(**frames) * *nb_frames);
409 if (!*frames) {
412 }
413
414 p = frames_str1;
415 for (i = 0; i < *nb_frames; i++) {
416 long int f;
417 char *tailptr;
418 char *fstr =
av_strtok(p,
",", &saveptr);
419
420 p = NULL;
421 if (!fstr) {
423 frames_str);
425 }
426 f = strtol(fstr, &tailptr, 10);
427 if (*tailptr || f <= 0 || f >= INT_MAX) {
429 "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
430 fstr);
432 }
433 (*frames)[i] = f;
434
435 /* check on monotonicity */
436 if (i && (*frames)[i-1] > (*frames)[i]) {
438 "Specified frame %d is greater than the following frame %d\n",
439 (*frames)[i], (*frames)[i-1]);
441 }
442 }
443
447 }
448
450 {
451 int buf_size = 32768;
453 if (!buf)
456 if (!*ctx) {
459 }
460 return 0;
461 }
462
464 {
467 }
468
470 {
473
476 /* select first index of type with highest priority */
478 static const enum AVMediaType type_priority_list[] = {
484 };
486
488 type_index_map[i] = -1;
489
490 /* select first index for each type */
493 if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
494 /* ignore attached pictures/cover art streams */
496 type_index_map[type] = i;
497 }
498
500 type = type_priority_list[i];
502 break;
503 }
504 } else {
508 if (ret < 0)
510 if (ret > 0) {
512 break;
513 }
514 }
515 }
516
521 }
522
523 return 0;
524 }
525
527 {
531
535
538 "segment_time, segment_times, and segment_frames options "
539 "are mutually exclusive, select just one of them\n");
541 }
542
549 } else {
550 /* set default value if not specified */
555 "Invalid time duration specification '%s' for segment_time option\n",
558 }
559 }
560
568 }
570 goto fail;
571 }
574
576 goto fail;
580
582
585 goto fail;
586 }
591 goto fail;
592 }
593
595 goto fail;
597
599 goto fail;
600
604 goto fail;
605 } else {
607 goto fail;
608 }
609
612 goto fail;
613 }
615
618
623 goto fail;
624 }
625
626 fail:
627 if (ret) {
632 }
634 }
635
637 {
641 int64_t end_pts = INT64_MAX,
offset;
642 int start_frame = INT_MAX;
644
651 } else {
653 }
654
655 av_dlog(s,
"packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
659
667 goto fail;
668
670 goto fail;
671
673
680 }
681
683 av_log(s,
AV_LOG_DEBUG,
"segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
687 }
688
689 av_log(s,
AV_LOG_DEBUG,
"stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
694
695 /* compute new timestamps */
702
706
708
709 fail:
712
713 if (ret < 0) {
717 }
718
720 }
721
723 {
727
731 goto fail;
735 } else {
737 }
738 fail:
741
745
747 while (cur) {
750 cur = next;
751 }
752
755 }
756
757 #define OFFSET(x) offsetof(SegmentContext, x)
758 #define E AV_OPT_FLAG_ENCODING_PARAM
760 {
"reference_stream",
"set reference stream",
OFFSET(reference_stream_specifier),
AV_OPT_TYPE_STRING, {.str =
"auto"}, CHAR_MIN, CHAR_MAX,
E },
761 {
"segment_format",
"set container format used for the segments",
OFFSET(format),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
E },
763
767
768 {
"segment_list_size",
"set the maximum number of playlist entries",
OFFSET(list_size),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
769
777
779 {
"segment_time_delta",
"set approximation value used for the segment times",
OFFSET(time_delta),
AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0,
E },
782 {
"segment_wrap",
"set number after which the index wraps",
OFFSET(segment_idx_wrap),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
783 {
"segment_start_number",
"set the sequence number of the first segment",
OFFSET(segment_idx),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
784
785 {
"individual_header_trailer",
"write header/trailer to each segment",
OFFSET(individual_header_trailer),
AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1,
E },
786 {
"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 },
787 {
"reset_timestamps",
"reset timestamps at the begin of each segment",
OFFSET(reset_timestamps),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1,
E },
788 {
"initial_offset",
"set initial timestamp offset",
OFFSET(initial_offset),
AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX,
E },
789 { NULL },
790 };
791
797 };
798
807 .priv_class = &seg_class,
808 };
809
815 };
816
818 .
name =
"stream_segment,ssegment",
825 .priv_class = &sseg_class,
826 };