利用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;

}

你可能感兴趣的:(利用FFmpeg按视频关键帧精准切割。)