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

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容