美文网首页
利用FFmpeg按视频关键帧精准切割。

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

作者: 嘿嘿和露红叶 | 来源:发表于2018-06-10 14:25 被阅读95次

    在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按视频关键帧精准切割。

          本文链接:https://www.haomeiwen.com/subject/dmifeftx.html