hlsenc.c 39.3 KB
Newer Older
1 2 3 4
/*
 * Apple HTTP Live Streaming segmenter
 * Copyright (c) 2012, Luca Barbato
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8 9 10 11
 * 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.
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * 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
18
 * License along with FFmpeg; if not, write to the Free Software
19 20 21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

22
#include "config.h"
23
#include <float.h>
24
#include <stdint.h>
25 26 27
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
28

29
#include "libavutil/avassert.h"
30 31 32 33 34
#include "libavutil/mathematics.h"
#include "libavutil/parseutils.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/log.h"
35
#include "libavutil/time_internal.h"
36 37

#include "avformat.h"
38
#include "avio_internal.h"
39
#include "internal.h"
40
#include "os_support.h"
41

42 43 44
#define KEYSIZE 16
#define LINE_BUFFER_SIZE 1024

45 46
typedef struct HLSSegment {
    char filename[1024];
47
    char sub_filename[1024];
48
    double duration; /* in seconds */
49 50
    int64_t pos;
    int64_t size;
51

52 53 54
    char key_uri[LINE_BUFFER_SIZE + 1];
    char iv_string[KEYSIZE*2 + 1];

55 56
    struct HLSSegment *next;
} HLSSegment;
57

58 59 60
typedef enum HLSFlags {
    // Generate a single media file and use byte ranges in the playlist.
    HLS_SINGLE_FILE = (1 << 0),
61
    HLS_DELETE_SEGMENTS = (1 << 1),
62
    HLS_ROUND_DURATIONS = (1 << 2),
63
    HLS_DISCONT_START = (1 << 3),
64
    HLS_OMIT_ENDLIST = (1 << 4),
65
    HLS_SPLIT_BY_TIME = (1 << 5),
66
    HLS_APPEND_LIST = (1 << 6),
67
    HLS_PROGRAM_DATE_TIME = (1 << 7),
68 69
} HLSFlags;

70 71 72 73 74 75 76
typedef enum {
    PLAYLIST_TYPE_NONE,
    PLAYLIST_TYPE_EVENT,
    PLAYLIST_TYPE_VOD,
    PLAYLIST_TYPE_NB,
} PlaylistType;

77 78
typedef struct HLSContext {
    const AVClass *class;  // Class for private options.
79
    unsigned number;
80
    int64_t sequence;
81
    int64_t start_sequence;
82
    AVOutputFormat *oformat;
83
    AVOutputFormat *vtt_oformat;
84

85
    AVFormatContext *avf;
86
    AVFormatContext *vtt_avf;
87

88
    float time;            // Set by a private option.
89
    float init_time;       // Set by a private option.
90
    int max_nb_segments;   // Set by a private option.
91
    int  wrap;             // Set by a private option.
92
    uint32_t flags;        // enum HLSFlags
93
    uint32_t pl_type;      // enum PlaylistType
94
    char *segment_filename;
95

96
    int use_localtime;      ///< flag to expand filename with localtime
97
    int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename
98
    int allowcache;
99 100
    int64_t recording_time;
    int has_video;
101
    int has_subtitle;
102 103
    int64_t start_pts;
    int64_t end_pts;
104
    double duration;      // last segment duration computed so far, in seconds
105 106
    int64_t start_pos;    // last segment starting position
    int64_t size;         // last segment size
107
    int64_t max_seg_size; // every segment file max size
108
    int nb_entries;
109
    int discontinuity_set;
110

111 112
    HLSSegment *segments;
    HLSSegment *last_segment;
113
    HLSSegment *old_segments;
114

115
    char *basename;
116 117
    char *vtt_basename;
    char *vtt_m3u8_name;
118
    char *baseurl;
Steven Liu's avatar
Steven Liu committed
119
    char *format_options_str;
120 121
    char *vtt_format_options_str;
    char *subtitle_filename;
Steven Liu's avatar
Steven Liu committed
122
    AVDictionary *format_options;
123 124 125 126 127 128

    char *key_info_file;
    char key_file[LINE_BUFFER_SIZE + 1];
    char key_uri[LINE_BUFFER_SIZE + 1];
    char key_string[KEYSIZE*2 + 1];
    char iv_string[KEYSIZE*2 + 1];
129 130
    AVDictionary *vtt_format_options;

131 132
    char *method;

133
    double initial_prog_date_time;
134 135
} HLSContext;

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
static int mkdir_p(const char *path) {
    int ret = 0;
    char *temp = av_strdup(path);
    char *pos = temp;
    char tmp_ch = '\0';

    if (!path || !temp) {
        return -1;
    }

    if (!strncmp(temp, "/", 1) || !strncmp(temp, "\\", 1)) {
        pos++;
    } else if (!strncmp(temp, "./", 2) || !strncmp(temp, ".\\", 2)) {
        pos += 2;
    }

    for ( ; *pos != '\0'; ++pos) {
        if (*pos == '/' || *pos == '\\') {
            tmp_ch = *pos;
            *pos = '\0';
            ret = mkdir(temp, 0755);
            *pos = tmp_ch;
        }
    }

    if ((*(pos - 1) != '/') || (*(pos - 1) != '\\')) {
        ret = mkdir(temp, 0755);
    }

    av_free(temp);
    return ret;
}

