segment.c 28.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (c) 2011, Luca Barbato
 *
 * This file is part of Libav.
 *
 * Libav is free software; you can redistribute it and/or
 * 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.
 *
 * Libav is distributed in the hope that it will be useful,
 * 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
 * License along with Libav; if not, write to the Free Software
 * 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 47 48 49
    char filename[1024];
    struct SegmentListEntry *next;
} SegmentListEntry;

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

60 61 62
#define SEGMENT_LIST_FLAG_CACHE 1
#define SEGMENT_LIST_FLAG_LIVE  2

63 64
typedef struct {
    const AVClass *class;  /**< Class for private options. */
65 66 67
    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
68
    AVOutputFormat *oformat;
69
    AVFormatContext *avf;
70 71
    char *format;          ///< format to use for output segment files
    char *list;            ///< filename for the segment list file
72
    int   list_flags;      ///< flags affecting list generation
73
    int   list_size;       ///< number of entries for the segment list file
74
    ListType list_type;    ///< set the list type
75
    AVIOContext *list_pb;  ///< list file put-byte context
76 77
    char *time_str;        ///< segment duration specification string
    int64_t time;          ///< segment duration
78

79 80 81
    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
82 83 84 85 86 87

    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;

88 89
    char *time_delta_str;  ///< approximation value duration used for the segment times
    int64_t time_delta;
90
    int  individual_header_trailer; /**< Set by a private option. */
91
    int  write_header_trailer; /**< Set by a private option. */
92 93

    int reset_timestamps;  ///< reset timestamps at the begin of each segment
94 95 96
    char *reference_stream_specifier; ///< reference stream specifier
    int   reference_stream_index;

97
    SegmentListEntry cur_entry;
98 99 100
    SegmentListEntry *segment_list_entries;
    SegmentListEntry *segment_list_entries_end;

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

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

108
    if (needs_quoting)
109 110
        avio_w8(ctx, '"');

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

120
static int segment_mux_init(AVFormatContext *s)
121
{
122
    SegmentContext *seg = s->priv_data;
123 124 125 126 127 128 129 130 131
    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;
132
    av_dict_copy(&oc->metadata, s->metadata, 0);
133 134 135

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

138 139
        if (!(st = avformat_new_stream(oc, NULL)))
            return AVERROR(ENOMEM);
140 141 142 143 144 145 146 147 148 149
        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;
        }
150 151 152 153 154 155
        st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
    }

    return 0;
}

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

    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);
    }
168
    av_strlcpy(seg->cur_entry.filename, oc->filename, sizeof(seg->cur_entry.filename));
169 170 171
    return 0;
}

172
static int segment_start(AVFormatContext *s, int write_header)
173
{
174 175
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
176 177
    int err = 0;

178 179
    if (write_header) {
        avformat_free_context(oc);
180
        seg->avf = NULL;
181 182
        if ((err = segment_mux_init(s)) < 0)
            return err;
183
        oc = seg->avf;
184
    }
185

186
    seg->segment_idx++;
187 188
    if ((err = set_segment_filename(s)) < 0)
        return err;
189
    seg->segment_count++;
190 191 192 193 194

    if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                          &s->interrupt_callback, NULL)) < 0)
        return err;

195
    if (oc->oformat->priv_class && oc->priv_data)
196
        av_opt_set(oc->priv_data, "resend_headers", "1", 0); /* mpegts specific */
197

198 199 200 201
    if (write_header) {
        if ((err = avformat_write_header(oc, NULL)) < 0)
            return err;
    }
202

203
    seg->is_first_pkt = 1;
204 205 206
    return 0;
}

207 208 209
static int segment_list_open(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
210 211 212 213 214 215
    int ret;

    ret = avio_open2(&seg->list_pb, seg->list, AVIO_FLAG_WRITE,
                     &s->interrupt_callback, NULL);
    if (ret < 0)
        return ret;
216

217 218 219 220
    if (seg->list_type == LIST_TYPE_M3U8 && seg->segment_list_entries) {
        SegmentListEntry *entry;
        double max_duration = 0;

221
        avio_printf(seg->list_pb, "#EXTM3U\n");
222
        avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n");
223
        avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->segment_list_entries->index);
224 225
        avio_printf(seg->list_pb, "#EXT-X-ALLOW-CACHE:%s\n",
                    seg->list_flags & SEGMENT_LIST_FLAG_CACHE ? "YES" : "NO");
226 227 228 229

        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));
230 231
    } else if (seg->list_type == LIST_TYPE_FFCONCAT) {
        avio_printf(seg->list_pb, "ffconcat version 1.0\n");
232 233
    }

234
    return ret;
235 236
}

237 238
static void segment_list_print_entry(AVIOContext      *list_ioctx,
                                     ListType          list_type,
239 240
                                     const SegmentListEntry *list_entry,
                                     void *log_ctx)
241 242 243 244 245 246 247 248 249 250 251 252 253 254
{
    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;
255 256 257 258 259 260 261 262 263 264 265 266
    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;
    }
267 268 269 270 271
    default:
        av_assert0(!"Invalid list type");
    }
}

