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

123456789101112131415161718192021222324252627282930313233343536

帧拷贝

//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;
}
---------------------
作者:bikeytang
来源:CSDN
原文:https://blog.csdn.net/BikeyTang/article/details/51491139
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/zxtceq/p/10130586.html

时间: 2024-10-11 16:38:31

利用FFMPEG命令进行文件分割的相关文章

Linux的Split 命令 (文件分割)

功能能说明:分割文件.############################################################################# Split:按指定的行数截断文件 格式: split [-n] file [name] 参数说明: -n: 指定截断的每一文件的长度,不指定缺省为1000行 file: 要截断的文件 name: 截断后产生的文件的文件名的开头字母,不指定,缺省为x,即截断后产生的文件的文件名为xaa,xab....直到xzz #####

利用linux的df和du命令查看文件和目录的内存占用

文章目录 当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择. df可以查看一级文件夹大小.使用比例.档案系统及其挂入点,但对文件却无能为力. du可以查看文件及文件夹的大小. 两者配合使用,非常有效.比如用df查看哪个一级目录过大,然后用df查看文件夹或文件的大小,如此便可迅速确定症结. 做后台开发经常遇到磁盘占满的情况,毕竟不断有日志在生成,不断有文件在备份,如果长时间不管的话,总有一天会占用满. 题主有一天在发布java项目的时候,就发现提示 1 No space

linux大文件分割 split命令

linux split 命令 功能说明:切割文件. 语 法:split [--help][--version][-<行数>][-b <字节>][-C <字节>][-l <行数>][要切割的文件][输出文件名] [前缀] 补充说明:split可将文件切成较小的文件,预设每1000行会切成一个小文件. 参 数: -<行数>或-l<行数> 指定每多少行就要切成一个小文件. -b<字节> 指定每多少字就要切成一个小文件.支持单位:

FFmpeg命令行工具和批处理脚本进行简单的音视频文件编辑

FFmpeg_Tutorial FFmpeg工具和sdk库的使用demo 一.使用FFmpeg命令行工具和批处理脚本进行简单的音视频文件编辑 1.基本介绍 对于每一个从事音视频技术开发的工程师,想必没有一个人对FFmpeg这个名称感到陌生.FFmpeg是一套非常知名的音视频处理的开源工具,它包含了开发完成的工具软件.封装好的函数库以及源代码供我们按需使用.FFmpeg提供了非常强大的功能,可以完成音视频的编码.解码.转码.视频采集.后处理(抓图.水印.封装/解封装.格式转换等),还有流媒体服务等

(转)MP4文件两种格式AVC1和H264的区别及利用FFMPEG demux为h264码流事项

出自:http://www.mworkbox.com/wp/work/314.html 2013-05-04 MP4的视频H264封装有2种格式:h264和avc1,对于这个细节,很容易被忽略.笔者也是在改编LIVE555流媒体时,增加mp4文件类型支持时遇到了该问题. (一)首先,从原理上了解一下这2种格式的区别:AVC1 描述:H.264 bitstream without start codes.一般通过ffmpeg转码生成的视频,是不带起始码0×00000001的.H264 描述:H.2

ffmpeg把ts文件转m3u8并切片

Linux_x86_64流媒体环境:nginx + EasyDarwin-master 客户端播放器:VLC media player 下载windows下的ffmepg二进制版本,请进网站http://ffmpeg.zeranoe.com/builds/win32/shared|static 最新版本,无需自己编译. 下载完解压后,需要配置ffmepg的path环境变量.Path=%Path%;E:\ffmpeg-20151117-git-e9aea6d-win64-static\ffmpeg

利用FFmpeg玩转Android视频录制与压缩(二)&lt;转&gt;

转载出处:http://blog.csdn.net/mabeijianxi/article/details/72983362 预热 时光荏苒,光阴如梭,离上一次吹牛逼已经过去了两三个月,身边很多人的女票已经分了又合,合了又分,本屌依旧骄傲单身.上一次啊我们大致说了一些简单的FFmpeg命令以及Java层简单的调用方式,然后有很多朋友在github或者csdn上给我留言,很多时候我都选择避而不答,原因是本库以前用的so包是不开源的,我根本改不了里面东西.但是这一次啊我们玩点大的,我重新编译了FFm

转载:ffmpeg 音视频合成分割

http://blog.csdn.net/jixiuffff/article/details/5709976 当然先安装了 gentoo 下一条命令搞定 emerge  ffmpeg 格式转换 (将file.avi 转换成output.flv) ffmpeg -i  file.avi   output.flv -i 表示输入文件 :现在有个视频video.avi,有个音频 audio.mp3,将其合并成output.avi 两个命令                     ( video2.av

利用Shell命令获取IP地址

一 :获取单个网卡的IPv4地址,方法如下: 方法一:$/sbin/ifconfig ethX | awk '/inet addr/ {print $2}' | cut -f2 -d ":" 方法二:$/sbin/ifconfig ethX | awk '/inet addr/ {print $2}' | awk -F: '{print $2}' 方法三:$/sbin/ifconfig ethX | sed -ne 's/\(.*\)addr:\([[:digit:].]*\)\(.*