Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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