Ticket #1483: 0001-lavf-segment-add-segment_frames-option.patch

File 0001-lavf-segment-add-segment_frames-option.patch, 9.0 KB (added by Stefano Sabatini, 14 years ago)
  • doc/muxers.texi

    From 57f9b7f208567b4f3cdfa6125a3b4b767e474897 Mon Sep 17 00:00:00 2001
    From: Stefano Sabatini <stefasab@gmail.com>
    Date: Sun, 9 Dec 2012 20:26:30 +0100
    Subject: [PATCH] lavf/segment: add segment_frames option
    
    This is meant to address trac ticket #1483.
    ---
     doc/muxers.texi       |   15 +++++++
     libavformat/segment.c |  103 +++++++++++++++++++++++++++++++++++++++++++------
     2 files changed, 106 insertions(+), 12 deletions(-)
    
    diff --git a/doc/muxers.texi b/doc/muxers.texi
    index 25cf565..763b172 100644
    a b the specified time and the time set by @var{force_key_frames}.  
    599599Specify a list of split points. @var{times} contains a list of comma
    600600separated duration specifications, in increasing order.
    601601
     602@item segment_frames @var{frames}
     603Specify a list of split video frame numbers. @var{frames} contains a
     604list of comma separated integer numbers, in increasing order.
     605
     606This option specifies to start a new segment whenever a video key
     607frame is found and the sequential number (starting from 0) of the
     608video frame is greater or equal to the next value in the list.
     609
    602610@item segment_wrap @var{limit}
    603611Wrap around segment index once it reaches @var{limit}.
    604612
    … … In order to force key frames on the input file, transcoding is  
    643651required.
    644652
    645653@item
     654Segment the input file by splitting the input file according to the
     655frame numbers sequence specified with the @var{segment_frame} option:
     656@example
     657ffmpeg -i in.mkv -codec copy -map 0 -f segment -segment_list out.csv -segment_frames 100,200,300,500,800 out%03d.nut
     658@end example
     659
     660@item
    646661To convert the @file{in.mkv} to TS segments using the @code{libx264}
    647662and @code{libfaac} encoders:
    648663@example
  • libavformat/segment.c

    diff --git a/libavformat/segment.c b/libavformat/segment.c
    index dc9b9c8..801fd42 100644
    a b typedef enum {  
    4545    LIST_TYPE_NB,
    4646} ListType;
    4747
    48 
    4948#define SEGMENT_LIST_FLAG_CACHE 1
    5049#define SEGMENT_LIST_FLAG_LIVE  2
    5150
    … … typedef struct {  
    6564    AVIOContext *list_pb;  ///< list file put-byte context
    6665    char *time_str;        ///< segment duration specification string
    6766    int64_t time;          ///< segment duration
     67
    6868    char *times_str;       ///< segment times specification string
    6969    int64_t *times;        ///< list of segment interval specification
    7070    int nb_times;          ///< number of elments in the times array
     71
     72    char *frames_str;      ///< segment frame numbers specification string
     73    int *frames;           ///< list of frame number specification
     74    int nb_frames;         ///< number of elments in the frames array
     75    int frame_count;
     76
    7177    char *time_delta_str;  ///< approximation value duration used for the segment times
    7278    int64_t time_delta;
    7379    int  individual_header_trailer; /**< Set by a private option. */
    … … end:  
    318324    return ret;
    319325}
    320326
     327static int parse_frames(void *log_ctx, int **frames, int *nb_frames,
     328                        const char *frames_str)
     329{
     330    char *p;
     331    int i, ret = 0;
     332    char *frames_str1 = av_strdup(frames_str);
     333    char *saveptr = NULL;
     334
     335    if (!frames_str1)
     336        return AVERROR(ENOMEM);
     337
     338#define FAIL(err) ret = err; goto end
     339
     340    *nb_frames = 1;
     341    for (p = frames_str1; *p; p++)
     342        if (*p == ',')
     343            (*nb_frames)++;
     344
     345    *frames = av_malloc(sizeof(**frames) * *nb_frames);
     346    if (!*frames) {
     347        av_log(log_ctx, AV_LOG_ERROR, "Could not allocate forced frames array\n");
     348        FAIL(AVERROR(ENOMEM));
     349    }
     350
     351    p = frames_str1;
     352    for (i = 0; i < *nb_frames; i++) {
     353        long int f;
     354        char *tailptr;
     355        char *fstr = av_strtok(p, ",", &saveptr);
     356
     357        p = NULL;
     358        if (!fstr) {
     359            av_log(log_ctx, AV_LOG_ERROR, "Empty frame specification in frame list %s\n",
     360                   frames_str);
     361            FAIL(AVERROR(EINVAL));
     362        }
     363        f = strtol(fstr, &tailptr, 10);
     364        if (*tailptr || f <= 0 || f >= INT_MAX) {
     365            av_log(log_ctx, AV_LOG_ERROR,
     366                   "Invalid argument '%s', must be a positive integer <= INT64_MAX\n",
     367                   fstr);
     368            FAIL(AVERROR(EINVAL));
     369        }
     370        (*frames)[i] = f;
     371
     372        /* check on monotonicity */
     373        if (i && (*frames)[i-1] > (*frames)[i]) {
     374            av_log(log_ctx, AV_LOG_ERROR,
     375                   "Specified frame %d is greater than the following frame %d\n",
     376                   (*frames)[i], (*frames)[i-1]);
     377            FAIL(AVERROR(EINVAL));
     378        }
     379    }
     380
     381end:
     382    av_free(frames_str1);
     383    return ret;
     384}
     385
    321386static int open_null_ctx(AVIOContext **ctx)
    322387{
    323388    int buf_size = 32768;
    … … static int seg_write_header(AVFormatContext *s)  
    348413    if (!seg->write_header_trailer)
    349414        seg->individual_header_trailer = 0;
    350415
    351     if (seg->time_str && seg->times_str) {
     416    if (!!seg->time_str + !!seg->times_str + !!seg->frames_str > 1) {
    352417        av_log(s, AV_LOG_ERROR,
    353                "segment_time and segment_times options are mutually exclusive, select just one of them\n");
     418               "segment_time, segment_times, and segment_frames options "
     419               "are mutually exclusive, select just one of them\n");
    354420        return AVERROR(EINVAL);
    355421    }
    356422
    357     if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) {
     423    if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && (seg->times_str || seg->frames_str)) {
    358424        av_log(s, AV_LOG_ERROR,
    359                "segment_flags +live and segment_times options are mutually exclusive:"
    360                "specify -segment_time if you want a live-friendly list\n");
     425               "segment_flags +live and segment_times or segment_frames options are mutually exclusive: "
     426               "specify segment_time option if you want a live-friendly list\n");
    361427        return AVERROR(EINVAL);
    362428    }
    363429
    364430    if (seg->times_str) {
    365431        if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0)
    366432            return ret;
     433    } else if (seg->frames_str) {
     434        if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0)
     435            return ret;
    367436    } else {
    368437        /* set default value if not specified */
    369438        if (!seg->time_str)
    … … static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)  
    468537    SegmentContext *seg = s->priv_data;
    469538    AVFormatContext *oc = seg->avf;
    470539    AVStream *st = s->streams[pkt->stream_index];
    471     int64_t end_pts;
     540    int64_t end_pts = INT64_MAX;
     541    int start_frame = INT_MAX;
    472542    int ret;
    473543
    474544    if (seg->times) {
    475545        end_pts = seg->segment_count <= seg->nb_times ?
    476546            seg->times[seg->segment_count-1] : INT64_MAX;
     547    } else if (seg->frames) {
     548        start_frame = seg->segment_count <= seg->nb_frames ?
     549            seg->frames[seg->segment_count-1] : INT_MAX;
    477550    } else {
    478551        end_pts = seg->time * seg->segment_count;
    479552    }
    480553
    481554    /* if the segment has video, start a new segment *only* with a key video frame */
    482     if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
    483         pkt->pts != AV_NOPTS_VALUE &&
    484         av_compare_ts(pkt->pts, st->time_base,
    485                       end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
    486         pkt->flags & AV_PKT_FLAG_KEY) {
     555    if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO && seg->frame_count >= start_frame && pkt->flags & AV_PKT_FLAG_KEY) ||
     556        ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO || !seg->has_video) &&
     557         pkt->pts != AV_NOPTS_VALUE &&
     558         av_compare_ts(pkt->pts, st->time_base,
     559                       end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0 &&
     560         pkt->flags & AV_PKT_FLAG_KEY)) {
    487561        ret = segment_end(s, seg->individual_header_trailer);
    488562
    489563        if (!ret)
    … … static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)  
    528602    ret = ff_write_chained(oc, pkt->stream_index, pkt, s);
    529603
    530604fail:
     605    if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
     606        seg->frame_count++;
     607
    531608    if (ret < 0) {
    532609        if (seg->list)
    533610            avio_close(seg->list_pb);
    … … fail:  
    557634
    558635    av_opt_free(seg);
    559636    av_freep(&seg->times);
     637    av_freep(&seg->frames);
    560638
    561639    avformat_free_context(oc);
    562640    return ret;
    … … static const AVOption options[] = {  
    584662    { "segment_time",      "set segment duration",                       OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
    585663    { "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E },
    586664    { "segment_times",     "set segment split time points",              OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
     665    { "segment_frames",    "set segment split frame numbers",            OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL},  0, 0,       E },
    587666    { "segment_wrap",      "set number after which the index wraps",     OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
    588667    { "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E },
    589668