169 170 171 172
static int hls_delete_old_segments(HLSContext *hls) {

    HLSSegment *segment, *previous_segment = NULL;
    float playlist_duration = 0.0f;
173
    int ret = 0, path_size, sub_path_size;
174 175
    char *dirname = NULL, *p, *sub_path;
    char *path = NULL;
176 177 178 179 180 181 182 183 184 185

    segment = hls->segments;
    while (segment) {
        playlist_duration += segment->duration;
        segment = segment->next;
    }

    segment = hls->old_segments;
    while (segment) {
        playlist_duration -= segment->duration;
186
        hls->initial_prog_date_time += segment->duration;
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
        previous_segment = segment;
        segment = previous_segment->next;
        if (playlist_duration <= -previous_segment->duration) {
            previous_segment->next = NULL;
            break;
        }
    }

    if (segment) {
        if (hls->segment_filename) {
            dirname = av_strdup(hls->segment_filename);
        } else {
            dirname = av_strdup(hls->avf->filename);
        }
        if (!dirname) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        p = (char *)av_basename(dirname);
        *p = '\0';
    }

    while (segment) {
        av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n",
                                  segment->filename);
        path_size = strlen(dirname) + strlen(segment->filename) + 1;
        path = av_malloc(path_size);
        if (!path) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
218

219 220 221 222 223 224
        av_strlcpy(path, dirname, path_size);
        av_strlcat(path, segment->filename, path_size);
        if (unlink(path) < 0) {
            av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
                                     path, strerror(errno));
        }
225

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
        if (segment->sub_filename[0] != '\0') {
            sub_path_size = strlen(dirname) + strlen(segment->sub_filename) + 1;
            sub_path = av_malloc(sub_path_size);
            if (!sub_path) {
                ret = AVERROR(ENOMEM);
                goto fail;
            }

            av_strlcpy(sub_path, dirname, sub_path_size);
            av_strlcat(sub_path, segment->sub_filename, sub_path_size);
            if (unlink(sub_path) < 0) {
                av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n",
                                         sub_path, strerror(errno));
            }
            av_free(sub_path);
241
        }
242
        av_freep(&path);
243 244 245 246 247 248
        previous_segment = segment;
        segment = previous_segment->next;
        av_free(previous_segment);
    }

fail:
249
    av_free(path);
250 251 252 253 254
    av_free(dirname);

    return ret;
}

255 256 257 258 259 260 261
static int hls_encryption_start(AVFormatContext *s)
{
    HLSContext *hls = s->priv_data;
    int ret;
    AVIOContext *pb;
    uint8_t key[KEYSIZE];

262
    if ((ret = s->io_open(s, &pb, hls->key_info_file, AVIO_FLAG_READ, NULL)) < 0) {
263 264 265 266 267 268 269 270 271 272 273 274 275 276
        av_log(hls, AV_LOG_ERROR,
                "error opening key info file %s\n", hls->key_info_file);
        return ret;
    }

    ff_get_line(pb, hls->key_uri, sizeof(hls->key_uri));
    hls->key_uri[strcspn(hls->key_uri, "\r\n")] = '\0';

    ff_get_line(pb, hls->key_file, sizeof(hls->key_file));
    hls->key_file[strcspn(hls->key_file, "\r\n")] = '\0';

    ff_get_line(pb, hls->iv_string, sizeof(hls->iv_string));
    hls->iv_string[strcspn(hls->iv_string, "\r\n")] = '\0';

277
    ff_format_io_close(s, &pb);
278 279 280 281 282 283 284 285 286 287 288

    if (!*hls->key_uri) {
        av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n");
        return AVERROR(EINVAL);
    }

    if (!*hls->key_file) {
        av_log(hls, AV_LOG_ERROR, "no key file specified in key info file\n");
        return AVERROR(EINVAL);
    }

289
    if ((ret = s->io_open(s, &pb, hls->key_file, AVIO_FLAG_READ, NULL)) < 0) {
290 291 292 293 294
        av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", hls->key_file);
        return ret;
    }

    ret = avio_read(pb, key, sizeof(key));
295
    ff_format_io_close(s, &pb);
296 297 298 299 300 301 302 303 304 305 306
    if (ret != sizeof(key)) {
        av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", hls->key_file);
        if (ret >= 0 || ret == AVERROR_EOF)
            ret = AVERROR(EINVAL);
        return ret;
    }
    ff_data_to_hex(hls->key_string, key, sizeof(key), 0);

    return 0;
}

307 308 309 310 311 312 313 314
static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
{
    int len = ff_get_line(s, buf, maxlen);
    while (len > 0 && av_isspace(buf[len - 1]))
        buf[--len] = '\0';
    return len;
}

