当前位置: 首页 > news >正文

ffmpeg 转换flv压缩大小_用FFMPEG SDK进行视频转码压缩时解决音视频不同步问题的方法(转)...

用FFMPEG SDK进行视频转码压缩的时候,转码成功后去看视频的内容,发现音视频是不同步的。这个的确是一个恼火的事情。我在用FFMPEG SDK做h264格式的FLV文件编码Filter的时候就碰到了这个问题。经过研究发现,FFMPEG SDK写入视频的时候有两个地方用来控制写入的时间戳,一个是AvPacket, 一个是AvFrame。 在调用avcodec_encode_video的时候需要传入AvFrame的对象指针,也就是传入一帧未压缩的视频进行压缩处理,AvFrame包含一个pts的参数,这个参数就是当前帧将来在还原播放的时候的时间戳。而AvPacket里面也有pts,还有dts。说起这个就必须要说明一下I,P,B三种视频压缩帧。I帧就是关键帧,不依赖于其他视频帧,P帧是向前预测的帧,只依赖于前面的视频帧,而B帧是双向预测视频帧,依赖于前后视频帧。由于B帧的存在,因为它是双向的,必须知道前面的视频帧和后面的视频帧的详细内容后,才能知道本B帧最终该呈现什么图像。而pts和dts两个参数就是用来控制视频帧的显示和解码的顺序。pts就是帧显示的顺序。dts就是帧被读取进行解码的顺序。如果没有B帧存在,dts和pts是相同的。反之,则是不相同的。关于这个的详细介绍可以参考一下mpeg的原理。再说说AvPacket中包含的pts和dts两个到底该设置什么值?pts和dts需要设置的就是视频帧解码和显示的顺序。每增加一帧就加一,并不是播放视频的时间戳。

但是实践证明经过rmvb解码的视频有时候并不是固定帧率的,而是变帧率的,这样,如果每压缩一帧,pts和dts加一的方案为导致音视频不同步。那怎么来解决音视频同步的问题呢?请看如下代码段。lTimeStamp 是通过directshow 获取的当前的视频帧的时间戳。m_llframe_index为当前已经经过压缩处理的帧的数量。首先av_rescale计算得到当前压缩处理已经需要处理什么时间戳的视频帧,如果该时间戳尚未到达directshow当前提供的视频帧的时间戳,则将该帧丢弃掉。否则进行压缩操作。并设置AVPacket的pts和dts。这里假设B帧不存在。因为在将来播放的时候视频以我们设定的固定播放帧率进行播放,所以需要根据设定的播放帧率计算得到的视频帧时间戳和directshow提供的当前视频帧的时间戳进行比较,设定是否需要进行实施延缓播放的策略。如果需要延缓播放,则将pts增加步长2,否则以普通速度播放,则设置为1.dts与之相同。__int64 x =av_rescale(m_llframe_index,AV_TIME_BASE*(int64_t)c-

if( x < lTimeStamp ){return TRUE;}m_pVideoFrame2-

m_llframe_index += pkt.duration;}//pkt.pts = lTimeStamp * (__int64)frame_rate.den / 1000;if( c-

请问avcodec_decode_video解码的帧为什么后面的比前面的pts小呢?

