segment.c 29.6 KB
Newer Older
1 2 3
/*
 * Copyright (c) 2011, Luca Barbato
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7 8 9 10
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * 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 13 14 15 16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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 19 20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

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 28
/* #define DEBUG */

29 30 31 32 33
#include <float.h>

#include "avformat.h"
#include "internal.h"

34
#include "libavutil/avassert.h"
35 36 37 38 39
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/avstring.h"
#include "libavutil/parseutils.h"
#include "libavutil/mathematics.h"
40
#include "libavutil/timestamp.h"
41

42 43 44
typedef struct SegmentListEntry {
    int index;
    double start_time, end_time;
45
    int64_t start_pts;
46
    int64_t offset_pts;
47
    char *filename;
48 49 50
    struct SegmentListEntry *next;
} SegmentListEntry;

51
typedef enum {
52
    LIST_TYPE_UNDEFINED = -1,
53
    LIST_TYPE_FLAT = 0,
54
    LIST_TYPE_CSV,
55
    LIST_TYPE_M3U8,
56
    LIST_TYPE_EXT, ///< deprecated
57
    LIST_TYPE_FFCONCAT,
58 59 60
    LIST_TYPE_NB,
} ListType;

61 62 63
#define SEGMENT_LIST_FLAG_CACHE 1
#define SEGMENT_LIST_FLAG_LIVE  2

64 65
typedef struct {
    const AVClass *class;  /**< Class for private options. */
66 67 68
    int segment_idx;       ///< index of the segment file to write, starting from 0
    int segment_idx_wrap;  ///< number after which the index wraps
    int segment_count;     ///< number of segment files already written
69
    AVOutputFormat *oformat;
70
    AVFormatContext *avf;
71 72
    char *format;          ///< format to use for output segment files
    char *list;            ///< filename for the segment list file
73
    int   list_flags;      ///< flags affecting list generation
74
    int   list_size;       ///< number of entries for the segment list file
75
    char *list_entry_prefix; ///< prefix to add to list entry filenames
76
    ListType list_type;    ///< set the list type
77
    AVIOContext *list_pb;  ///< list file put-byte context
78 79
    char *time_str;        ///< segment duration specification string
    int64_t time;          ///< segment duration
80

81 82 83
    char *times_str;       ///< segment times specification string
    int64_t *times;        ///< list of segment interval specification
    int nb_times;          ///< number of elments in the times array
84 85 86 87 88 89

    char *frames_str;      ///< segment frame numbers specification string
    int *frames;           ///< list of frame number specification
    int nb_frames;         ///< number of elments in the frames array
    int frame_count;

90
    int64_t time_delta;
91
    int  individual_header_trailer; /**< Set by a private option. */
92
    int  write_header_trailer; /**< Set by a private option. */
93 94

    int reset_timestamps;  ///< reset timestamps at the begin of each segment
95
    int64_t initial_offset;    ///< initial timestamps offset, expressed in microseconds
96 97 98
    char *reference_stream_specifier; ///< reference stream specifier
    int   reference_stream_index;

99
    SegmentListEntry cur_entry;
100 101 102
    SegmentListEntry *segment_list_entries;
    SegmentListEntry *segment_list_entries_end;

103
    int is_first_pkt;      ///< tells if it is the first packet in the segment
104 105
} SegmentContext;

106 107
static void print_csv_escaped_str(AVIOContext *ctx, const char *str)
{
108
    int needs_quoting = !!str[strcspn(str, "\",\n\r")];
109

110
    if (needs_quoting)
111 112
        avio_w8(ctx, '"');

113 114
    for (; *str; str++) {
        if (*str == '"')
115
            avio_w8(ctx, '"');
116
        avio_w8(ctx, *str);
117
    }
118
    if (needs_quoting)
119 120 121
        avio_w8(ctx, '"');
}