315 316 317 318
static int hls_mux_init(AVFormatContext *s)
{
    HLSContext *hls = s->priv_data;
    AVFormatContext *oc;
319
    AVFormatContext *vtt_oc = NULL;
320
    int i, ret;
321

322 323 324 325
    ret = avformat_alloc_output_context2(&hls->avf, hls->oformat, NULL, NULL);
    if (ret < 0)
        return ret;
    oc = hls->avf;
326 327 328

    oc->oformat            = hls->oformat;
    oc->interrupt_callback = s->interrupt_callback;
329
    oc->max_delay          = s->max_delay;
330 331 332
    oc->opaque             = s->opaque;
    oc->io_open            = s->io_open;
    oc->io_close           = s->io_close;
333
    av_dict_copy(&oc->metadata, s->metadata, 0);
334

335 336 337 338 339 340 341 342 343
    if(hls->vtt_oformat) {
        ret = avformat_alloc_output_context2(&hls->vtt_avf, hls->vtt_oformat, NULL, NULL);
        if (ret < 0)
            return ret;
        vtt_oc          = hls->vtt_avf;
        vtt_oc->oformat = hls->vtt_oformat;
        av_dict_copy(&vtt_oc->metadata, s->metadata, 0);
    }

344 345
    for (i = 0; i < s->nb_streams; i++) {
        AVStream *st;
346
        AVFormatContext *loc;
347
        if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
348 349 350 351 352
            loc = vtt_oc;
        else
            loc = oc;

        if (!(st = avformat_new_stream(loc, NULL)))
353
            return AVERROR(ENOMEM);
354
        avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
355
        st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
356
        st->time_base = s->streams[i]->time_base;
357
    }
358
    hls->start_pos = 0;
359 360 361 362

    return 0;
}

363
/* Create a new segment and append it to the segment list */
364 365
static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, double duration,
                              int64_t pos, int64_t size)
366
{
367
    HLSSegment *en = av_malloc(sizeof(*en));
368
    const char  *filename;
369
    int ret;
370 371 372 373

    if (!en)
        return AVERROR(ENOMEM);

374 375 376
    filename = av_basename(hls->avf->filename);

    if (hls->use_localtime_mkdir) {
377
        filename = hls->avf->filename;
378 379
    }
    av_strlcpy(en->filename, filename, sizeof(en->filename));
380

381 382
    if(hls->has_subtitle)
        av_strlcpy(en->sub_filename, av_basename(hls->vtt_avf->filename), sizeof(en->sub_filename));
383 384
    else
        en->sub_filename[0] = '\0';
385

386
    en->duration = duration;
387 388
    en->pos      = pos;
    en->size     = size;
389 390
    en->next     = NULL;

391 392 393 394 395
    if (hls->key_info_file) {
        av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri));
        av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string));
    }

396 397
    if (!hls->segments)
        hls->segments = en;
398
    else
399
        hls->last_segment->next = en;
400

401
    hls->last_segment = en;
402

403 404 405 406
    // EVENT or VOD playlists imply sliding window cannot be used
    if (hls->pl_type != PLAYLIST_TYPE_NONE)
        hls->max_nb_segments = 0;

407 408 409
    if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) {
        en = hls->segments;
        hls->segments = en->next;
410 411 412 413 414 415 416 417
        if (en && hls->flags & HLS_DELETE_SEGMENTS &&
                !(hls->flags & HLS_SINGLE_FILE || hls->wrap)) {
            en->next = hls->old_segments;
            hls->old_segments = en;
            if ((ret = hls_delete_old_segments(hls)) < 0)
                return ret;
        } else
            av_free(en);
418 419
    } else
        hls->nb_entries++;
420

421 422 423
    if (hls->max_seg_size > 0) {
        return 0;
    }
424 425
    hls->sequence++;

426 427 428
    return 0;
}

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 468 469 470 471 472 473 474 475 476
static int parse_playlist(AVFormatContext *s, const char *url)
{
    HLSContext *hls = s->priv_data;
    AVIOContext *in;
    int ret = 0, is_segment = 0;
    int64_t new_start_pos;
    char line[1024];
    const char *ptr;

    if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
                                   &s->interrupt_callback, NULL,
                                   s->protocol_whitelist, s->protocol_blacklist)) < 0)
        return ret;

    read_chomp_line(in, line, sizeof(line));
    if (strcmp(line, "#EXTM3U")) {
        ret = AVERROR_INVALIDDATA;
        goto fail;
    }

    while (!avio_feof(in)) {
        read_chomp_line(in, line, sizeof(line));
        if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
            hls->sequence = atoi(ptr);
        } else if (av_strstart(line, "#EXTINF:", &ptr)) {
            is_segment = 1;
            hls->duration = atof(ptr);
        } else if (av_strstart(line, "#", NULL)) {
            continue;
        } else if (line[0]) {
            if (is_segment) {
                is_segment = 0;
                new_start_pos = avio_tell(hls->avf->pb);
                hls->size = new_start_pos - hls->start_pos;
                av_strlcpy(hls->avf->filename, line, sizeof(line));
                ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
                if (ret < 0)
                    goto fail;
                hls->start_pos = new_start_pos;
            }
        }
    }

