1 /*
2 * Copyright (c) 2011, Luca Barbato
3 *
4 * This file is part of Libav.
5 *
6 * Libav 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 * Libav 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 Libav; 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
49
59
60 #define SEGMENT_LIST_FLAG_CACHE 1
61 #define SEGMENT_LIST_FLAG_LIVE 2
62
64 const AVClass *
class;
/**< Class for private options. */
65 int segment_idx;
///< index of the segment file to write, starting from 0
70 char *
format;
///< format to use for output segment files
71 char *
list;
///< filename for the segment list file
73 int list_size;
///< number of entries for the segment list file
76 char *
time_str;
///< segment duration specification string
77 int64_t
time;
///< segment duration
78
79 char *
times_str;
///< segment times specification string
80 int64_t *
times;
///< list of segment interval specification
81 int nb_times;
///< number of elments in the times array
82
83 char *
frames_str;
///< segment frame numbers specification string
84 int *
frames;
///< list of frame number specification
85 int nb_frames;
///< number of elments in the frames array
87
88 char *
time_delta_str;
///< approximation value duration used for the segment times
92
96
100
103
105 {
106 int needs_quoting = !!str[strcspn(str, "\",\n\r")];
107
108 if (needs_quoting)
110
111 for (; *str; str++) {
112 if (*str == '"')
115 }
116 if (needs_quoting)
118 }
119
121 {
124 int i;
125
127 if (!oc)
129
133
137
147 } else {
149 }
151 }
152
153 return 0;
154 }
155
157 {
160
167 }
169 return 0;
170 }
171
173 {
176 int err = 0;
177
178 if (write_header) {
182 return err;
184 }
185
188 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
324
326 }
327
328 static int parse_times(
void *log_ctx, int64_t **times,
int *nb_times,
329 const char *times_str)
330 {
331 char *p;
334 char *saveptr = NULL;
335
336 if (!times_str1)
338
339 #define FAIL(err) ret = err; goto end
340
341 *nb_times = 1;
342 for (p = times_str1; *p; p++)
343 if (*p == ',')
344 (*nb_times)++;
345
346 *times =
av_malloc(
sizeof(**times) * *nb_times);
347 if (!*times) {
350 }
351
352 p = times_str1;
353 for (i = 0; i < *nb_times; i++) {
355 char *tstr =
av_strtok(p,
",", &saveptr);
356 p = NULL;
357
358 if (!tstr || !tstr[0]) {
360 times_str);
362 }
363
365 if (ret < 0) {
367 "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
369 }
371
372 /* check on monotonicity */
373 if (i && (*times)[i-1] > (*times)[i]) {
375 "Specified time %f is greater than the following time %f\n",
376 (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
378 }
379 }
380
384 }
385
387 const char *frames_str)
388 {
389 char *p;
391 char *frames_str1 =
av_strdup(frames_str);
392 char *saveptr = NULL;
393
394 if (!frames_str1)
396
397 #define FAIL(err) ret = err; goto end
398
399 *nb_frames = 1;
400 for (p = frames_str1; *p; p++)
401 if (*p == ',')
402 (*nb_frames)++;
403
404 *frames =
av_malloc(
sizeof(**frames) * *nb_frames);
405 if (!*frames) {
408 }
409
410 p = frames_str1;
411 for (i = 0; i < *nb_frames; i++) {
412 long int f;
413 char *tailptr;
414 char *fstr =
av_strtok(p,
",", &saveptr);
415
416 p = NULL;
417 if (!fstr) {
419 frames_str);
421 }
422 f = strtol(fstr, &tailptr, 10);
423 if (*tailptr || f <= 0 || f >= INT_MAX) {
425 "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
426 fstr);
428 }
429 (*frames)[i] = f;
430
431 /* check on monotonicity */
432 if (i && (*frames)[i-1] > (*frames)[i]) {
434 "Specified frame %d is greater than the following frame %d\n",
435 (*frames)[i], (*frames)[i-1]);
437 }
438 }
439
443 }
444
446 {
447 int buf_size = 32768;
449 if (!buf)
452 if (!*ctx) {
455 }
456 return 0;
457 }
458
460 {
463 }
464
466 {
469
472 /* select first index of type with highest priority */
474 static const enum AVMediaType type_priority_list[] = {
480 };
482
484 type_index_map[i] = -1;
485
486 /* select first index for each type */
489 if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
490 /* ignore attached pictures/cover art streams */
492 type_index_map[type] = i;
493 }
494
496 type = type_priority_list[i];
498 break;
499 }
500 } else {
504 if (ret < 0)
506 if (ret > 0) {
508 break;
509 }
510 }
511 }
512
517 }
518
519 return 0;
520 }
521
523 {
527
531
534 "segment_time, segment_times, and segment_frames options "
535 "are mutually exclusive, select just one of them\n");
537 }
538
545 } else {
546 /* set default value if not specified */
551 "Invalid time duration specification '%s' for segment_time option\n",
554 }
555 }
556
560 "Invalid time duration specification '%s' for delta option\n",
563 }
564 }
565
573 }
575 goto fail;
576 }
579
581 goto fail;
585
587
590 goto fail;
591 }
596 goto fail;
597 }
598
600 goto fail;
602
604 goto fail;
606
610 goto fail;
611 } else {
613 goto fail;
614 }
615
618 goto fail;
619 }
621
624
629 goto fail;
630 }
631
632 fail:
633 if (ret) {
638 }
640 }
641
643 {
647 int64_t end_pts = INT64_MAX;
648 int start_frame = INT_MAX;
650
657 } else {
659 }
660
661 av_dlog(s,
"packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
665
673
674 if (!ret)
676
677 if (ret)
678 goto fail;
679
681
688 }
689
691 av_log(s,
AV_LOG_DEBUG,
"segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
695 }
696
698 av_log(s,
AV_LOG_DEBUG,
"stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
703
704 /* compute new timestamps */
709
713 }
714
716
717 fail:
720
721 if (ret < 0) {
725 }
726
728 }
729
731 {
735
739 goto fail;
743 } else {
745 }
746 fail:
749
753
755 while (cur) {
758 cur = next;
759 }
760
763 }
764
765 #define OFFSET(x) offsetof(SegmentContext, x)
766 #define E AV_OPT_FLAG_ENCODING_PARAM
768 {
"reference_stream",
"set reference stream",
OFFSET(reference_stream_specifier),
AV_OPT_TYPE_STRING, {.str =
"auto"}, CHAR_MIN, CHAR_MAX,
E },
769 {
"segment_format",
"set container format used for the segments",
OFFSET(format),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,
E },
771
775
776 {
"segment_list_size",
"set the maximum number of playlist entries",
OFFSET(list_size),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
777
785
787 {
"segment_time_delta",
"set approximation value used for the segment times",
OFFSET(time_delta_str),
AV_OPT_TYPE_STRING, {.str =
"0"}, 0, 0,
E },
790 {
"segment_wrap",
"set number after which the index wraps",
OFFSET(segment_idx_wrap),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
791 {
"segment_start_number",
"set the sequence number of the first segment",
OFFSET(segment_idx),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
E },
792
793 {
"individual_header_trailer",
"write header/trailer to each segment",
OFFSET(individual_header_trailer),
AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1,
E },
794 {
"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 },
795 {
"reset_timestamps",
"reset timestamps at the begin of each segment",
OFFSET(reset_timestamps),
AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1,
E },
796 { NULL },
797 };
798
804 };
805
814 .priv_class = &seg_class,
815 };
816
822 };
823
825 .
name =
"stream_segment,ssegment",
832 .priv_class = &sseg_class,
833 };