122
static int segment_mux_init(AVFormatContext *s)
123
{
124
    SegmentContext *seg = s->priv_data;
125 126 127 128 129 130 131 132 133
    AVFormatContext *oc;
    int i;

    seg->avf = oc = avformat_alloc_context();
    if (!oc)
        return AVERROR(ENOMEM);

    oc->oformat            = seg->oformat;
    oc->interrupt_callback = s->interrupt_callback;
134
    av_dict_copy(&oc->metadata, s->metadata, 0);
135 136 137

    for (i = 0; i < s->nb_streams; i++) {
        AVStream *st;
138 139
        AVCodecContext *icodec, *ocodec;

140 141
        if (!(st = avformat_new_stream(oc, NULL)))
            return AVERROR(ENOMEM);
142 143 144 145 146 147 148 149 150 151
        icodec = s->streams[i]->codec;
        ocodec = st->codec;
        avcodec_copy_context(ocodec, icodec);
        if (!oc->oformat->codec_tag ||
            av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == ocodec->codec_id ||
            av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0) {
            ocodec->codec_tag = icodec->codec_tag;
        } else {
            ocodec->codec_tag = 0;
        }
152 153 154 155 156 157
        st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
    }

    return 0;
}

158 159 160 161
static int set_segment_filename(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
162
    size_t size;
163 164 165 166 167 168 169 170

    if (seg->segment_idx_wrap)
        seg->segment_idx %= seg->segment_idx_wrap;
    if (av_get_frame_filename(oc->filename, sizeof(oc->filename),
                              s->filename, seg->segment_idx) < 0) {
        av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", s->filename);
        return AVERROR(EINVAL);
    }
171 172 173 174 175 176 177 178 179 180 181 182 183

    /* copy modified name in list entry */
    size = strlen(av_basename(oc->filename)) + 1;
    if (seg->list_entry_prefix)
        size += strlen(seg->list_entry_prefix);

    seg->cur_entry.filename = av_mallocz(size);
    if (!seg->cur_entry.filename)
        return AVERROR(ENOMEM);
    snprintf(seg->cur_entry.filename, size, "%s%s",
             seg->list_entry_prefix ? seg->list_entry_prefix : "",
             av_basename(oc->filename));

184 185 186
    return 0;
}

187
static int segment_start(AVFormatContext *s, int write_header)
188
{
189 190
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
191 192
    int err = 0;

193 194
    if (write_header) {
        avformat_free_context(oc);
195
        seg->avf = NULL;
196 197
        if ((err = segment_mux_init(s)) < 0)
            return err;
198
        oc = seg->avf;
199
    }
200

201
    seg->segment_idx++;
202 203
    if ((err = set_segment_filename(s)) < 0)
        return err;
204 205

    if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
206 207
                          &s->interrupt_callback, NULL)) < 0) {
        av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
208
        return err;
209
    }
210

211
    if (oc->oformat->priv_class && oc->priv_data)
212
        av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
213

214 215 216 217
    if (write_header) {
        if ((err = avformat_write_header(oc, NULL)) < 0)
            return err;
    }
218

219
    seg->is_first_pkt = 1;
220 221 222
    return 0;
}

223 224 225
static int segment_list_open(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
226 227 228 229
    int ret;

    ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
                     &s->interrupt_callback, NULL);
230 231
    if (ret < 0) {
        av_log(s, AV_LOG_ERROR, "Failed to open segment list '%s'\n", seg->list);
232
        return ret;
233
    }
234

235 236 237 238
    if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
        SegmentListEntry *entry;
        double max_duration = 0;

239
        avio_printf(seg->list_pb, "#EXTM3U\n");
240
        avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
241
        avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
242 243
        avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
                    seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
244 245 246 247

        for (entry = seg->segment_list_entries; entry; entry = entry->next)
            max_duration = FFMAX(max_duration, entry->end_time - entry->start_time);
        avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%"PRId64"\n", (int64_t)ceil(max_duration));
248 249
    } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
        avio_printf(seg->list_pb, "ffconcat version 1.0\n");
250 251
    }

252
    return ret;