fail:
    avio_close(in);
    return ret;
}

477
static void hls_free_segments(HLSSegment *p)
478
{
479
    HLSSegment *en;
480 481 482 483 484 485 486 487

    while(p) {
        en = p;
        p = p->next;
        av_free(en);
    }
}

488 489 490 491 492 493
static void set_http_options(AVDictionary **options, HLSContext *c)
{
    if (c->method)
        av_dict_set(options, "method", c->method, 0);
}

494 495 496
static int hls_window(AVFormatContext *s, int last)
{
    HLSContext *hls = s->priv_data;
497
    HLSSegment *en;
498
    int target_duration = 0;
499
    int ret = 0;
500
    AVIOContext *out = NULL;
501
    AVIOContext *sub_out = NULL;
502
    char temp_filename[1024];
503
    int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - hls->nb_entries);
504
    int version = 3;
505 506
    const char *proto = avio_find_protocol_name(s->filename);
    int use_rename = proto && !strcmp(proto, "file");
507
    static unsigned warned_non_file;
508 509
    char *key_uri = NULL;
    char *iv_string = NULL;
510
    AVDictionary *options = NULL;
511
    double prog_date_time = hls->initial_prog_date_time;
512 513 514 515 516 517
    int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);

    if (byterange_mode) {
        version = 4;
        sequence = 0;
    }
518 519

    if (!use_rename && !warned_non_file++)
Moritz Barsnick's avatar
Moritz Barsnick committed
520
        av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
521

522
    set_http_options(&options, hls);
523
    snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
524
    if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0)
525 526
        goto fail;

527
    for (en = hls->segments; en; en = en->next) {
528
        if (target_duration < en->duration)
529
            target_duration = ceil(en->duration);
530 531
    }

532
    hls->discontinuity_set = 0;
533 534
    avio_printf(out, "#EXTM3U\n");
    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
535
    if (hls->allowcache == 0 || hls->allowcache == 1) {
536
        avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
537
    }
538 539
    avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
    avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
540 541 542 543 544
    if (hls->pl_type == PLAYLIST_TYPE_EVENT) {
        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
    } else if (hls->pl_type == PLAYLIST_TYPE_VOD) {
        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
    }
545

546
    av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
547
           sequence);
548 549 550 551
    if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && hls->discontinuity_set==0 ){
        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
        hls->discontinuity_set = 1;
    }
552
    for (en = hls->segments; en; en = en->next) {
553 554 555 556 557 558 559 560 561 562
        if (hls->key_info_file && (!key_uri || strcmp(en->key_uri, key_uri) ||
                                    av_strcasecmp(en->iv_string, iv_string))) {
            avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
            if (*en->iv_string)
                avio_printf(out, ",IV=0x%s", en->iv_string);
            avio_printf(out, "\n");
            key_uri = en->key_uri;
            iv_string = en->iv_string;
        }

563
        if (hls->flags & HLS_ROUND_DURATIONS)
564
            avio_printf(out, "#EXTINF:%ld,\n",  lrint(en->duration));
565 566
        else
            avio_printf(out, "#EXTINF:%f,\n", en->duration);
567
        if (byterange_mode)
568
             avio_printf(out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
569
                         en->size, en->pos);
570
        if (hls->flags & HLS_PROGRAM_DATE_TIME) {
571
            time_t tt, wrongsecs;
572 573 574 575 576 577
            int milli;
            struct tm *tm, tmpbuf;
            char buf0[128], buf1[128];
            tt = (int64_t)prog_date_time;
            milli = av_clip(lrint(1000*(prog_date_time - tt)), 0, 999);
            tm = localtime_r(&tt, &tmpbuf);
578
            strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
579 580 581 582 583 584 585 586 587 588 589 590
            if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
                int tz_min, dst = tm->tm_isdst;
                tm = gmtime_r(&tt, &tmpbuf);
                tm->tm_isdst = dst;
                wrongsecs = mktime(tm);
                tz_min = (abs(wrongsecs - tt) + 30) / 60;
                snprintf(buf1, sizeof(buf1),
                         "%c%02d%02d",
                         wrongsecs <= tt ? '+' : '-',
                         tz_min / 60,
                         tz_min % 60);
            }
591 592 593
            avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
            prog_date_time += en->duration;
        }
594
        if (hls->baseurl)
595 596
            avio_printf(out, "%s", hls->baseurl);
        avio_printf(out, "%s\n", en->filename);
597 598
    }

599
    if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
600
        avio_printf(out, "#EXT-X-ENDLIST\n");
601

