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#include <time.h>
31
32#include "avformat.h"
33#include "internal.h"
34
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/timestamp.h"
43
44typedef struct SegmentListEntry {
45    int index;
46    double start_time, end_time;
47    int64_t start_pts;
48    int64_t offset_pts;
49    char *filename;
50    struct SegmentListEntry *next;
51} SegmentListEntry;
52
53typedef enum {
54    LIST_TYPE_UNDEFINED = -1,
55    LIST_TYPE_FLAT = 0,
56    LIST_TYPE_CSV,
57    LIST_TYPE_M3U8,
58    LIST_TYPE_EXT, ///< deprecated
59    LIST_TYPE_FFCONCAT,
60    LIST_TYPE_NB,
61} ListType;
62
63#define SEGMENT_LIST_FLAG_CACHE 1
64#define SEGMENT_LIST_FLAG_LIVE  2
65
66typedef struct {
67    const AVClass *class;  /**< Class for private options. */
68    int segment_idx;       ///< index of the segment file to write, starting from 0
69    int segment_idx_wrap;  ///< number after which the index wraps
70    int segment_idx_wrap_nb;  ///< number of time the index has wraped
71    int segment_count;     ///< number of segment files already written
72    AVOutputFormat *oformat;
73    AVFormatContext *avf;
74    char *format;          ///< format to use for output segment files
75    char *list;            ///< filename for the segment list file
76    int   list_flags;      ///< flags affecting list generation
77    int   list_size;       ///< number of entries for the segment list file
78
79    int use_clocktime;    ///< flag to cut segments at regular clock time
80    int64_t last_val;      ///< remember last time for wrap around detection
81    int64_t last_cut;      ///< remember last cut
82    int cut_pending;
83
84    char *entry_prefix;    ///< prefix to add to list entry filenames
85    ListType list_type;    ///< set the list type
86    AVIOContext *list_pb;  ///< list file put-byte context
87    char *time_str;        ///< segment duration specification string
88    int64_t time;          ///< segment duration
89
90    char *times_str;       ///< segment times specification string
91    int64_t *times;        ///< list of segment interval specification
92    int nb_times;          ///< number of elments in the times array
93
94    char *frames_str;      ///< segment frame numbers specification string
95    int *frames;           ///< list of frame number specification
96    int nb_frames;         ///< number of elments in the frames array
97    int frame_count;
98
99    int64_t time_delta;
100    int  individual_header_trailer; /**< Set by a private option. */
101    int  write_header_trailer; /**< Set by a private option. */
102
103    int reset_timestamps;  ///< reset timestamps at the begin of each segment
104    int64_t initial_offset;    ///< initial timestamps offset, expressed in microseconds
105    char *reference_stream_specifier; ///< reference stream specifier
106    int   reference_stream_index;
107
108    SegmentListEntry cur_entry;
109    SegmentListEntry *segment_list_entries;
110    SegmentListEntry *segment_list_entries_end;
111
112    int is_first_pkt;      ///< tells if it is the first packet in the segment
113} SegmentContext;
114
115static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
116{
117    int needs_quoting = !!str[strcspn(str, "\",\n\r")];
118
119    if (needs_quoting)
120        avio_w8(ctx, '"');
121
122    for (; *str; str++) {
123        if (*str == '"')
124            avio_w8(ctx, '"');
125        avio_w8(ctx, *str);
126    }
127    if (needs_quoting)
128        avio_w8(ctx, '"');
129}
130
131static int segment_mux_init(AVFormatContext *s)
132{
133    SegmentContext *seg = s->priv_data;
134    AVFormatContext *oc;
135    int i;
136
137    seg->avf = oc = avformat_alloc_context();
138    if (!oc)
139        return AVERROR(ENOMEM);
140
141    oc->oformat            = seg->oformat;
142    oc->interrupt_callback = s->interrupt_callback;
143    av_dict_copy(&oc->metadata, s->metadata, 0);
144
145    for (i = 0; i < s->nb_streams; i++) {
146        AVStream *st;
147        AVCodecContext *icodec, *ocodec;
148
149        if (!(st = avformat_new_stream(oc, NULL)))
150            return AVERROR(ENOMEM);
151        icodec = s->streams[i]->codec;
152        ocodec = st->codec;
153        avcodec_copy_context(ocodec, icodec);
154        if (!oc->oformat->codec_tag ||
155            av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
156            av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
157            ocodec->codec_tag = icodec->codec_tag;
158        } else {
159            ocodec->codec_tag = 0;
160        }
161        st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
162    }
163
164    return 0;
165}
166
167static int set_segment_filename(AVFormatContext *s)
168{
169    SegmentContext *seg = s->priv_data;
170    AVFormatContext *oc = seg->avf;
171    size_t size;
172
173    if (seg->segment_idx_wrap)
174        seg->segment_idx %= seg->segment_idx_wrap;
175    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
176                              s->filename, seg->segment_idx) < 0) {
177        av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
178        return AVERROR(EINVAL);
179    }
180
181    /* copy modified name in list entry */
182    size = strlen(av_basename(oc->filename)) + 1;
183    if (seg->entry_prefix)
184        size += strlen(seg->entry_prefix);
185
186    seg->cur_entry.filename = av_mallocz(size);
187    if (!seg->cur_entry.filename)
188        return AVERROR(ENOMEM);
189    snprintf(seg->cur_entry.filename, size, "%s%s",
190             seg->entry_prefix ? seg->entry_prefix : "",
191             av_basename(oc->filename));
192
193    return 0;
194}
195
196static int segment_start(AVFormatContext *s, int write_header)
197{
198    SegmentContext *seg = s->priv_data;
199    AVFormatContext *oc = seg->avf;
200    int err = 0;
201
202    if (write_header) {
203        avformat_free_context(oc);
204        seg->avf = NULL;
205        if ((err = segment_mux_init(s)) < 0)
206            return err;
207        oc = seg->avf;
208    }
209
210    seg->segment_idx++;
211    if ((seg->segment_idx_wrap) && (seg->segment_idx%seg->segment_idx_wrap == 0))
212        seg->segment_idx_wrap_nb++;
213
214    if ((err = set_segment_filename(s)) < 0)
215        return err;
216
217    if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
218                          &s->interrupt_callback, NULL)) < 0) {
219        av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
220        return err;
221    }
222
223    if (oc->oformat->priv_class && oc->priv_data)
224        av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
225
226    if (write_header) {
227        if ((err = avformat_write_header(oc, NULL)) < 0)
228            return err;
229    }
230
231    seg->is_first_pkt = 1;
232    return 0;
233}
234
235static int segment_list_open(AVFormatContext *s)
236{
237    SegmentContext *seg = s->priv_data;
238    int ret;
239
240    ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
241                     &s->interrupt_callback, NULL);
242    if (ret < 0) {
243        av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
244        return ret;
245    }
246
247    if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
248        SegmentListEntry *entry;
249        double max_duration = 0;
250
251        avio_printf(seg->list_pb, "#EXTM3U\n");
252        avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
253        avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
254        avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
255                    seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
256
257        av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%d\n",
258               seg->segment_list_entries->index);
259
260        for (entry = seg->segment_list_entries; entry; entry = entry->next)
261            max_duration = FFMAX(max_duration, entry->end_time - entry->start_time);
262        avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration));
263    } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
264        avio_printf(seg->list_pb, "ffconcat version 1.0\n");
265    }
266
267    return ret;
268}
269
270static void segment_list_print_entry(AVIOContext      *list_ioctx,
271                                     ListType          list_type,
272                                     const SegmentListEntry *list_entry,
273                                     void *log_ctx)
274{
275    switch (list_type) {
276    case LIST_TYPE_FLAT:
277        avio_printf(list_ioctx, "%s\n", list_entry->filename);
278        break;
279    case LIST_TYPE_CSV:
280    case LIST_TYPE_EXT:
281        print_csv_escaped_str(list_ioctx, list_entry->filename);
282        avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time);
283        break;
284    case LIST_TYPE_M3U8:
285        avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n",
286                    list_entry->end_time - list_entry->start_time, list_entry->filename);
287        break;
288    case LIST_TYPE_FFCONCAT:
289    {
290        char *buf;
291        if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) {
292            av_log(log_ctx, AV_LOG_WARNING,
293                   "Error writing list entry '%s' in list file\n", list_entry->filename);
294            return;
295        }
296        avio_printf(list_ioctx, "file %s\n", buf);
297        av_free(buf);
298        break;
299    }
300    default:
301        av_assert0(!"Invalid list type");
302    }
303}
304
305static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
306{
307    SegmentContext *seg = s->priv_data;
308    AVFormatContext *oc = seg->avf;
309    int ret = 0;
310
311    av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
312    if (write_trailer)
313        ret = av_write_trailer(oc);
314
315    if (ret < 0)
316        av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
317               oc->filename);
318
319    if (seg->list) {
320        if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
321            SegmentListEntry *entry = av_mallocz(sizeof(*entry));
322            if (!entry) {
323                ret = AVERROR(ENOMEM);
324                goto end;
325            }
326
327            /* append new element */
328            memcpy(entry, &seg->cur_entry, sizeof(*entry));
329            if (!seg->segment_list_entries)
330                seg->segment_list_entries = seg->segment_list_entries_end = entry;
331            else
332                seg->segment_list_entries_end->next = entry;
333            seg->segment_list_entries_end = entry;
334
335            /* drop first item */
336            if (seg->list_size && seg->segment_count > seg->list_size) {
337                entry = seg->segment_list_entries;
338                seg->segment_list_entries = seg->segment_list_entries->next;
339                av_free(entry->filename);
340                av_freep(&entry);
341            }
342
343            avio_close(seg->list_pb);
344            if ((ret = segment_list_open(s)) < 0)
345                goto end;
346            for (entry = seg->segment_list_entries; entry; entry = entry->next)
347                segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
348            if (seg->list_type == LIST_TYPE_M3U8 && is_last)
349                avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
350        } else {
351            segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
352        }
353        avio_flush(seg->list_pb);
354    }
355
356    av_log(s, AV_LOG_VERBOSE, "segment:'%s' count:%d ended\n",
357           seg->avf->filename, seg->segment_count);
358    seg->segment_count++;
359
360end:
361    avio_close(oc->pb);
362
363    return ret;
364}
365
366static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
367                       const char *times_str)
368{
369    char *p;
370    int i, ret = 0;
371    char *times_str1 = av_strdup(times_str);
372    char *saveptr = NULL;
373
374    if (!times_str1)
375        return AVERROR(ENOMEM);
376
377#define FAIL(err) ret = err; goto end
378
379    *nb_times = 1;
380    for (p = times_str1; *p; p++)
381        if (*p == ',')
382            (*nb_times)++;
383
384    *times = av_malloc_array(*nb_times, sizeof(**times));
385    if (!*times) {
386        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
387        FAIL(AVERROR(ENOMEM));
388    }
389
390    p = times_str1;
391    for (i = 0; i < *nb_times; i++) {
392        int64_t t;
393        char *tstr = av_strtok(p, ",", &saveptr);
394        p = NULL;
395
396        if (!tstr || !tstr[0]) {
397            av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
398                   times_str);
399            FAIL(AVERROR(EINVAL));
400        }
401
402        ret = av_parse_time(&t, tstr, 1);
403        if (ret < 0) {
404            av_log(log_ctx, AV_LOG_ERROR,
405                   "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
406            FAIL(AVERROR(EINVAL));
407        }
408        (*times)[i] = t;
409
410        /* check on monotonicity */
411        if (i && (*times)[i-1] > (*times)[i]) {
412            av_log(log_ctx, AV_LOG_ERROR,
413                   "Specified time %f is greater than the following time %f\n",
414                   (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
415            FAIL(AVERROR(EINVAL));
416        }
417    }
418
419end:
420    av_free(times_str1);
421    return ret;
422}
423
424static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
425                        const char *frames_str)
426{
427    char *p;
428    int i, ret = 0;
429    char *frames_str1 = av_strdup(frames_str);
430    char *saveptr = NULL;
431
432    if (!frames_str1)
433        return AVERROR(ENOMEM);
434
435#define FAIL(err) ret = err; goto end
436
437    *nb_frames = 1;
438    for (p = frames_str1; *p; p++)
439        if (*p == ',')
440            (*nb_frames)++;
441
442    *frames = av_malloc_array(*nb_frames, sizeof(**frames));
443    if (!*frames) {
444        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
445        FAIL(AVERROR(ENOMEM));
446    }
447
448    p = frames_str1;
449    for (i = 0; i < *nb_frames; i++) {
450        long int f;
451        char *tailptr;
452        char *fstr = av_strtok(p, ",", &saveptr);
453
454        p = NULL;
455        if (!fstr) {
456            av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
457                   frames_str);
458            FAIL(AVERROR(EINVAL));
459        }
460        f = strtol(fstr, &tailptr, 10);
461        if (*tailptr || f <= 0 || f >= INT_MAX) {
462            av_log(log_ctx, AV_LOG_ERROR,
463                   "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
464                   fstr);
465            FAIL(AVERROR(EINVAL));
466        }
467        (*frames)[i] = f;
468
469        /* check on monotonicity */
470        if (i && (*frames)[i-1] > (*frames)[i]) {
471            av_log(log_ctx, AV_LOG_ERROR,
472                   "Specified frame %d is greater than the following frame %d\n",
473                   (*frames)[i], (*frames)[i-1]);
474            FAIL(AVERROR(EINVAL));
475        }
476    }
477
478end:
479    av_free(frames_str1);
480    return ret;
481}
482
483static int open_null_ctx(AVIOContext **ctx)
484{
485    int buf_size = 32768;
486    uint8_t *buf = av_malloc(buf_size);
487    if (!buf)
488        return AVERROR(ENOMEM);
489    *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
490    if (!*ctx) {
491        av_free(buf);
492        return AVERROR(ENOMEM);
493    }
494    return 0;
495}
496
497static void close_null_ctx(AVIOContext *pb)
498{
499    av_free(pb->buffer);
500    av_free(pb);
501}
502
503static int select_reference_stream(AVFormatContext *s)
504{
505    SegmentContext *seg = s->priv_data;
506    int ret, i;
507
508    seg->reference_stream_index = -1;
509    if (!strcmp(seg->reference_stream_specifier, "auto")) {
510        /* select first index of type with highest priority */
511        int type_index_map[AVMEDIA_TYPE_NB];
512        static const enum AVMediaType type_priority_list[] = {
513            AVMEDIA_TYPE_VIDEO,
514            AVMEDIA_TYPE_AUDIO,
515            AVMEDIA_TYPE_SUBTITLE,
516            AVMEDIA_TYPE_DATA,
517            AVMEDIA_TYPE_ATTACHMENT
518        };
519        enum AVMediaType type;
520
521        for (i = 0; i < AVMEDIA_TYPE_NB; i++)
522            type_index_map[i] = -1;
523
524        /* select first index for each type */
525        for (i = 0; i < s->nb_streams; i++) {
526            type = s->streams[i]->codec->codec_type;
527            if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
528                /* ignore attached pictures/cover art streams */
529                && !(s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC))
530                type_index_map[type] = i;
531        }
532
533        for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
534            type = type_priority_list[i];
535            if ((seg->reference_stream_index = type_index_map[type]) >= 0)
536                break;
537        }
538    } else {
539        for (i = 0; i < s->nb_streams; i++) {
540            ret = avformat_match_stream_specifier(s, s->streams[i],
541                                                  seg->reference_stream_specifier);
542            if (ret < 0)
543                return ret;
544            if (ret > 0) {
545                seg->reference_stream_index = i;
546                break;
547            }
548        }
549    }
550
551    if (seg->reference_stream_index < 0) {
552        av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
553               seg->reference_stream_specifier);
554        return AVERROR(EINVAL);
555    }
556
557    return 0;
558}
559
560static int seg_write_header(AVFormatContext *s)
561{
562    SegmentContext *seg = s->priv_data;
563    AVFormatContext *oc = NULL;
564    int ret;
565
566    seg->segment_count = 0;
567    if (!seg->write_header_trailer)
568        seg->individual_header_trailer = 0;
569
570    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
571        av_log(s, AV_LOG_ERROR,
572               "segment_time, segment_times, and segment_frames options "
573               "are mutually exclusive, select just one of them\n");
574        return AVERROR(EINVAL);
575    }
576
577    if (seg->times_str) {
578        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
579            return ret;
580    } else if (seg->frames_str) {
581        if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
582            return ret;
583    } else {
584        /* set default value if not specified */
585        if (!seg->time_str)
586            seg->time_str = av_strdup("2");
587        if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
588            av_log(s, AV_LOG_ERROR,
589                   "Invalid time duration specification '%s' for segment_time option\n",
590                   seg->time_str);
591            return ret;
592        }
593    }
594
595    if (seg->list) {
596        if (seg->list_type == LIST_TYPE_UNDEFINED) {
597            if      (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
598            else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
599            else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
600            else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
601            else                                      seg->list_type = LIST_TYPE_FLAT;
602        }
603        if ((ret = segment_list_open(s)) < 0)
604            goto fail;
605    }
606    if (seg->list_type == LIST_TYPE_EXT)
607        av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
608
609    if ((ret = select_reference_stream(s)) < 0)
610        goto fail;
611    av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
612           seg->reference_stream_index,
613           av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type));
614
615    seg->oformat = av_guess_format(seg->format, s->filename, NULL);
616
617    if (!seg->oformat) {
618        ret = AVERROR_MUXER_NOT_FOUND;
619        goto fail;
620    }
621    if (seg->oformat->flags & AVFMT_NOFILE) {
622        av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
623               seg->oformat->name);
624        ret = AVERROR(EINVAL);
625        goto fail;
626    }
627
628    if ((ret = segment_mux_init(s)) < 0)
629        goto fail;
630    oc = seg->avf;
631
632    if ((ret = set_segment_filename(s)) < 0)
633        goto fail;
634
635    if (seg->write_header_trailer) {
636        if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
637                              &s->interrupt_callback, NULL)) < 0) {
638            av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
639            goto fail;
640        }
641    } else {
642        if ((ret = open_null_ctx(&oc->pb)) < 0)
643            goto fail;
644    }
645
646    if ((ret = avformat_write_header(oc, NULL)) < 0) {
647        avio_close(oc->pb);
648        goto fail;
649    }
650    seg->is_first_pkt = 1;
651
652    if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
653        s->avoid_negative_ts = 1;
654
655    if (!seg->write_header_trailer) {
656        close_null_ctx(oc->pb);
657        if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
658                              &s->interrupt_callback, NULL)) < 0)
659            goto fail;
660    }
661
662fail:
663    if (ret) {
664        if (seg->list)
665            avio_close(seg->list_pb);
666        if (seg->avf)
667            avformat_free_context(seg->avf);
668    }
669    return ret;
670}
671
672static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
673{
674    SegmentContext *seg = s->priv_data;
675    AVStream *st = s->streams[pkt->stream_index];
676    int64_t end_pts = INT64_MAX, offset;
677    int start_frame = INT_MAX;
678    int ret;
679    struct tm ti;
680    int64_t usecs;
681    int64_t wrapped_val;
682
683    if (seg->times) {
684        end_pts = seg->segment_count < seg->nb_times ?
685            seg->times[seg->segment_count] : INT64_MAX;
686    } else if (seg->frames) {
687        start_frame = seg->segment_count < seg->nb_frames ?
688            seg->frames[seg->segment_count] : INT_MAX;
689    } else {
690        if (seg->use_clocktime) {
691            int64_t avgt = av_gettime();
692            time_t sec = avgt / 1000000;
693#if HAVE_LOCALTIME_R
694            localtime_r(&sec, &ti);
695#else
696            ti = *localtime(&sec);
697#endif
698            usecs = (int64_t)(ti.tm_hour*3600 + ti.tm_min*60 + ti.tm_sec) * 1000000 + (avgt % 1000000);
699            wrapped_val = usecs % seg->time;
700            if (seg->last_cut != usecs && wrapped_val < seg->last_val) {
701                seg->cut_pending = 1;
702                seg->last_cut = usecs;
703            }
704            seg->last_val = wrapped_val;
705        } else {
706            end_pts = seg->time * (seg->segment_count+1);
707        }
708    }
709
710    av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
711           pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
712           pkt->flags & AV_PKT_FLAG_KEY,
713           pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);
714
715    if (pkt->stream_index == seg->reference_stream_index &&
716        pkt->flags & AV_PKT_FLAG_KEY &&
717        (seg->cut_pending || seg->frame_count >= start_frame ||
718         (pkt->pts != AV_NOPTS_VALUE &&
719          av_compare_ts(pkt->pts, st->time_base,
720                        end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
721        if ((ret = segment_end(s, seg->individual_header_trailer, 0)) < 0)
722            goto fail;
723
724        if ((ret = segment_start(s, seg->individual_header_trailer)) < 0)
725            goto fail;
726
727        seg->cut_pending = 0;
728        seg->cur_entry.index = seg->segment_idx + seg->segment_idx_wrap*seg->segment_idx_wrap_nb;
729        seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base);
730        seg->cur_entry.start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
731    } else if (pkt->pts != AV_NOPTS_VALUE) {
732        seg->cur_entry.end_time =
733            FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
734    }
735
736    if (seg->is_first_pkt) {
737        av_log(s, AV_LOG_VERBOSE, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
738               seg->avf->filename, pkt->stream_index,
739               av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
740        seg->is_first_pkt = 0;
741    }
742
743    av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
744           pkt->stream_index,
745           av_ts2timestr(seg->cur_entry.start_pts, &AV_TIME_BASE_Q),
746           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
747           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
748
749    /* compute new timestamps */
750    offset = av_rescale_q(seg->initial_offset - (seg->reset_timestamps ? seg->cur_entry.start_pts : 0),
751                          AV_TIME_BASE_Q, st->time_base);
752    if (pkt->pts != AV_NOPTS_VALUE)
753        pkt->pts += offset;
754    if (pkt->dts != AV_NOPTS_VALUE)
755        pkt->dts += offset;
756
757    av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
758           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
759           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
760
761    ret = ff_write_chained(seg->avf, pkt->stream_index, pkt, s);
762
763fail:
764    if (pkt->stream_index == seg->reference_stream_index)
765        seg->frame_count++;
766
767    return ret;
768}
769
770static int seg_write_trailer(struct AVFormatContext *s)
771{
772    SegmentContext *seg = s->priv_data;
773    AVFormatContext *oc = seg->avf;
774    SegmentListEntry *cur, *next;
775
776    int ret;
777    if (!seg->write_header_trailer) {
778        if ((ret = segment_end(s, 0, 1)) < 0)
779            goto fail;
780        open_null_ctx(&oc->pb);
781        ret = av_write_trailer(oc);
782        close_null_ctx(oc->pb);
783    } else {
784        ret = segment_end(s, 1, 1);
785    }
786fail:
787    if (seg->list)
788        avio_close(seg->list_pb);
789
790    av_opt_free(seg);
791    av_freep(&seg->times);
792    av_freep(&seg->frames);
793
794    cur = seg->segment_list_entries;
795    while (cur) {
796        next = cur->next;
797        av_free(cur->filename);
798        av_free(cur);
799        cur = next;
800    }
801
802    avformat_free_context(oc);
803    return ret;
804}
805
806#define OFFSET(x) offsetof(SegmentContext, x)
807#define E AV_OPT_FLAG_ENCODING_PARAM
808static const AVOption options[] = {
809    { "reference_stream",  "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
810    { "segment_format",    "set container format used for the segments", OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
811    { "segment_list",      "set the segment list filename",              OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
812
813    { "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"},
814    { "cache",             "allow list caching",                                    0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX,   E, "list_flags"},
815    { "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"},
816
817    { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.i64 = 0},     0, INT_MAX, E },
818
819    { "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" },
820    { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
821    { "csv",  "csv format",      0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV  }, INT_MIN, INT_MAX, E, "list_type" },
822    { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, E, "list_type" },
823    { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" },
824    { "m3u8", "M3U8 format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
825    { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
826
827    { "segment_atclocktime",      "set segment to be cut at clocktime",  OFFSET(use_clocktime), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E},
828    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
829    { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
830    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
831    { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
832    { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
833    { "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
834    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
835    { "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 },
836
837    { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
838    { "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 },
839    { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
840    { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E },
841    { NULL },
842};
843
844static const AVClass seg_class = {
845    .class_name = "segment muxer",
846    .item_name  = av_default_item_name,
847    .option     = options,
848    .version    = LIBAVUTIL_VERSION_INT,
849};
850
851AVOutputFormat ff_segment_muxer = {
852    .name           = "segment",
853    .long_name      = NULL_IF_CONFIG_SMALL("segment"),
854    .priv_data_size = sizeof(SegmentContext),
855    .flags          = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
856    .write_header   = seg_write_header,
857    .write_packet   = seg_write_packet,
858    .write_trailer  = seg_write_trailer,
859    .priv_class     = &seg_class,
860};
861
862static const AVClass sseg_class = {
863    .class_name = "stream_segment muxer",
864    .item_name  = av_default_item_name,
865    .option     = options,
866    .version    = LIBAVUTIL_VERSION_INT,
867};
868
869AVOutputFormat ff_stream_segment_muxer = {
870    .name           = "stream_segment,ssegment",
871    .long_name      = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
872    .priv_data_size = sizeof(SegmentContext),
873    .flags          = AVFMT_NOFILE,
874    .write_header   = seg_write_header,
875    .write_packet   = seg_write_packet,
876    .write_trailer  = seg_write_trailer,
877    .priv_class     = &sseg_class,
878};
879