253 254
}

255 256
static void segment_list_print_entry(AVIOContext      *list_ioctx,
                                     ListType          list_type,
257 258
                                     const SegmentListEntry *list_entry,
                                     void *log_ctx)
259 260 261 262 263 264 265 266 267 268 269 270 271 272
{
    switch (list_type) {
    case LIST_TYPE_FLAT:
        avio_printf(list_ioctx, "%s\n", list_entry->filename);
        break;
    case LIST_TYPE_CSV:
    case LIST_TYPE_EXT:
        print_csv_escaped_str(list_ioctx, list_entry->filename);
        avio_printf(list_ioctx, ",%f,%f\n", list_entry->start_time, list_entry->end_time);
        break;
    case LIST_TYPE_M3U8:
        avio_printf(list_ioctx, "#EXTINF:%f,\n%s\n",
                    list_entry->end_time - list_entry->start_time, list_entry->filename);
        break;
273 274 275 276 277 278 279 280 281 282 283 284
    case LIST_TYPE_FFCONCAT:
    {
        char *buf;
        if (av_escape(&buf, list_entry->filename, NULL, AV_ESCAPE_MODE_AUTO, AV_ESCAPE_FLAG_WHITESPACE) < 0) {
            av_log(log_ctx, AV_LOG_WARNING,
                   "Error writing list entry '%s' in list file\n", list_entry->filename);
            return;
        }
        avio_printf(list_ioctx, "file %s\n", buf);
        av_free(buf);
        break;
    }
285 286 287 288 289
    default:
        av_assert0(!"Invalid list type");
    }
}

290
static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
291
{
292 293
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
294 295
    int ret = 0;

296
    av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
297
    if (write_trailer)
298
        ret = av_write_trailer(oc);
299

300
    if (ret < 0)
301
        av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
302 303
               oc->filename);

304
    if (seg->list) {
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
        if (seg->list_size || seg->list_type == LIST_TYPE_M3U8) {
            SegmentListEntry *entry = av_mallocz(sizeof(*entry));
            if (!entry) {
                ret = AVERROR(ENOMEM);
                goto end;
            }

            /* append new element */
            memcpy(entry, &seg->cur_entry, sizeof(*entry));
            if (!seg->segment_list_entries)
                seg->segment_list_entries = seg->segment_list_entries_end = entry;
            else
                seg->segment_list_entries_end->next = entry;
            seg->segment_list_entries_end = entry;

            /* drop first item */
            if (seg->list_size && seg->segment_count > seg->list_size) {
                entry = seg->segment_list_entries;
                seg->segment_list_entries = seg->segment_list_entries->next;
324
                av_free(entry->filename);
325 326 327
                av_freep(&entry);
            }

328
            avio_close(seg->list_pb);
329
            if ((ret = segment_list_open(s)) < 0)
330
                goto end;
331
            for (entry = seg->segment_list_entries; entry; entry = entry->next)
332
                segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
333
            if (seg->list_type == LIST_TYPE_M3U8 && is_last)
334 335
                avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
        } else {
336
            segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
337
        }
338
        avio_flush(seg->list_pb);
339 340
    }

341 342 343 344
    av_log(s, AV_LOG_VERBOSE, "segment:'%s' count:%d ended\n",
           seg->avf->filename, seg->segment_count);
    seg->segment_count++;