602
    if( hls->vtt_m3u8_name ) {
603
        if ((ret = s->io_open(s, &sub_out, hls->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
604 605 606 607 608 609 610 611 612 613 614 615 616 617
            goto fail;
        avio_printf(sub_out, "#EXTM3U\n");
        avio_printf(sub_out, "#EXT-X-VERSION:%d\n", version);
        if (hls->allowcache == 0 || hls->allowcache == 1) {
            avio_printf(sub_out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
        }
        avio_printf(sub_out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
        avio_printf(sub_out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);

        av_log(s, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n",
               sequence);

        for (en = hls->segments; en; en = en->next) {
            avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
618
            if (byterange_mode)
619 620 621 622 623 624 625 626 627 628 629 630
                 avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
                         en->size, en->pos);
            if (hls->baseurl)
                avio_printf(sub_out, "%s", hls->baseurl);
            avio_printf(sub_out, "%s\n", en->sub_filename);
        }

        if (last)
            avio_printf(sub_out, "#EXT-X-ENDLIST\n");

    }

631
fail:
632
    av_dict_free(&options);
633
    ff_format_io_close(s, &out);
634
    ff_format_io_close(s, &sub_out);
635
    if (ret >= 0 && use_rename)
636
        ff_rename(temp_filename, s->filename, s);
637 638 639 640 641 642 643
    return ret;
}

static int hls_start(AVFormatContext *s)
{
    HLSContext *c = s->priv_data;
    AVFormatContext *oc = c->avf;
644
    AVFormatContext *vtt_oc = c->vtt_avf;
645 646
    AVDictionary *options = NULL;
    char *filename, iv_string[KEYSIZE*2 + 1];
647 648
    int err = 0;

649
    if (c->flags & HLS_SINGLE_FILE) {
650 651
        av_strlcpy(oc->filename, c->basename,
                   sizeof(oc->filename));
652 653 654
        if (c->vtt_basename)
            av_strlcpy(vtt_oc->filename, c->vtt_basename,
                  sizeof(vtt_oc->filename));
655 656 657
    } else if (c->max_seg_size > 0) {
        if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
            c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
658
            AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
659 660 661
                av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -use_localtime 1 with it\n", c->basename);
                return AVERROR(EINVAL);
        }
662
    } else {
663 664 665 666 667 668 669 670 671
        if (c->use_localtime) {
            time_t now0;
            struct tm *tm, tmpbuf;
            time(&now0);
            tm = localtime_r(&now0, &tmpbuf);
            if (!strftime(oc->filename, sizeof(oc->filename), c->basename, tm)) {
                av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
                return AVERROR(EINVAL);
            }
672 673 674 675 676 677 678 679

            if (c->use_localtime_mkdir) {
                const char *dir;
                char *fn_copy = av_strdup(oc->filename);
                if (!fn_copy) {
                    return AVERROR(ENOMEM);
                }
                dir = av_dirname(fn_copy);
680
                if (mkdir_p(dir) == -1 && errno != EEXIST) {
681 682 683 684 685 686
                    av_log(oc, AV_LOG_ERROR, "Could not create directory %s with use_localtime_mkdir\n", dir);
                    av_free(fn_copy);
                    return AVERROR(errno);
                }
                av_free(fn_copy);
            }
687 688
        } else if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
                                  c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
689
                                  AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
Moritz Barsnick's avatar
Moritz Barsnick committed
690
            av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", c->basename);
691 692
            return AVERROR(EINVAL);
        }
693
        if( c->vtt_basename) {
694 695
            if (av_get_frame_filename2(vtt_oc->filename, sizeof(vtt_oc->filename),
                              c->vtt_basename, c->wrap ? c->sequence % c->wrap : c->sequence,
696
                              AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
697 698 699 700 701
                av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
                return AVERROR(EINVAL);
            }
       }
    }
702
    c->number++;
703

704 705
    set_http_options(&options, c);

706 707
    if (c->key_info_file) {
        if ((err = hls_encryption_start(s)) < 0)
708
            goto fail;
709 710
        if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0))
                < 0)
711
            goto fail;
712 713 714 715
        err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string));
        if (!err)
            snprintf(iv_string, sizeof(iv_string), "%032"PRIx64, c->sequence);
        if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0)
716
           goto fail;
717 718 719

        filename = av_asprintf("crypto:%s", oc->filename);
        if (!filename) {
720 721
            err = AVERROR(ENOMEM);
            goto fail;
722
        }
723
        err = s->io_open(s, &oc->pb, filename, AVIO_FLAG_WRITE, &options);
724 725 726 727 728
        av_free(filename);
        av_dict_free(&options);
        if (err < 0)
            return err;
    } else
729
        if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
730
            goto fail;
731
    if (c->vtt_basename) {
732
        set_http_options(&options, c);
733
        if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0)
734
            goto fail;
735
    }
736
    av_dict_free(&options);
737

738 739 740 741 742 743
    /* We only require one PAT/PMT per segment. */
    if (oc->oformat->priv_class && oc->priv_data) {
        char period[21];

        snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1);

