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. 前端开发 —— js 常用工具函数(utilities)

    1. 时间 function getCurTime() { var date = new Date(); return date.toLocaleTimeString(); } date.toLoca ...

  2. 教你正确进入DFU及恢复模式 无敌重刷模式

    苹果分为两种恢复模式,一种叫做DFU模式,另一种是恢复模式.DFU的全称是Development FirmwareUpgrade,实际意思就是iPhone固件的强制升降级模式.而恢复模式则是屏幕上会显 ...

  3. egret游戏入门之学习资源篇

    最近因需要,入手H5游戏. 写游戏当然需要有引擎. H5游戏开发:游戏引擎入门推荐 如何选择 H5 游戏引擎 白鹭引擎和layabox哪个好用,哪个技术更成熟 ? LayaBox 与 Egret 选择 ...

  4. exec函数簇

    转自:http://www.cppblog.com/prayer/archive/2009/04/15/80077.html 也许有不少读者从本系列文章一推出就开始读,一直到这里还有一个很大的疑惑:既 ...

  5. 通过Jersey客户端API调用REST风格的Web服务

    Jersey 客户端 API 基础 要开始使用 Jersey 客户端 API,你首先需要创建一个 com.sun.jersey .api.client.Client 类的实例.下面是最简单的方法: i ...

  6. USB设备驱动_WDS

    1. usb_alloc_dev中的 bus_type 中指定了匹配函数,和uevent中的环境参数. ====>不同的bus_type的匹配函数可能是不同的,uevent的环境变量参数也可能是 ...

  7. 使用VBS发邮件

    NameSpace = "http://schemas.microsoft.com/cdo/configuration/"set Email = CreateObject(&quo ...

  8. Linux系统Centos安装Python3.7

    Linux下默认系统自带python2.7的版本,这个版本被系统很多程序所依赖,所以不建议删除,如果使用最新的Python3那么我们知道编译安装源码包和系统默认包之间是没有任何影响的,所以可以安装py ...

  9. ML(3.2): NavieBayes R_kalR

    ML3.1 介绍e1071包实施朴素贝叶斯分类的函数,本例使用klaR包中的NaiveBayes函数,因为该函数较之前者增加了两个功能,一个是可以输入先验概率,另一个是在正态分布基础上增加了核平滑密度 ...

  10. 【jmeter】Jmeter启动GUI界面出错

    今天要用Jmeter测试服务器性能,发现GUI界面总是有warning提示: WARNING: Could not open/create prefs root node Software\JavaS ...