345
end:
346 347 348 349 350
    avio_close(oc->pb);

    return ret;
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
static int parse_times(void *log_ctx, int64_t **times, int *nb_times,
                       const char *times_str)
{
    char *p;
    int i, ret = 0;
    char *times_str1 = av_strdup(times_str);
    char *saveptr = NULL;

    if (!times_str1)
        return AVERROR(ENOMEM);

#define FAIL(err) ret = err; goto end

    *nb_times = 1;
    for (p = times_str1; *p; p++)
        if (*p == ',')
            (*nb_times)++;

    *times = av_malloc(sizeof(**times) * *nb_times);
    if (!*times) {
        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced times array\n");
        FAIL(AVERROR(ENOMEM));
    }

    p = times_str1;
    for (i = 0; i < *nb_times; i++) {
        int64_t t;
        char *tstr = av_strtok(p, ",", &saveptr);
        p = NULL;

381 382 383 384 385 386
        if (!tstr || !tstr[0]) {
            av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
                   times_str);
            FAIL(AVERROR(EINVAL));
        }

387 388 389
        ret = av_parse_time(&t, tstr, 1);
        if (ret < 0) {
            av_log(log_ctx, AV_LOG_ERROR,
390
                   "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
            FAIL(AVERROR(EINVAL));
        }
        (*times)[i] = t;

        /* check on monotonicity */
        if (i && (*times)[i-1] > (*times)[i]) {
            av_log(log_ctx, AV_LOG_ERROR,
                   "Specified time %f is greater than the following time %f\n",
                   (float)((*times)[i])/1000000, (float)((*times)[i-1])/1000000);
            FAIL(AVERROR(EINVAL));
        }
    }

end:
    av_free(times_str1);
    return ret;
}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
                        const char *frames_str)
{
    char *p;
    int i, ret = 0;
    char *frames_str1 = av_strdup(frames_str);
    char *saveptr = NULL;

    if (!frames_str1)
        return AVERROR(ENOMEM);

#define FAIL(err) ret = err; goto end

    *nb_frames = 1;
    for (p = frames_str1; *p; p++)
        if (*p == ',')
            (*nb_frames)++;

    *frames = av_malloc(sizeof(**frames) * *nb_frames);
    if (!*frames) {
        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
        FAIL(AVERROR(ENOMEM));
    }

    p = frames_str1;
    for (i = 0; i < *nb_frames; i++) {
        long int f;
        char *tailptr;
        char *fstr = av_strtok(p, ",", &saveptr);

        p = NULL;
        if (!fstr) {
            av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
                   frames_str);
            FAIL(AVERROR(EINVAL));
        }
        f = strtol(fstr, &tailptr, 10);
        if (*tailptr || f <= 0 || f >= INT_MAX) {
            av_log(log_ctx, AV_LOG_ERROR,
                   "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
                   fstr);
            FAIL(AVERROR(EINVAL));
        }
        (*frames)[i] = f;

        /* check on monotonicity */
        if (i && (*frames)[i-1] > (*frames)[i]) {
            av_log(log_ctx, AV_LOG_ERROR,
                   "Specified frame %d is greater than the following frame %d\n",
                   (*frames)[i], (*frames)[i-1]);
            FAIL(AVERROR(EINVAL));
        }
    }

end:
    av_free(frames_str1);
    return ret;
}

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
static int open_null_ctx(AVIOContext **ctx)
{
    int buf_size = 32768;
    uint8_t *buf = av_malloc(buf_size);
    if (!buf)
        return AVERROR(ENOMEM);
    *ctx = avio_alloc_context(buf, buf_size, AVIO_FLAG_WRITE, NULL, NULL, NULL, NULL);
    if (!*ctx) {
        av_free(buf);
        return AVERROR(ENOMEM);
    }
    return 0;
}