744
        av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0);
745 746 747
        av_opt_set(oc->priv_data, "sdt_period", period, 0);
        av_opt_set(oc->priv_data, "pat_period", period, 0);
    }
748

749 750 751 752 753
    if (c->vtt_basename) {
        err = avformat_write_header(vtt_oc,NULL);
        if (err < 0)
            return err;
    }
754

755
    return 0;
756 757 758 759
fail:
    av_dict_free(&options);

    return err;
760 761 762 763 764 765 766 767
}

static int hls_write_header(AVFormatContext *s)
{
    HLSContext *hls = s->priv_data;
    int ret, i;
    char *p;
    const char *pattern = "%d.ts";
768
    const char *pattern_localtime_fmt = "-%s.ts";
769
    const char *vtt_pattern = "%d.vtt";
Steven Liu's avatar
Steven Liu committed
770
    AVDictionary *options = NULL;
771
    int basename_size;
772
    int vtt_basename_size;
773

774
    hls->sequence       = hls->start_sequence;
775
    hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
776 777
    hls->start_pts      = AV_NOPTS_VALUE;

778 779 780 781 782 783
    if (hls->flags & HLS_PROGRAM_DATE_TIME) {
        time_t now0;
        time(&now0);
        hls->initial_prog_date_time = now0;
    }

Steven Liu's avatar
Steven Liu committed
784 785 786 787 788 789 790 791
    if (hls->format_options_str) {
        ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0);
        if (ret < 0) {
            av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", hls->format_options_str);
            goto fail;
        }
    }

792
    for (i = 0; i < s->nb_streams; i++) {
793
        hls->has_video +=
794
            s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
795
        hls->has_subtitle +=
796
            s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
797
    }
798 799 800 801 802 803 804 805 806 807 808 809 810

    if (hls->has_video > 1)
        av_log(s, AV_LOG_WARNING,
               "More than a single video stream present, "
               "expect issues decoding it.\n");

    hls->oformat = av_guess_format("mpegts", NULL, NULL);

    if (!hls->oformat) {
        ret = AVERROR_MUXER_NOT_FOUND;
        goto fail;
    }

811 812 813 814 815 816 817 818
    if(hls->has_subtitle) {
        hls->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
        if (!hls->oformat) {
            ret = AVERROR_MUXER_NOT_FOUND;
            goto fail;
        }
    }

819 820 821 822 823 824 825 826 827
    if (hls->segment_filename) {
        hls->basename = av_strdup(hls->segment_filename);
        if (!hls->basename) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    } else {
        if (hls->flags & HLS_SINGLE_FILE)
            pattern = ".ts";
828

829 830 831 832 833
        if (hls->use_localtime) {
            basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
        } else {
            basename_size = strlen(s->filename) + strlen(pattern) + 1;
        }
834 835 836 837 838
        hls->basename = av_malloc(basename_size);
        if (!hls->basename) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
839

840
        av_strlcpy(hls->basename, s->filename, basename_size);
841

842 843 844
        p = strrchr(hls->basename, '.');
        if (p)
            *p = '\0';
845 846 847 848 849
        if (hls->use_localtime) {
            av_strlcat(hls->basename, pattern_localtime_fmt, basename_size);
        } else {
            av_strlcat(hls->basename, pattern, basename_size);
        }
850
    }
851

852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
    if(hls->has_subtitle) {

        if (hls->flags & HLS_SINGLE_FILE)
            vtt_pattern = ".vtt";
        vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
        hls->vtt_basename = av_malloc(vtt_basename_size);
        if (!hls->vtt_basename) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        hls->vtt_m3u8_name = av_malloc(vtt_basename_size);
        if (!hls->vtt_m3u8_name ) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        av_strlcpy(hls->vtt_basename, s->filename, vtt_basename_size);
        p = strrchr(hls->vtt_basename, '.');
        if (p)
            *p = '\0';

        if( hls->subtitle_filename ) {
            strcpy(hls->vtt_m3u8_name, hls->subtitle_filename);
        } else {
            strcpy(hls->vtt_m3u8_name, hls->vtt_basename);
            av_strlcat(hls->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
        }
        av_strlcat(hls->vtt_basename, vtt_pattern, vtt_basename_size);
    }

881 882 883
    if ((ret = hls_mux_init(s)) < 0)
        goto fail;

884 885
    if (hls->flags & HLS_APPEND_LIST) {
        parse_playlist(s, s->filename);
886 887 888 889 890 891
        if (hls->init_time > 0) {
            av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
                   " hls_init_time value will have no effect\n");
            hls->init_time = 0;
            hls->recording_time = hls->time * AV_TIME_BASE;
        }
892 893
    }

894 895 896
    if ((ret = hls_start(s)) < 0)
        goto fail;

Steven Liu's avatar
Steven Liu committed
897 898 899 900 901
    av_dict_copy(&options, hls->format_options, 0);
    ret = avformat_write_header(hls->avf, &options);
    if (av_dict_count(options)) {
        av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
        ret = AVERROR(EINVAL);
902
        goto fail;
Steven Liu's avatar
Steven Liu committed
903
    }