请问如下代码:while( av_read_frame(pFormatCtxSource,&packet)<=0 ){if( packet.stream_index==videoStream ){int out_size = avcodec_decode_video(pCodecCtxSource,pFrameSource, &bFrameFinished, packet.data, packet.size); // Decode fromsource frameif( bFrameFinished ){pFrameSource-

在我Decode的时候,第一帧得到的 pFrameSource-

答复:

Because you have B - Framefor example:the Inputsequence for video encoder1  2  3   4   5    6   7I   B   B   P  B   B   ILet's take1,2,3.. as PTS for simplificationthe out sequencefor video encoder ( this equals the decoder sequence)1  4  2    3   7   5   6I  P    B   B   I    B   Byou will get aPTS sequence as following:

1  4  2  3  7  5  6 7  5 6sequence will be same as your question

问:

哦,那是不是我的pts不能这么算呢?而是要每次+1,对吗?那么,packet中的pts和dts要用在什么地方呢?我这样按存储顺序进行解码的话,显示之前是不是要自己进行缓存呢?谢谢!

另外,还有个问题,既然解码的时候,不一定是按照pts递增的顺序得到的解码后的画面,那我在编码图像的时候,是应该按照解码出来的帧顺序进行编码吗?还是把帧先缓存起来,最后严格接照图像的显示顺序来编码呢?用代码来表示,就是:方法一:while(av_read_frame ){ 解码; pts+1; 编码; 输出;}

方法二:while(av_read_frame ){解码;if( pts{

缓存;

}

else

{

编码缓存的帧并写入文件;

}

}

这两个方法,哪个是正确的呢?因为我看到网上的代码都用的是方法一,但是我觉得方法二是对的呀?

答:

the output of decoderis the right order for display because I/P frames will be cacheduntil next I/P

理解:

Decoder 后output的pts 是按正常的顺序,即显示的顺序输出的,如果有B帧,decoder会缓存。

但encoder后,输出的是按dts输出的。

Pts,dts并不是时间戳,而更应该理解为frame的顺序序列号。由于每帧frame的帧率并不一定是一致的,可能会变化的。转换为时间戳的话,应该是(pts*帧率)。为加深理解

可以将pts比做是第pts帧frame,假设每帧的帧率不变的话,则显示的时间戳为(pts*帧率),如果考虑帧率变化的,则要想办法将(pts*当前的帧率)累加到后面。

在tutorial5中在decode 下增加trace后打印情况:

len1 = avcodec_decode_video(is-

packet-

printf("-----------------------------------------------------------------------------\n");

printf("avcodec_decode_videopacket-

printf("avcodec_decode_videopFrame-

if(pFrame-

printf("avcodec_decode_video*(uint64_t *)pFrame-

其中播一个mp4文件的打印情况:

-----------------------------------------------------------------------------

avcodec_decode_video packet-<1ae>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<1ae>

-----------------------------------------------------------------------------

avcodec_decode_video packet-<1af>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<1af>

-----------------------------------------------------------------------------

avcodec_decode_video packet-<24c>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<1ac>

-----------------------------------------------------------------------------

avcodec_decode_video packet-<24d>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<24d>

-----------------------------------------------------------------------------

avcodec_decode_video packet-<24e>

avcodec_decode_videopFrame-<0>

avcodec_decode_video*(uint64_t *)pFrame-<24e>

以下为播放rm文件的情况:

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<1831b>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<1831b>

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<18704>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<18704>

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<18aed>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<18aed>

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<18ed6>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<18ed6>

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<192bf>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<192bf>

-----------------------------------------------------------------------------

avcodec_decode_videopacket-<196a8>

avcodec_decode_videopFrame-<0>

avcodec_decode_video *(uint64_t *)pFrame-<196a8>

可以看出有的pts是+1 累加,有的是加了很多,但都是按顺序累加的。当传人decoder前的packet有pts时,则decoder后获取的frame将会赋值packet的pts;当传人的packet 只是一帧的部分数据或是B帧,由于decoder出来的frame要按正常的pts顺序输出,有可能decoder不会获取到frame ,或decoder内部会缓存也不会输出frame,即frame的pts会为空。Frame pts(即opaque) 为空的话则会看frame-

对于:

pts *= av_q2d(is-

// Did we get avideo frame?

if(frameFinished) {

pts =synchronize_video(is, pFrame, pts);

/ synchronize_video考虑了3中情况:

1.    pts拿到的话就用该pts

2.    pts没有拿到的话就用前一帧的pts时间

3.    如果该帧要重复显示,则将显示的数量*帧率,再加到前面的pts中。

if(queue_picture(is, pFrame, pts) < 0 decodershowp>

static double synchronize_video(VideoState *is, AVFrame*src_frame, double pts) {

doubleframe_delay;

if(pts != 0) {

/* if we havepts, set video clock to it */

is-

} else {

/* if we aren'tgiven a pts, set it to the clock */

pts =is-

}

/* update thevideo clock */

/很关键:前面传进来的pts已经是时间戳了,是当前frame开始播放的时间戳,

/下面frame_delay是该帧显示完将要花费的时间,(pts+frame_delay)也即是/预测的下一帧将要播放的时间戳。

frame_delay =av_q2d(is-

/* if we arerepeating a frame, adjust clock accordingly */

//重复多帧的话要累加上

frame_delay +=src_frame-

is-

return pts;/此时返回的值即为下一帧将要开始显示的时间戳。

}

///开定时器去显示帧队列中的已经decode过的数据,按前面的分析我们已经知道帧队列中的数据已经是按pts顺序插入到队列中的。Timer的作用就是有帧率不一致及重复帧的情况造成时间戳不是线性的,有快有慢,从而tutorial5才有timer的方式来播放:追赶

以下是一个网友很直观浅显的例子解释:

ccq(183892517) 17:05:21 if(packet-

David Cen(3727567) 17:06:44 就是有一把尺子 一只蚂蚁跟着一个标杆走  David Cen(3727567) 17:06:58 标杆是匀速的 蚂蚁或快或慢 DavidCen(3727567) 17:07:18 慢了你就抽它 让他跑起来 快了就拽它  David Cen(3727567) 17:07:38 这样音(标杆)视频(蚂蚁)就能同步了 DavidCen(3727567) 17:08:00 这里最大的问题就是音频是匀速的 视频是非线性的

另外:此时vp–

static void video_refresh_timer(void *userdata) {

VideoState *is = (VideoState*)userdata;

VideoPicture *vp;

double actual_delay, delay,sync_threshold, ref_clock, diff;

if(is-

if(is-

schedule_refresh(is, 1);

} else {

vp =&is-

delay = vp-

if(delay < delay>= 1.0) {

/* if incorrect delay, useprevious one */

delay =is-

}

/* save for next time */

is-

is-

/* update delay to sync toaudio */

ref_clock = get_audio_clock(is);/获取到声音当前播放的时间戳。

diff = vp-

//也就是说在diff这段时间中声音是匀速发生的,但是在delay这段时间frame的显示可能就会有快//慢的区别。

/* Skip or repeat the frame.Take delay into account

FFPlay still doesn't "know if this is thebest guess." */

sync_threshold = (delay

if(fabs(diff) < AV NOSYNC_THRESHOLD p>

if(diff < -sync_threshold p>

delay = 0;//下一帧画面显示的时间和当前的声音很近的话加快显示下一帧(即后面video_display显示完当前帧后开启定时器很快去显示下一帧)

} else if(diff <=sync_threshold) {

delay = 2 * delay;//下一帧开始显示的时间和当前声音的时间隔的比较长则延缓,即两帧画面间话的显示的时间长度大于两帧画面间的声音播放的时间,则我们将两帧画显示的时候加倍拖长点,比如帧1和帧2的时间显示间隔为40ms,但帧1和帧2的声音播放时间为55ms,怎么办呢?我们不可能去打乱声音的质量的,则我们采用的方法是:将两帧画面的播放间隔加大,本来是过30ms就要开始播下一帧的,我们改成60ms后才播下一帧。

}

}/

当然如果diff大于AV_NOSYNC_THRESHOLD,即快进的模式了,画面跳动太大,不存在音视频同步的问题了。

is-

/* computer the REAL delay*/

actual_delay =is-

if(actual_delay < 0 p>

/* Really it should skipthe picture instead */

actual_delay = 0.010;

}

schedule_refresh(is,(int)(actual_delay * 1000 + 0.5));开定时器去显示下一帧

/* show the picture! */

video_display(is);立马显示当前帧

/* update queue for nextpicture! */

if(++is-

is-

}

SDL_LockMutex(is-

is-

SDL_CondSignal(is-

SDL_UnlockMutex(is-

}

} else {

schedule_refresh(is, 100);

}

196a8>0>196a8>192bf>0>192bf>18ed6>0>18ed6>18aed>0>18aed>18704>0>18704>1831b>0>1831b>24e>0>24e>24d>0>24d>1ac>0>24c>1af>0>1af>1ae>0>1ae>

相关文章:

  • vuex异步获取数据_vuex起步 (三、异步管理数据)
  • python处理考勤数据txt文件_腾讯课堂考勤助手:一款自动化考勤数据处理工具
  • conda create出现连接问题_网络连接出现问题,苹果HomePod mini出现故障门
  • 启动不了 驱动程序签字功能_电脑的蓝牙功能您用了吗?不识别蓝牙和蓝牙搜索不到设备怎么办?...
  • cwntos新建目录挂载磁盘_怎么初始化你的 Linux 新磁盘?
  • dataframe for循环 筛选_Python:编写地道循环的两个建议,轻松应对新需求
  • 套用带标题行的表格样式_计算机二级通关攻略??必背考点 知识点整理 带你稳过计算机二级...
  • 城市运行一网统管_白鹤镇:聚焦“一网统管”建设,助力城市精细化管理
  • 交错级数如何判断收敛_数学分析|第12章 数项级数交错级数敛散性判别方法总结I:莱布尼茨和拉贝模型...
  • 10以内的分解与组成怎么教_十以内的加减法怎么教?分四步进行教学,轻松掌握计算方法...
  • 安卓手机怎么看海拔高度_安卓手机会越用越卡,怎么解决?
  • 5.1.2全景声音箱摆位_什么是杜比全景声家庭影院?音箱怎么摆位?
  • 资产管理台账系统_固定资产管理系统是如何进行精细化管理的?
  • 综保参数如何设定_自动编程的切削参数设置如何设置?答案就在这篇文章里
  • mac 终端取消之前的命令_小白学Mac终端命令
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • Javascript编码规范
  • linux学习笔记
  • python 学习笔记 - Queue Pipes,进程间通讯
  • vue-loader 源码解析系列之 selector
  • yii2中session跨域名的问题
  • 编写符合Python风格的对象
  • 初识 beanstalkd
  • 电商搜索引擎的架构设计和性能优化
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 简单实现一个textarea自适应高度
  • 自制字幕遮挡器
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • MPAndroidChart 教程:Y轴 YAxis
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 组复制官方翻译九、Group Replication Technical Details
  • ​io --- 处理流的核心工具​
  • #Java第九次作业--输入输出流和文件操作
  • (1)bark-ml
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (翻译)terry crowley: 写给程序员
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (六)软件测试分工
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (十一)图像的罗伯特梯度锐化
  • (一一四)第九章编程练习
  • (转)VC++中ondraw在什么时候调用的
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .axf 转化 .bin文件 的方法
  • .NET 使用配置文件
  • .NET 中 GetProcess 相关方法的性能
  • .NET 中让 Task 支持带超时的异步等待
  • .net6+aspose.words导出word并转pdf
  • .netcore如何运行环境安装到Linux服务器
  • @Resource和@Autowired的区别