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同步的更多相关文章

  1. FFmpeg学习6:视音频同步

    在上一篇文章中,视频和音频是各自独立播放的,并不同步.本文主要描述了如何以音频的播放时长为基准,将视频同步到音频上以实现视音频的同步播放的.主要有以下几个方面的内容 视音频同步的简单介绍 DTS 和 ...

  2. ffmpeg转码MPEG2-TS的音视频同步机制分析

    http://blog.chinaunix.net/uid-26000296-id-3483782.html 一.FFmpeg忽略了adaptation_field()数据 FFmpeg忽略了包含PC ...

  3. Ffmpeg和SDL如何同步视频(转)

    ong> PTS和DTS 幸运的是,音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面.音频流有采样,视频流有每秒的帧率.然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视 ...

  4. Ffmpeg和SDL如何同步音频

    ong> 同步音頻 现在我们已经有了一个比较像样的播放器.所以让我们看一下还有哪些零碎的东西没处理.上次,我们掩饰了一点同步问题,也就是同步音频到视频而不是其它的同步方式.我们将采用和视频一样的 ...

  5. [ffmpeg] 多输入滤波同步方式(framesync)

    滤波也不总是单一的输入,也存在对多个输入流进行滤波的需求,最常见的就是对视频添加可视水印,水印的组成通常为原视频以及作为水印的图片或者小动画,在ffmpeg中可以使用overlay滤波器进行水印添加. ...

  6. FFmpeg简易播放器的实现-音视频同步

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...

  7. ffmpeg 2.3版本号, 关于ffplay音视频同步的分析

    近期学习播放器的一些东西.所以接触了ffmpeg,看源代码的过程中.就想了解一下ffplay是怎么处理音视频同步的,之前仅仅大概知道通过pts来进行同步,但对于怎样实现却不甚了解,所以想借助这个机会, ...

  8. FFmpeg 入门(6):音频同步

    本文转自:FFmpeg 入门(6):音频同步 | www.samirchen.com 音频同步 上一节我们做了将视频同步到音频时钟,这一节我们反过来,将音频同步到视频.首先,我们要实现一个视频时钟来跟 ...

  9. FFmpeg 入门(5):视频同步

    本文转自:FFmpeg 入门(5):视频同步 | www.samirchen.com 视频如何同步 在之前的教程中,我们已经可以开始播放视频了,也已经可以开始播放音频了,但是视频和音频的播放还未同步, ...

随机推荐

  1. Python 动态传参

    def chi(zhushi, cai, fushi, tang, tiandian): print(zhushi,cai,fushi,tang,tiandian) chi("大碗大米饭&q ...

  2. SWIFT Tuple Pattern及Struct Pattern

    定义一个Tuple let color = (1.0,1.0,1.0,1.0) switch color{ case (0.0,0.5...1.0,let blue,_): //匹配第一个值为0.0第 ...

  3. 会声会影X7安装不了,总是提示已经安装其他版本,怎么办

    会声会影X7安装不了,总是提示已经安装其他版本,怎么办 卸载c++2008,安装会声会影,ok. 卸载工具:Windows Install Clean Up

  4. Luogu 3245 大数

    Luogu 3245 大数 开始就想 \(10\) 进制 \(hash\) ,\(Hash(r)\equiv Hash(l-1)\cdot 10^{r-l+1}\) ,感觉没什么美妙的性质啊... 然 ...

  5. 用pthon来写个跳板机

    用pthon来写个跳板机   1.需求 程序一:1.后台管理- 堡垒机上创建用户和密码(堡垒机root封装的类,UserProfile表)- .bashrc /usr/bin/python3 /dat ...

  6. V4L2驱动内核文档翻译(一)

    随着一些视频或者图像硬件的复杂化,V4L2驱动也越来越趋于复杂.许多硬件有多个IC,在/dev下生成多个video设备或者其他的诸如,DVB,ALSA,FB,I2C ,IR等等非V4L2的设备.所以, ...

  7. Json.NET Updates: Merge, Dependency Injection, F# and JSONPath Support

    Json.NET 6.0 received 4 releases this year, the latest last week. Over these releases, several new f ...

  8. event store

    Event Store The documentation has now moved to the wiki in this repository. For a quick start, look  ...

  9. zmediaboard-Hi3518参数及配置

    1.12_13.uboot的烧写和flash分区1_21.12.1.裸机烧录uboot(1)什么叫裸机烧录?设备是空白的,未经烧录的,就叫裸机.(2)裸机烧录一个设备有2种方案:1是用外部烧录器来烧录 ...

  10. Apache+modproxy布置tomcat集群

    一.环境: Apache: 2.2.14: 下载地址:http://archive.apache.org/dist/httpd/binaries/win32/ Tomcat: 7.0.82 JDK1. ...