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

File 0001-lavf-segment-add-segment_frames-option.patch, 9.0 KB (added by saste, 4 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