利用FFmpeg按视频关键帧精准切割。

在xcode上开发

//param splitSeconds 为视频分割的时长

+ (BOOL)executeSplit:(unsignedint)splitSeconds {

    AVFormatContext*ifmtCtx,*ofmtCtx;

    charconst      *inputFileName, *outputFileName;

    int            video_index;

    inputFileName = [[[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp4"] UTF8String];

    outputFileName ="/Users/ubaby/Library/Containers/bylh.testFFmpegOS/Data/Documents/test0.mp4";

    AVPacketreadPkt, splitKeyPacket;

    intret;

    av_register_all();

    if((ret =avformat_open_input(&ifmtCtx, inputFileName,0,0)) <0) {

        return false;

    }


    if((ret =avformat_find_stream_info(ifmtCtx,0)) <0) {

        return false;

    }

    for(inti =0; i < ifmtCtx->nb_streams; i++) {


        AVStream*in_stream = ifmtCtx->streams[i];

        if(in_stream->codec->codec_type==AVMEDIA_TYPE_VIDEO){

            video_index = i;

        }


    }

    intden = ifmtCtx->streams[video_index]->r_frame_rate.den;

    intnum = ifmtCtx->streams[video_index]->r_frame_rate.num;

    floatfps = (float)num / den;

    unsignedintsplitVideoSize = fps*splitSeconds;

    charconst      *save_name = outputFileName;

    charconst      *temp_name = save_name;

    avformat_alloc_output_context2(&ofmtCtx, NULL, NULL, temp_name);


    if(!ofmtCtx) {

        return false;

    }

    if (![testVideo writeVideoHeader:ifmtCtx out_filename:ofmtCtx out_filename:temp_name])

    {

        return false;

    }

    NSMutableArray *vecKeyFramePos = [NSMutableArray arrayWithCapacity:1];


    uint64_tframe_index =0;

    uint64_tkeyFrame_index =0;

    intframeCount =0;

    //读取分割点附近的关键帧位置

    while(1)

    {

        ++frame_index;

        ret =av_read_frame(ifmtCtx, &readPkt);

        if(ret <0)

        {

            break;

        }

        //过滤,只处理视频流

        if(readPkt.stream_index== video_index){

            ++frameCount;

            if(readPkt.flags&AV_PKT_FLAG_KEY)

            {

                keyFrame_index = frame_index;

            }

            if(frameCount==splitVideoSize)

            {

                //                vecKeyFramePos.push_back(keyFrame_index);

                [vecKeyFramePosaddObject:[NSNumbernumberWithUnsignedLongLong:keyFrame_index]];

                frameCount =0;

            }

        }

        av_packet_unref(&readPkt);

    }


    avformat_close_input(&ifmtCtx);

    ifmtCtx =NULL;

    //为了重新获取avformatcontext

    if((ret =avformat_open_input(&ifmtCtx, inputFileName,0,0)) <0) {

        return-1;

    }


    if((ret =avformat_find_stream_info(ifmtCtx,0)) <0) {

        return-1;

    }

    intnumber =0;

    av_init_packet(&splitKeyPacket);

    splitKeyPacket.data=NULL;

    splitKeyPacket.size=0;

    //时长对应的帧数超过视频的总视频帧数,则拷贝完整视频

    if(!vecKeyFramePos.count){

        [vecKeyFramePosaddObject:[NSNumbernumberWithUnsignedLongLong:frame_index]];

    }

    keyFrame_index = [[vecKeyFramePosfirstObject]unsignedLongLongValue];


    NSUIntegerkeyFrameIter =1;

    frame_index =0;

    int64_tlastPts =0;

    int64_tlastDts =0;

    int64_tprePts =0;

    int64_tpreDts =0;

    while(1)

    {

        ++frame_index;

        ret =av_read_frame(ifmtCtx, &readPkt);

        if(ret <0)

        {

            break;

        }


        av_packet_rescale_ts(&readPkt, ifmtCtx->streams[readPkt.stream_index]->time_base, ofmtCtx->streams[readPkt.stream_index]->time_base);

        prePts = readPkt.pts;

        preDts = readPkt.dts;

        readPkt.pts-= lastPts;

        readPkt.dts-= lastDts;

        if(readPkt.pts< readPkt.dts)

        {

            readPkt.pts= readPkt.dts+1;

        }

        //为分割点处的关键帧要进行拷贝

        if(readPkt.flags&AV_PKT_FLAG_KEY&&frame_index == keyFrame_index)

        {

            av_copy_packet(&splitKeyPacket, &readPkt);

        }

        else{

            ret =av_interleaved_write_frame(ofmtCtx, &readPkt);

            if(ret <0) {

                //break;


            }

        }


        if(frame_index == keyFrame_index)

        {

            lastDts = preDts;

            lastPts = prePts;

            if(keyFrameIter < vecKeyFramePos.count)

            {

                keyFrame_index = [vecKeyFramePos[keyFrameIter]unsignedLongLongValue];

                keyFrameIter++;

            }


            av_write_trailer(ofmtCtx);

            avio_close(ofmtCtx->pb);

            avformat_free_context(ofmtCtx);

            ++number;

            NSString *tempStr = [NSString stringWithFormat:@"/Users/ubaby/Library/Containers/bylh.testFFmpegOS/Data/Documents/test%d.mp4",number];

            NSLog(@"===========%@",tempStr);

            temp_name = [tempStrUTF8String];

            avformat_alloc_output_context2(&ofmtCtx,NULL,NULL, temp_name);

            if(!ofmtCtx) {

                returnfalse;

            }

            if(![testVideowriteVideoHeader:ifmtCtxout_filename:ofmtCtxout_filename:temp_name])

            {

                returnfalse;

            }

            splitKeyPacket.pts=0;

            splitKeyPacket.dts=0;

            //把上一个分片处的关键帧写入到下一个分片的起始处,保证下一个分片的开头为I帧

            ret =av_interleaved_write_frame(ofmtCtx, &splitKeyPacket);

        }



        av_packet_unref(&readPkt);


    }

    av_packet_unref(&splitKeyPacket);

    av_write_trailer(ofmtCtx);

    avformat_close_input(&ifmtCtx);

    avio_close(ofmtCtx->pb);

    avformat_free_context(ofmtCtx);


    return true;

}

+ (BOOL)writeVideoHeader:(AVFormatContext*)ifmtCtx out_filename:(AVFormatContext*)ofmtCtx out_filename:(charconst*)out_filename

{

    AVOutputFormat *ofmt = NULL;

    intret;

    int            video_index;


    ofmt = ofmtCtx->oformat;

    for(inti =0; i < ifmtCtx->nb_streams; i++) {


        //∏˘æ› ‰»Î¡˜¥¥Ω® ‰≥ˆ¡˜£®Create output AVStream according to input AVStream£©

        AVStream*in_stream = ifmtCtx->streams[i];

        if(in_stream->codec->codec_type==AVMEDIA_TYPE_VIDEO){

            video_index = i;

        }

        AVStream*out_stream =avformat_new_stream(ofmtCtx, in_stream->codec->codec);

        if(!out_stream) {

            ret =AVERROR_UNKNOWN;

            returnfalse;

        }

        //∏¥÷∆AVCodecContextµƒ…Ë÷√£®Copy the settings of AVCodecContext£©

        ret =avcodec_copy_context(out_stream->codec, in_stream->codec);

        if(ret <0) {

            returnfalse;

        }

        out_stream->codec->codec_tag=0;

        if(ofmtCtx->oformat->flags&AVFMT_GLOBALHEADER)

            out_stream->codec->flags|=AV_CODEC_FLAG_GLOBAL_HEADER;


    }

    if(!(ofmt->flags&AVFMT_NOFILE)) {

        ret =avio_open(&ofmtCtx->pb, out_filename,AVIO_FLAG_WRITE);

        if(ret <0) {

            returnfalse;

        }

    }

    ret =avformat_write_header(ofmtCtx,NULL);

    if(ret <0){

        return false;

    }

    return true;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容