static void close_null_ctx(AVIOContext *pb)
{
    av_free(pb->buffer);
    av_free(pb);
}

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
static int select_reference_stream(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
    int ret, i;

    seg->reference_stream_index = -1;
    if (!strcmp(seg->reference_stream_specifier, "auto")) {
        /* select first index of type with highest priority */
        int type_index_map[AVMEDIA_TYPE_NB];
        static const enum AVMediaType type_priority_list[] = {
            AVMEDIA_TYPE_VIDEO,
            AVMEDIA_TYPE_AUDIO,
            AVMEDIA_TYPE_SUBTITLE,
            AVMEDIA_TYPE_DATA,
            AVMEDIA_TYPE_ATTACHMENT
        };
        enum AVMediaType type;

        for (i = 0; i < AVMEDIA_TYPE_NB; i++)
            type_index_map[i] = -1;

        /* select first index for each type */
        for (i = 0; i < s->nb_streams; i++) {
            type = s->streams[i]->codec->codec_type;
512 513 514
            if ((unsigned)type < AVMEDIA_TYPE_NB && type_index_map[type] == -1
                /* ignore attached pictures/cover art streams */
                && !(s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC))
515 516 517 518 519 520 521 522 523 524 525 526 527
                type_index_map[type] = i;
        }

        for (i = 0; i < FF_ARRAY_ELEMS(type_priority_list); i++) {
            type = type_priority_list[i];
            if ((seg->reference_stream_index = type_index_map[type]) >= 0)
                break;
        }
    } else {
        for (i = 0; i < s->nb_streams; i++) {
            ret = avformat_match_stream_specifier(s, s->streams[i],
                                                  seg->reference_stream_specifier);
            if (ret < 0)
528
                return ret;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
            if (ret > 0) {
                seg->reference_stream_index = i;
                break;
            }
        }
    }

    if (seg->reference_stream_index < 0) {
        av_log(s, AV_LOG_ERROR, "Could not select stream matching identifier '%s'\n",
               seg->reference_stream_specifier);
        return AVERROR(EINVAL);
    }

    return 0;
}

545 546 547
static int seg_write_header(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
548
    AVFormatContext *oc = NULL;
549
    int ret;
550

551
    seg->segment_count = 0;
552 553
    if (!seg->write_header_trailer)
        seg->individual_header_trailer = 0;
554

555
    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
556
        av_log(s, AV_LOG_ERROR,
557 558
               "segment_time, segment_times, and segment_frames options "
               "are mutually exclusive, select just one of them\n");
559 560 561 562 563 564
        return AVERROR(EINVAL);
    }

    if (seg->times_str) {
        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
            return ret;
565 566 567
    } else if (seg->frames_str) {
        if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
            return ret;
568 569 570 571 572 573 574 575 576 577
    } else {
        /* set default value if not specified */
        if (!seg->time_str)
            seg->time_str = av_strdup("2");
        if ((ret = av_parse_time(&seg->time, seg->time_str, 1)) < 0) {
            av_log(s, AV_LOG_ERROR,
                   "Invalid time duration specification '%s' for segment_time option\n",
                   seg->time_str);
            return ret;
        }
578
    }
579

580 581
    if (seg->list) {
        if (seg->list_type == LIST_TYPE_UNDEFINED) {
582 583
            if      (av_match_ext(seg->list, "csv" )) seg->list_type = LIST_TYPE_CSV;
            else if (av_match_ext(seg->list, "ext" )) seg->list_type = LIST_TYPE_EXT;
584
            else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
585
            else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
586 587
            else                                      seg->list_type = LIST_TYPE_FLAT;
        }
588
        if ((ret = segment_list_open(s)) < 0)
589
            goto fail;
590
    }
591 592
    if (seg->list_type == LIST_TYPE_EXT)
        av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
593

594
    if ((ret = select_reference_stream(s)) < 0)
595 596 597 598
        goto fail;
    av_log(s, AV_LOG_VERBOSE, "Selected stream id:%d type:%s\n",
           seg->reference_stream_index,
           av_get_media_type_string(s->streams[seg->reference_stream_index]->codec->codec_type));
599

600
    seg->oformat = av_guess_format(seg->format, s->filename, NULL);
601

602
    if (!seg->oformat) {
603 604 605
        ret = AVERROR_MUXER_NOT_FOUND;
        goto fail;
    }
606
    if (seg->oformat->flags & AVFMT_NOFILE) {
607
        av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
608
               seg->oformat->name);
609 610 611 612
        ret = AVERROR(EINVAL);
        goto fail;
    }

613 614 615
    if ((ret = segment_mux_init(s)) < 0)
        goto fail;
    oc = seg->avf;
616