272
static int segment_end(AVFormatContext *s, int write_trailer, int is_last)
273
{
274 275
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
276 277
    int ret = 0;

278
    av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */
279
    if (write_trailer)
280
        ret = av_write_trailer(oc);
281

282
    if (ret < 0)
283
        av_log(s, AV_LOG_ERROR, "Failure occurred when ending segment '%s'\n",
284 285
               oc->filename);

286
    if (seg->list) {
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
        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;
                av_freep(&entry);
            }

309
            avio_close(seg->list_pb);
310
            if ((ret = segment_list_open(s)) < 0)
311
                goto end;
312
            for (entry = seg->segment_list_entries; entry; entry = entry->next)
313
                segment_list_print_entry(seg->list_pb, seg->list_type, entry, s);
314
            if (seg->list_type == LIST_TYPE_M3U8 && is_last)
315 316
                avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n");
        } else {
317
            segment_list_print_entry(seg->list_pb, seg->list_type, &seg->cur_entry, s);
318
        }
319
        avio_flush(seg->list_pb);
320 321 322
    }

end:
323 324 325 326 327
    avio_close(oc->pb);

    return ret;
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
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;

358 359 360 361 362 363
        if (!tstr || !tstr[0]) {
            av_log(log_ctx, AV_LOG_ERROR, "Empty time specification in times list %s\n",
                   times_str);
            FAIL(AVERROR(EINVAL));
        }

364 365 366
        ret = av_parse_time(&t, tstr, 1);
        if (ret < 0) {
            av_log(log_ctx, AV_LOG_ERROR,
367
                   "Invalid time duration specification '%s' in times list %s\n", tstr, times_str);
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
            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;
}

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 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
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;
}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
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);
}

465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
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;
489 490 491
            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))
492 493 494 495 496 497 498 499 500 501 502 503 504
                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)
505
                return ret;
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
            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;
}

522 523 524
static int seg_write_header(AVFormatContext *s)
{
    SegmentContext *seg = s->priv_data;
525
    AVFormatContext *oc = NULL;
526
    int ret;
527

528
    seg->segment_count = 0;
529 530
    if (!seg->write_header_trailer)
        seg->individual_header_trailer = 0;
531

532
    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
533
        av_log(s, AV_LOG_ERROR,
534 535
               "segment_time, segment_times, and segment_frames options "
               "are mutually exclusive, select just one of them\n");
536 537 538 539 540 541
        return AVERROR(EINVAL);
    }

    if (seg->times_str) {
        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
            return ret;
542 543 544
    } else if (seg->frames_str) {
        if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
            return ret;
545 546 547 548 549 550 551 552 553 554
    } 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;
        }
555
    }
556

557 558 559 560 561 562 563 564 565
    if (seg->time_delta_str) {
        if ((ret = av_parse_time(&seg->time_delta, seg->time_delta_str, 1)) < 0) {
            av_log(s, AV_LOG_ERROR,
                   "Invalid time duration specification '%s' for delta option\n",
                   seg->time_delta_str);
            return ret;
        }
    }

566 567
    if (seg->list) {
        if (seg->list_type == LIST_TYPE_UNDEFINED) {
568 569
            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;
570
            else if (av_match_ext(seg->list, "m3u8")) seg->list_type = LIST_TYPE_M3U8;
571
            else if (av_match_ext(seg->list, "ffcat,ffconcat")) seg->list_type = LIST_TYPE_FFCONCAT;
572 573
            else                                      seg->list_type = LIST_TYPE_FLAT;
        }
574
        if ((ret = segment_list_open(s)) < 0)
575
            goto fail;
576
    }
577 578
    if (seg->list_type == LIST_TYPE_EXT)
        av_log(s, AV_LOG_WARNING, "'ext' list type option is deprecated in favor of 'csv'\n");
579

580
    if ((ret = select_reference_stream(s)) < 0)
581 582 583 584
        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));
585

586
    seg->oformat = av_guess_format(seg->format, s->filename, NULL);
587

588
    if (!seg->oformat) {
589 590 591
        ret = AVERROR_MUXER_NOT_FOUND;
        goto fail;
    }
592
    if (seg->oformat->flags & AVFMT_NOFILE) {
593
        av_log(s, AV_LOG_ERROR, "format %s not supported.\n",
594
               seg->oformat->name);
595 596 597 598
        ret = AVERROR(EINVAL);
        goto fail;
    }

599 600 601
    if ((ret = segment_mux_init(s)) < 0)
        goto fail;
    oc = seg->avf;
602

603
    if ((ret = set_segment_filename(s)) < 0)
604
        goto fail;
605
    seg->segment_count++;
606

607 608 609 610 611 612 613 614
    if (seg->write_header_trailer) {
        if ((ret = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE,
                              &s->interrupt_callback, NULL)) < 0)
            goto fail;
    } else {
        if ((ret = open_null_ctx(&oc->pb)) < 0)
            goto fail;
    }
615 616 617 618 619

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

622 623 624
    if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0)
        s->avoid_negative_ts = 1;

625 626 627 628 629 630 631
    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;
    }

