利用FFmpeg进行视频文件进行分割
来源:互联网 发布:怀孕软件哪个好 编辑:程序博客网 时间:2024/05/16 19:08
利用FFMPEG命令进行文件分割
ffmpeg -ss 00:00:00 -i input.mp4 -c copy -t 60 output.mp4
-ss 表示视频分割的起始时间,-t 表示分割时长,同时也可以用 00:01:00表示
注意 :-ss 要放在 -i 之前
对于普通的视频分割这个命令可能够用了
但是
如果你想要连续风格一段视频,简单的使用此命令就会发现一个问题:连续分割的视频之间存在细微的交集
原因:
视频的开始都是一个关键帧,如果视频的第一帧不是关键帧就会导致视频播放的前面简短画面模糊不清,所以为了让视频不会出现开始画面模糊的情况,就会从所开始时间定位到其对应帧,如果该帧不是关键帧,则在其位置附近找关键帧的位置,然后从该关键帧处开始复制视频帧。
根据起始时间定位到的帧不是关键帧,而是位于两个关键帧中间的B帧或P帧上,那么是从前一个关键帧开始还是后一个关键帧开始呢?
截至时间定位的帧同样可能处于非关键帧处,这时候不一定要向两边找关键帧?
这时候起始帧如果找前面的关键帧作为起始帧开始复制,就会导致本段视频的和前面视频有重复帧:重复帧数为起始关键帧和上一段截至帧之间的帧数。
如果起始帧找后面的关键帧开始复制,就会导致两段连续分割的视频可能出现跳帧现象
利用ffmpeg提供的库自己实现不重复不跳帧分割
利用上述分析,我们在分割的时候自己统一设置分割视频的截止帧为截止时间对应帧(假设此帧为非关键帧,否则为此帧的前一帧)附近前面关键帧的前一帧,而下一段分割视频就从该关键帧开始。
关键代码
视频头信息设置
AVOutputFormat *ofmt = NULL; int ret; ofmt = ofmtCtx->oformat; for (int i = 0; i < ifmtCtx->nb_streams; i++) { //根据输入流创建输出流 AVStream *in_stream = ifmtCtx->streams[i]; AVStream *out_stream = avformat_new_stream(ofmtCtx, in_stream->codec->codec); if (!out_stream) { return false; } //复制AVCodecContext的设置 ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { return false; } out_stream->codec->codec_tag = 0; if (ofmtCtx->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; } if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&ofmtCtx->pb, out_filename.c_str(), AVIO_FLAG_WRITE); if (ret < 0) { return false; } } ret = avformat_write_header(ofmtCtx, NULL); if (ret < 0){ return false; } return true;
帧拷贝
//param splitSeconds 为视频分割的时长 bool executeSplit(unsigned int splitSeconds){ AVPacket readPkt, splitKeyPacket; int ret; av_register_all(); if ((ret = avformat_open_input(&ifmtCtx, inputFileName.c_str(), 0, 0)) < 0) { return false; } if ((ret = avformat_find_stream_info(ifmtCtx, 0)) < 0) { return false; } for (int i = 0; i < ifmtCtx->nb_streams; i++) { AVStream *in_stream = ifmtCtx->streams[i]; if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO){ video_index = i; } } int den = ifmtCtx->streams[video_index]->r_frame_rate.den; int num = ifmtCtx->streams[video_index]->r_frame_rate.num; float fps = (float)num / den; unsigned int splitVideoSize = fps*splitSeconds; string save_name; save_name = outputFileName.substr(0, outputFileName.find_last_of(".")); string temp_name = save_name + "0"+suffixName; avformat_alloc_output_context2(&ofmtCtx, NULL, NULL, temp_name.c_str()); if (!ofmtCtx) { return false; } if (!writeVideoHeader(ifmtCtx, ofmtCtx, temp_name)) { return false; } vector<uint64_t> vecKeyFramePos; uint64_t frame_index = 0; uint64_t keyFrame_index = 0; int frameCount = 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); frameCount = 0; } } av_packet_unref(&readPkt); } avformat_close_input(&ifmtCtx); ifmtCtx = NULL; //为了重新获取avformatcontext if ((ret = avformat_open_input(&ifmtCtx, inputFileName.c_str(), 0, 0)) < 0) { return -1; } if ((ret = avformat_find_stream_info(ifmtCtx, 0)) < 0) { return -1; } int number = 0; av_init_packet(&splitKeyPacket); splitKeyPacket.data = NULL; splitKeyPacket.size = 0; //时长对应的帧数超过视频的总视频帧数,则拷贝完整视频 if (vecKeyFramePos.empty()){ vecKeyFramePos.push_back(frame_index); } vector<uint64_t>::iterator keyFrameIter = vecKeyFramePos.begin(); keyFrame_index = *keyFrameIter; ++keyFrameIter; frame_index = 0; int64_t lastPts = 0; int64_t lastDts = 0; int64_t prePts = 0; int64_t preDts = 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.end()) { keyFrame_index = *keyFrameIter; ++keyFrameIter; } av_write_trailer(ofmtCtx); avio_close(ofmtCtx->pb); avformat_free_context(ofmtCtx); ++number; char num[10]; _itoa_s(number, num, 10); temp_name = save_name + num + suffixName; avformat_alloc_output_context2(&ofmtCtx, NULL, NULL, temp_name.c_str()); if (!ofmtCtx) { return false; } if (!writeVideoHeader(ifmtCtx, ofmtCtx, save_name + num + suffixName)) { return false; } 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;}
1 0
- 利用FFmpeg进行视频文件进行分割
- 利用FFmpeg进行视频文件进行分割
- 利用FFMPEG进行视频分割
- 应用ffmpeg进行视频文件格式转换
- 使用ffmpeg命令对视频文件进行截图
- ffmpeg分割视频文件
- 如何将视频文件分割成流然后进行传输
- FFMpeg对视频文件进行解码的大致流程
- 使用ffmpeg进行视频文件转换成FLV整理
- ffmpeg对视频文件进行解码的大致流程
- ffmpeg对视频文件进行解码的大致流程
- 使用ffmpeg进行视频文件转换成FLV整理
- 利用mencoder进行视频分割和合并
- 利用mencoder进行视频分割和合并
- 利用cronolog对nginx日志进行分割
- 利用RAR对文件进行分割
- c利用strtok来进行字符串分割
- 【OpenCV】利用投影法进行字符分割
- mysql事务隔离级别
- 传感器
- 大型网站架构系列:消息队列(二)
- 了解 OAuth2.0协议 预研第三方登录 定义接口
- 209. Minimum Size Subarray Sum
- 利用FFmpeg进行视频文件进行分割
- HDU-5696-区间的价值(DFS)
- 虚拟文件系统VFS
- Hadoop与Spark整合
- java 数字时间年月日等 格式化format 输出
- 由system.currentTimeMillis() 获得当前的时间
- 朝鮮歷史 韓長庚 編
- 第十三周实践项目2————动物这样叫(3)
- EXCEL操作(自我总结)