ffmpeg同步
1:ffmpeg解码流程 拆包,构建队列,解码,同步,显示
//计算视频Frame的显示时间
//获取pts
pts = 0;
//decodec video frame
avcodec_decode_video2(AVFormatContxt*,AVFrame,int*,AVPacket);
if( (pts = av_frame_get_best_effort_timestamp(pFrame)) == AV_NOPTS_VALUSE )
{
}
else
{
pts = 0;
}
pts *= av_q2d(pFormatCtx->Stream[video_index]->time_base);
if(frameFinished)
{
//计算需要的时间延时
//pts = synchronize_video(⋯⋯);
}
//synchronize_video(^)函数的实现
//video_clock是视频播放到当前帧时的已播放的时间长度。在synchronize函数中,如果没有得到该帧的PTS就用当前的video_clock来近似,然后更新video_clock的值。到这里已经知道了video中frame的显示时间了(秒为单位)
double synchronize_video(VideoState *is,AVFrame *src_frame,double pts)
{
double frame_delay;
if(pts != 0)
{
is->video_clock = pts;//保存pts
}
else
{
pts = is->video_clock;//否者使用上一次保存的pts
}
//更新video clock
frame_delay = av_q2d(is->video_ctx->time_base);
frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
is->video_clock += frame_delay;
return pts;
}
//获取Audio Clock
//Audio Clock,也就是Audio的播放时长,可以在Audio时更新Audio Clock。在函数audio_decode_frame中解码新的packet,这是可以设置Auddio clock为该packet的PTS
if (pkt.pts != AV_NOPTS_VALUE)
{
audio_state->audio_clock = av_q2d(audio_state->stream->time_base) * pkt.pts;
}
//由于一个packet中可以包含多个帧,packet中的PTS比真正的播放的PTS可能会早很多,可以根据Sample Rate 和 Sample Format来计算出该packet中的数据可以播放的时长,再次更新Audio clock 。
// 每秒钟音频播放的字节数 sample_rate * channels * sample_format(一个sample占用的字节数)
audio_state->audio_clock += static_cast<double>(data_size) / (2 * audio_state->stream->codec->channels *
audio_state->stream->codec->sample_rate);
//上面乘以2是因为sample format是16位的无符号整型,占用2个字节。
for(;;)
{
while(is->audio_pkt_size > 0)
{
int got_frame = 0;
len1 = avcodec_decode_audio(is->audio_ctx,&is_audio_frame,&got_frame,pkt);
if(len1 < 0)
{
is->audio_pkt_size = 0;
break;
}
data_size = 0;
if(got_frame)
{
data_size = av_sample_get_buffer_size(NULL,is->audio_ctx->channels,is->audio_frame.nb_samples,is->audio_ctx->sample_fmt,1);
assert(data_size <= buf_size);
memcpy(audio_buf,is->audio_frame.data[0],data_size);
}
is->audio_pkt_data += len1;
is->audio_pkt_size -= len1;
if(data_size <= 0)
continue;
pts = is->audio_clock;
*pts_pts = pts;
n = 2 * is->audio_ctx->channles;
is->audio_clock += (double)data_size / double(n * is->audio_ctx->sample_rate);
return data_size;
}
if(pke->data)
av_free_packet(pkt);
if(is->quit)
{
return -1;
}
//read next packet
if(packet_queue_get(&is_audioqmpkt,1) < 0)
return -1;
is->audio_pkt_data = pkt->data;
is->audio_pkt_size = pkt->size;
if(pkt->pts != AV_NOPTS_VALUE)
{
is->audio_clock = av_q2d(is->audio_st->time_base)* pkt->pts;
}
}
//有了Audio clock后,在外面获取该值的时候却不能直接返回该值,因为audio缓冲区的可能还有未播放的数据,需要减去这部分的时间
double AudioStatue::Get_audip_clock()
{
int hw_buf_size = audio_buff_size - audio_buff_index;
int bytes_pre_sec = steam->codec->sample_rate * audio_ctx->channels * 2;
double pts = (audio_clock - static_cast<double>(hw_buf_size)/bytes_pre_sec;
return pts;
}
//audio缓冲区中剩余的数据除以每秒播放的音频数据得到剩余数据的播放时间,从Audio clock中减去这部分的值就是当前的audio的播放时长。
//同步
1:用当前的pts - 上一播放帧的pts得到一个时延
2:用当前帧的pts和Audio Clock进行比较,来判断视频的播放速度是快了还是慢了
3:根据上一步判断结果,设置播放下一阵的延迟时间
double current_pts = *(double*)video->frame->opaque;
double delay = current_pts - video->frame_last_pts;
if(delay < = 0 || delay >= 1.0)
delay = video->frame_last_delay;
video->frame_last_delay = delay;
video->frame_last_pts = current_pts;
//根据Audio Clock来判断Video播放的快慢
double ref_clock = media->audio->get_audio_clock();
double diff = current_pts - ref_clock;
double threshold = (delay > SYNC_THRESHOLD) ? delay L SYNV_THRESHOLD;
//调整播放下一阵的延迟时间,以实现同步
if(fabs(diff) < NOSYNC_THRESHOLD)
{
if(diff <= -threashold)//慢了
delay = 0;
else if(diff >= threshold)
delay *= 2;
}
video->frame_timer += delay;
double actual_delay = video->frame_timer = static_cast<double>(av_gettime())/1000000.0;
if(actual_delay <= 0.010)
actual_delay = 0.010;
//设置下一帧的播放延迟
schedule_refresh(media,static_cast<int>(actual_delay * 1000 + 0.5));
ffmpeg同步的更多相关文章
- FFmpeg学习6:视音频同步
在上一篇文章中,视频和音频是各自独立播放的,并不同步.本文主要描述了如何以音频的播放时长为基准,将视频同步到音频上以实现视音频的同步播放的.主要有以下几个方面的内容 视音频同步的简单介绍 DTS 和 ...
- ffmpeg转码MPEG2-TS的音视频同步机制分析
http://blog.chinaunix.net/uid-26000296-id-3483782.html 一.FFmpeg忽略了adaptation_field()数据 FFmpeg忽略了包含PC ...
- Ffmpeg和SDL如何同步视频(转)
ong> PTS和DTS 幸运的是,音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面.音频流有采样,视频流有每秒的帧率.然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视 ...
- Ffmpeg和SDL如何同步音频
ong> 同步音頻 现在我们已经有了一个比较像样的播放器.所以让我们看一下还有哪些零碎的东西没处理.上次,我们掩饰了一点同步问题,也就是同步音频到视频而不是其它的同步方式.我们将采用和视频一样的 ...
- [ffmpeg] 多输入滤波同步方式(framesync)
滤波也不总是单一的输入,也存在对多个输入流进行滤波的需求,最常见的就是对视频添加可视水印,水印的组成通常为原视频以及作为水印的图片或者小动画,在ffmpeg中可以使用overlay滤波器进行水印添加. ...
- FFmpeg简易播放器的实现-音视频同步
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- ffmpeg 2.3版本号, 关于ffplay音视频同步的分析
近期学习播放器的一些东西.所以接触了ffmpeg,看源代码的过程中.就想了解一下ffplay是怎么处理音视频同步的,之前仅仅大概知道通过pts来进行同步,但对于怎样实现却不甚了解,所以想借助这个机会, ...
- FFmpeg 入门(6):音频同步
本文转自:FFmpeg 入门(6):音频同步 | www.samirchen.com 音频同步 上一节我们做了将视频同步到音频时钟,这一节我们反过来,将音频同步到视频.首先,我们要实现一个视频时钟来跟 ...
- FFmpeg 入门(5):视频同步
本文转自:FFmpeg 入门(5):视频同步 | www.samirchen.com 视频如何同步 在之前的教程中,我们已经可以开始播放视频了,也已经可以开始播放音频了,但是视频和音频的播放还未同步, ...
随机推荐
- centos7系统初始化
echo "# swappiness=0的时候表示最大限度使用物理内存,然后才是 swap空间" >> /etc/sysctl.conf echo -e "v ...
- Java中线程同步的方法
同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法.在调用该方法前,需要获得内置锁,否则就处于阻塞状态. 注 ...
- js 如何控制文本域输入内容在一定间隔时间段才触发事件查询相关数据
<script>var flag = 0;var t;function openFlag () { t = setTimeout(function(){flag = 1; dosometh ...
- 51Nod 1067:Bash游戏 V2(巴什博弈)
1067 Bash游戏 V2 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注 有一堆石子共有N个.A B两个人轮流拿,A先拿.每次只能拿1,3,4 ...
- CH4302 Interval GCD
题意 4302 Interval GCD 0x40「数据结构进阶」例题 描述 给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一: &qu ...
- JSON字符串-赋张最初接触后台从map转json的方法
**************************************** json数组: *************************************************** ...
- php比较全的友好时间显示
分享一个php友好的比较完成的时间格式化函数,包括‘刚刚’,'几秒之前',‘几分钟前’,'几小时前',几天前,几周前,几个月前等.调用方式很简单,是从ThinkSNS 里面拿出来的. /** * 友好 ...
- php curl curl_getinfo()返回参数详解
php curl请求在curl_exec()函数执行之后,可以使用curl_getinfo()函数获取CURL请求输出的相关信息,示例代码如下: curl_exec($ch);$info = curl ...
- Spark机器配置计算
● Based on the recommendations mentioned above, Let's assign 5 core per executors => --executor-c ...
- linq to sql 项目移植后,数据库实体类需要重新创建?
项目中,使用LINQ to SQL 访问数据库,代码移植到其他机器上,每次需要重新生成dbml文件,有无方法只要更改app.config呢? 经过试验是可行的: 1.引用system.configur ...