632 633 634
fail:
    if (ret) {
        if (seg->list)
635
            avio_close(seg->list_pb);
636 637
        if (seg->avf)
            avformat_free_context(seg->avf);
638 639 640 641 642 643 644 645
    }
    return ret;
}

static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
{
    SegmentContext *seg = s->priv_data;
    AVFormatContext *oc = seg->avf;
646
    AVStream *st = s->streams[pkt->stream_index];
647 648
    int64_t end_pts = INT64_MAX;
    int start_frame = INT_MAX;
649 650
    int ret;

651
    if (seg->times) {
652 653
        end_pts = seg->segment_count <= seg->nb_times ?
            seg->times[seg->segment_count-1] : INT64_MAX;
654 655 656
    } else if (seg->frames) {
        start_frame = seg->segment_count <= seg->nb_frames ?
            seg->frames[seg->segment_count-1] : INT_MAX;
657
    } else {
658
        end_pts = seg->time * seg->segment_count;
659 660
    }

661 662 663 664 665
    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);

666
    if (pkt->stream_index == seg->reference_stream_index &&
667 668 669 670 671
        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))) {
672
        ret = segment_end(s, seg->individual_header_trailer, 0);
673 674

        if (!ret)
675
            ret = segment_start(s, seg->individual_header_trailer);
676 677 678 679

        if (ret)
            goto fail;

680 681
        oc = seg->avf;

682 683 684
        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);
685
    } else if (pkt->pts != AV_NOPTS_VALUE) {
686 687
        seg->cur_entry.end_time =
            FFMAX(seg->cur_entry.end_time, (double)(pkt->pts + pkt->duration) * av_q2d(st->time_base));
688 689
    }

690
    if (seg->is_first_pkt) {
691
        av_log(s, AV_LOG_DEBUG, "segment:'%s' starts with packet stream:%d pts:%s pts_time:%s frame:%d\n",
692
               seg->avf->filename, pkt->stream_index,
693
               av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base), seg->frame_count);
694 695 696
        seg->is_first_pkt = 0;
    }

697
    if (seg->reset_timestamps) {
698
        av_log(s, AV_LOG_DEBUG, "stream:%d start_pts_time:%s pts:%s pts_time:%s dts:%s dts_time:%s",
699 700 701 702
               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));
703 704 705

        /* compute new timestamps */
        if (pkt->pts != AV_NOPTS_VALUE)
706
            pkt->pts -= av_rescale_q(seg->cur_entry.start_pts, AV_TIME_BASE_Q, st->time_base);
707
        if (pkt->dts != AV_NOPTS_VALUE)
708
            pkt->dts -= av_rescale_q(seg->cur_entry.start_pts, AV_TIME_BASE_Q, st->time_base);
709

710 711 712
        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));
713 714
    }

715
    ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
716 717

fail:
718 719 720
    if (pkt->stream_index == seg->reference_stream_index)
        seg->frame_count++;

721 722
    if (ret < 0) {
        if (seg->list)
723
            avio_close(seg->list_pb);
724 725 726 727 728 729 730 731 732 733
        avformat_free_context(oc);
    }

    return ret;
}

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

736 737
    int ret;
    if (!seg->write_header_trailer) {
738
        if ((ret = segment_end(s, 0, 1)) < 0)
739
            goto fail;
740
        open_null_ctx(&oc->pb);
741
        ret = av_write_trailer(oc);
742 743
        close_null_ctx(oc->pb);
    } else {
744
        ret = segment_end(s, 1, 1);
745
    }
746
fail:
747
    if (seg->list)
748
        avio_close(seg->list_pb);
749 750

    av_opt_free(seg);
751
    av_freep(&seg->times);
752
    av_freep(&seg->frames);
753

754 755 756 757 758 759 760
    cur = seg->segment_list_entries;
    while (cur) {
        next = cur->next;
        av_free(cur);
        cur = next;
    }

761 762 763 764 765 766 767
    avformat_free_context(oc);
    return ret;
}

#define OFFSET(x) offsetof(SegmentContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
768
    { "reference_stream",  "set reference stream", OFFSET(reference_stream_specifier), AV_OPT_TYPE_STRING, {.str = "auto"}, CHAR_MIN, CHAR_MAX, E },
769 770
    { "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 },
771 772 773 774 775

    { "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"},

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

778
    { "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" },
779 780 781
    { "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" },
782
    { "ffconcat", "ffconcat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FFCONCAT }, INT_MIN, INT_MAX, E, "list_type" },
783 784 785
    { "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" },

786
    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
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 },
788
    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
789
    { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  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 797 798 799 800 801 802 803 804 805 806 807
    { 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",
808
    .long_name      = NULL_IF_CONFIG_SMALL("segment"),
809
    .priv_data_size = sizeof(SegmentContext),
810
    .flags          = AVFMT_NOFILE|AVFMT_GLOBALHEADER,
811 812 813 814 815
    .write_header   = seg_write_header,
    .write_packet   = seg_write_packet,
    .write_trailer  = seg_write_trailer,
    .priv_class     = &seg_class,
};
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833

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,
};