在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按视频关键帧精准切割。
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 图片转视频 为什么想将图片转视频? 是这样的,我打造的任性动图软件,在编辑制作GIF动图方面,已经基本完善。现在想...
- 在上一篇笔记中我们已经完成了使用SDL播放声音和视频,声音播放没有什么问题,而视频播放太快,很明显视频没有同步。在...