904
    //av_assert0(s->nb_streams == hls->avf->nb_streams);
905
    for (i = 0; i < s->nb_streams; i++) {
906
        AVStream *inner_st;
907
        AVStream *outer_st = s->streams[i];
908 909 910 911 912 913 914 915 916 917

        if (hls->max_seg_size > 0) {
            if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
                (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
                av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
                       "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
                       outer_st->codecpar->bit_rate, hls->max_seg_size);
            }
        }

918
        if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
919 920 921 922
            inner_st = hls->avf->streams[i];
        else if (hls->vtt_avf)
            inner_st = hls->vtt_avf->streams[0];
        else {
923
            /* We have a subtitle stream, when the user does not want one */
924 925 926
            inner_st = NULL;
            continue;
        }
927
        avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
928
    }
929
fail:
Steven Liu's avatar
Steven Liu committed
930 931

    av_dict_free(&options);
932
    if (ret < 0) {
933
        av_freep(&hls->basename);
934
        av_freep(&hls->vtt_basename);
935 936
        if (hls->avf)
            avformat_free_context(hls->avf);
937 938 939
        if (hls->vtt_avf)
            avformat_free_context(hls->vtt_avf);

940 941 942 943 944 945 946
    }
    return ret;
}

static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
{
    HLSContext *hls = s->priv_data;
947
    AVFormatContext *oc = NULL;
948 949
    AVStream *st = s->streams[pkt->stream_index];
    int64_t end_pts = hls->recording_time * hls->number;
950
    int is_ref_pkt = 1;
951
    int ret, can_split = 1;
952
    int stream_index = 0;
953

954 955 956 957 958 959 960 961
    if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) {
        /* reset end_pts, hls->recording_time at end of the init hls list */
        int init_list_dur = hls->init_time * hls->nb_entries * AV_TIME_BASE;
        int after_init_list_dur = (hls->sequence - hls->nb_entries ) * hls->time * AV_TIME_BASE;
        hls->recording_time = hls->time * AV_TIME_BASE;
        end_pts = init_list_dur + after_init_list_dur ;
    }

962
    if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE ) {
963 964 965 966 967 968
        oc = hls->vtt_avf;
        stream_index = 0;
    } else {
        oc = hls->avf;
        stream_index = pkt->stream_index;
    }
969 970 971 972 973
    if (hls->start_pts == AV_NOPTS_VALUE) {
        hls->start_pts = pkt->pts;
        hls->end_pts   = pkt->pts;
    }

974
    if (hls->has_video) {
975
        can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
976
                    ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
977
        is_ref_pkt = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
978
    }
979 980
    if (pkt->pts == AV_NOPTS_VALUE)
        is_ref_pkt = can_split = 0;
981

982
    if (is_ref_pkt)
983 984
        hls->duration = (double)(pkt->pts - hls->end_pts)
                                   * st->time_base.num / st->time_base.den;
985

986 987
    if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base,
                                   end_pts, AV_TIME_BASE_Q) >= 0) {
988 989 990 991 992
        int64_t new_start_pos;
        av_write_frame(oc, NULL); /* Flush any buffered data */

        new_start_pos = avio_tell(hls->avf->pb);
        hls->size = new_start_pos - hls->start_pos;
993
        ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
994
        hls->start_pos = new_start_pos;
995
        if (ret < 0)
996 997
            return ret;

998
        hls->end_pts = pkt->pts;
999
        hls->duration = 0;
1000

1001 1002 1003 1004
        if (hls->flags & HLS_SINGLE_FILE) {
            if (hls->avf->oformat->priv_class && hls->avf->priv_data)
                av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
            hls->number++;
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
        } else if (hls->max_seg_size > 0) {
            if (hls->avf->oformat->priv_class && hls->avf->priv_data)
                av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0);
            if (hls->start_pos >= hls->max_seg_size) {
                hls->sequence++;
                ff_format_io_close(s, &oc->pb);
                if (hls->vtt_avf)
                    ff_format_io_close(s, &hls->vtt_avf->pb);
                ret = hls_start(s);
                hls->start_pos = 0;
                /* When split segment by byte, the duration is short than hls_time,
                 * so it is not enough one segment duration as hls_time, */
                hls->number--;
            }
            hls->number++;
1020
        } else {
1021
            ff_format_io_close(s, &oc->pb);
1022
            if (hls->vtt_avf)
1023
                ff_format_io_close(s, &hls->vtt_avf->pb);
1024

1025 1026
            ret = hls_start(s);
        }
1027

1028
        if (ret < 0)
1029 1030
            return ret;

1031
        if( st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE )
1032 1033
            oc = hls->vtt_avf;
        else
1034 1035 1036 1037 1038 1039
        oc = hls->avf;

        if ((ret = hls_window(s, 0)) < 0)
            return ret;
    }