617
    if ((ret = set_segment_filename(s)) < 0)
618 619
        goto fail;

620 621
    if (seg->write_header_trailer) {
        if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
622 623
                              &s->interrupt_callback, NULL)) < 0) {
            av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", oc->filename);
624
            goto fail;
625
        }
626 627 628 629
    } else {
        if ((ret = open_null_ctx(&oc->pb)) < 0)
            goto fail;
    }
630 631 632 633 634

    if ((ret = avformat_write_header(oc, NULL)) < 0) {
        avio_close(oc->pb);
        goto fail;
    }
635
    seg->is_first_pkt = 1;
636

637 638 639
    if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
        s->avoid_negative_ts = 1;

640 641 642 643 644 645 646
    if (!seg->write_header_trailer) {
        close_null_ctx(oc->pb);
        if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                              &s->interrupt_callback, NULL)) < 0)
            goto fail;
    }

647 648 649
fail:
    if (ret) {
        if (seg->list)
650
            avio_close(seg->list_pb);
651 652
        if (seg->avf)
            avformat_free_context(seg->avf);
653 654 655 656 657 658 659 660
    }
    return ret;
}

static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
661
    AVStream *st = s->streams[pkt->stream_index];
662
    int64_t end_pts = INT64_MAX, offset;
663
    int start_frame = INT_MAX;
664 665
    int ret;

666
    if (seg->times) {
667 668
        end_pts = seg->segment_count < seg->nb_times ?
            seg->times[seg->segment_count] : INT64_MAX;
669 670
    } else if (seg->frames) {
        start_frame = seg->segment_count <= seg->nb_frames ?
671
            seg->frames[seg->segment_count] : INT_MAX;
672
    } else {
673
        end_pts = seg->time * (seg->segment_count+1);
674 675
    }

676 677 678 679 680
    av_dlog(s, "packet stream:%d pts:%s pts_time:%s is_key:%d frame:%d\n",
           pkt->stream_index, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
           pkt->flags & AV_PKT_FLAG_KEY,
           pkt->stream_index == seg->reference_stream_index ? seg->frame_count : -1);