1040
    ret = ff_write_chained(oc, stream_index, pkt, s, 0);
1041 1042 1043 1044 1045 1046 1047 1048

    return ret;
}

static int hls_write_trailer(struct AVFormatContext *s)
{
    HLSContext *hls = s->priv_data;
    AVFormatContext *oc = hls->avf;
1049
    AVFormatContext *vtt_oc = hls->vtt_avf;
1050 1051

    av_write_trailer(oc);
1052 1053
    if (oc->pb) {
        hls->size = avio_tell(hls->avf->pb) - hls->start_pos;
1054
        ff_format_io_close(s, &oc->pb);
1055
        hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size);
1056
    }
1057 1058 1059 1060 1061

    if (vtt_oc) {
        if (vtt_oc->pb)
            av_write_trailer(vtt_oc);
        hls->size = avio_tell(hls->vtt_avf->pb) - hls->start_pos;
1062
        ff_format_io_close(s, &vtt_oc->pb);
1063
    }
1064
    av_freep(&hls->basename);
1065
    avformat_free_context(oc);
1066

1067 1068 1069
    hls->avf = NULL;
    hls_window(s, 1);

1070 1071 1072 1073 1074 1075
    if (vtt_oc) {
        av_freep(&hls->vtt_basename);
        av_freep(&hls->vtt_m3u8_name);
        avformat_free_context(vtt_oc);
    }

1076 1077
    hls_free_segments(hls->segments);
    hls_free_segments(hls->old_segments);
1078 1079 1080 1081 1082 1083
    return 0;
}

#define OFFSET(x) offsetof(HLSContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
1084
    {"start_number",  "set first number in the sequence",        OFFSET(start_sequence),AV_OPT_TYPE_INT64,  {.i64 = 0},     0, INT64_MAX, E},
1085
    {"hls_time",      "set segment length in seconds",           OFFSET(time),    AV_OPT_TYPE_FLOAT,  {.dbl = 2},     0, FLT_MAX, E},
1086
    {"hls_init_time", "set segment length in seconds at init list",           OFFSET(init_time),    AV_OPT_TYPE_FLOAT,  {.dbl = 0},     0, FLT_MAX, E},
1087
    {"hls_list_size", "set maximum number of playlist entries",  OFFSET(max_nb_segments),    AV_OPT_TYPE_INT,    {.i64 = 5},     0, INT_MAX, E},
Steven Liu's avatar
Steven Liu committed
1088
    {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1089
    {"hls_vtt_options","set hls vtt list of options for the container format used for hls", OFFSET(vtt_format_options_str), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1090
    {"hls_wrap",      "set number after which the index wraps",  OFFSET(wrap),    AV_OPT_TYPE_INT,    {.i64 = 0},     0, INT_MAX, E},
1091
    {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, E},
1092
    {"hls_base_url",  "url to prepend to each playlist entry",   OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E},
1093
    {"hls_segment_filename", "filename template for segment files", OFFSET(segment_filename),   AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
1094
    {"hls_segment_size", "maximum size per segment file, (in bytes)",  OFFSET(max_seg_size),    AV_OPT_TYPE_INT,    {.i64 = 0},               0,       INT_MAX,   E},
1095
    {"hls_key_info_file",    "file with key URI and key file path", OFFSET(key_info_file),      AV_OPT_TYPE_STRING, {.str = NULL},            0,       0,         E},
1096
    {"hls_subtitle_path",     "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1097 1098
    {"hls_flags",     "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"},
    {"single_file",   "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX,   E, "flags"},
1099
    {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX,   E, "flags"},
1100
    {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX,   E, "flags"},
1101
    {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX,   E, "flags"},
1102
    {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX,   E, "flags"},
1103
    {"split_by_time", "split the hls segment by time which user set by hls_time", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SPLIT_BY_TIME }, 0, UINT_MAX,   E, "flags"},
1104
    {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX,   E, "flags"},
1105
    {"program_date_time", "add EXT-X-PROGRAM-DATE-TIME", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PROGRAM_DATE_TIME }, 0, UINT_MAX,   E, "flags"},
1106 1107
    {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
    {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
1108 1109 1110
    {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" },
    {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" },
    {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" },
1111
    {"method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1112

1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
    { NULL },
};

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


AVOutputFormat ff_hls_muxer = {
    .name           = "hls",
1126
    .long_name      = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"),
1127 1128
    .extensions     = "m3u8",
    .priv_data_size = sizeof(HLSContext),
1129 1130
    .audio_codec    = AV_CODEC_ID_AAC,
    .video_codec    = AV_CODEC_ID_H264,
1131
    .subtitle_codec = AV_CODEC_ID_WEBVTT,
1132 1133 1134 1135 1136 1137
    .flags          = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH,
    .write_header   = hls_write_header,
    .write_packet   = hls_write_packet,
    .write_trailer  = hls_write_trailer,
    .priv_class     = &hls_class,
};