681
    if (pkt->stream_index == seg->reference_stream_index &&
682 683 684 685 686
        pkt->flags & AV_PKT_FLAG_KEY &&
        (seg->frame_count >= start_frame ||
         (pkt->pts != AV_NOPTS_VALUE &&
          av_compare_ts(pkt->pts, st->time_base,
                        end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) {
687 688
        if ((ret = segment_end(s, seg->individual_header_trailer, 0)) < 0)
            goto fail;
689

690
        if ((ret = segment_start(s, seg->individual_header_trailer)) < 0)
691 692
            goto fail;

693 694
        oc = seg->avf;

695 696 697
        seg->cur_entry.index = seg->segment_idx;
        seg->cur_entry.start_time = (double)pkt->pts * av_q2d(st->time_base);
        seg->cur_entry.start_pts = av_rescale_q(pkt->pts, st->time_base, AV_TIME_BASE_Q);
698
    } else if (pkt->pts != AV_NOPTS_VALUE) {
699 700
        seg->cur_entry.end_time =
            FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
701 702
    }

703
    if (seg->is_first_pkt) {
704
        av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
705
               seg->avf->filename, pkt->stream_index,
706
               av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
707 708 709
        seg->is_first_pkt = 0;
    }

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
    av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
           pkt->stream_index,
           av_ts2timestr(seg->cur_entry.start_pts, &AV_TIME_BASE_Q),
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));

    /* compute new timestamps */
    offset = av_rescale_q(seg->initial_offset - (seg->reset_timestamps ? seg->cur_entry.start_pts : 0),
                          AV_TIME_BASE_Q, st->time_base);
    if (pkt->pts != AV_NOPTS_VALUE)
        pkt->pts += offset;
    if (pkt->dts != AV_NOPTS_VALUE)
        pkt->dts += offset;

    av_log(s, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
           av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
           av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
727

728
    ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
729 730

fail:
731 732 733
    if (pkt->stream_index == seg->reference_stream_index)
        seg->frame_count++;

734 735
    if (ret < 0) {
        if (seg->list)
736
            avio_close(seg->list_pb);
737 738 739 740 741 742 743 744 745 746
        avformat_free_context(oc);
    }

    return ret;
}

static int seg_write_trailer(struct AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
747 748
    SegmentListEntry *cur, *next;

749 750
    int ret;
    if (!seg->write_header_trailer) {
751
        if ((ret = segment_end(s, 0, 1)) < 0)
752
            goto fail;
753
        open_null_ctx(&oc->pb);
754
        ret = av_write_trailer(oc);
755 756
        close_null_ctx(oc->pb);
    } else {
757
        ret = segment_end(s, 1, 1);
758
    }
759
fail:
760
    if (seg->list)
761
        avio_close(seg->list_pb);
762 763

    av_opt_free(seg);
764
    av_freep(&seg->times);
765
    av_freep(&seg->frames);
766

767 768 769
    cur = seg->segment_list_entries;
    while (cur) {
        next = cur->next;
770
        av_free(cur->filename);
771 772 773 774
        av_free(cur);
        cur = next;
    }

775 776 777 778 779 780 781
    avformat_free_context(oc);
    return ret;
}

#define OFFSET(x) offsetof(SegmentContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
782
    { "reference_stream",  "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
783 784
    { "segment_format",    "set container format used for the segments", OFFSET(format),  AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
    { "segment_list",      "set the segment list filename",              OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
785 786 787 788 789

    { "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"},
    { "cache",             "allow list caching",                                    0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX,   E, "list_flags"},
    { "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"},

790
    { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT,  {.i64 = 0},     0, INT_MAX, E },
791
    { "segment_list_entry_prefix", "set prefix to prepend to each list entry filename", OFFSET(list_entry_prefix), AV_OPT_TYPE_STRING,  {.str = NULL}, 0, 0, E },
792

793
    { "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" },
794 795 796
    { "flat", "flat format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, E, "list_type" },
    { "csv",  "csv format",      0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_CSV  }, INT_MIN, INT_MAX, E, "list_type" },
    { "ext",  "extended format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_EXT  }, INT_MIN, INT_MAX, E, "list_type" },
797
    { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" },
798 799 800
    { "m3u8", "M3U8 format",     0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },
    { "hls", "Apple HTTP Live Streaming compatible", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, E, "list_type" },

801
    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
802
    { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E },
803
    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
804
    { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
805
    { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
806
    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
807

808
    { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
809
    { "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 },
810
    { "reset_timestamps", "reset timestamps at the begin of each segment", OFFSET(reset_timestamps), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E },
811
    { "initial_offset", "set initial timestamp offset", OFFSET(initial_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E },
812 813 814 815 816 817 818 819 820 821 822 823
    { NULL },
};

static const AVClass seg_class = {
    .class_name = "segment muxer",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

AVOutputFormat ff_segment_muxer = {
    .name           = "segment",
824
    .long_name      = NULL_IF_CONFIG_SMALL("segment"),
825
    .priv_data_size = sizeof(SegmentContext),
826
    .flags          = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
827 828 829 830 831
    .write_header   = seg_write_header,
    .write_packet   = seg_write_packet,
    .write_trailer  = seg_write_trailer,
    .priv_class     = &seg_class,
};
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849

static const AVClass sseg_class = {
    .class_name = "stream_segment muxer",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

AVOutputFormat ff_stream_segment_muxer = {
    .name           = "stream_segment,ssegment",
    .long_name      = NULL_IF_CONFIG_SMALL("streaming segment muxer"),
    .priv_data_size = sizeof(SegmentContext),
    .flags          = AVFMT_NOFILE,
    .write_header   = seg_write_header,
    .write_packet   = seg_write_packet,
    .write_trailer  = seg_write_trailer,
    .priv_class     = &sseg_class,
};