音视频同步原理

音视频同步原理及实现 在介绍DTS/PTS之前,我们先了解I/P/B帧的概念。I/P/B帧本身和音视频同步关系不大,但理解其概念有助于我们了解DTS/PTS存在的意义。视频本质上是由一帧帧画面组成,但实际应用过程中,每一帧画面会进行压缩(编码)处理,已达到减少空间占用的目的。编码方式可以分为帧内编码和帧间编码。内编码方式:即只利用了单帧图像内的空间相关性,对冗余数据进行编码,达到压缩效果,而没有利用时间相关性,不使用运动补偿。所以单靠自己,便能完整解码出一帧画面。 阅读详情

音视频同步

更多精彩内容

音频系统概述

在这里插入图片描述

  1. 音频时延payload_delay_ms jitter_delay(neteq)。

  2. 在WebRTC中有neteq,所以,音频的卡顿以及卡顿时长都是放在neteq内部进行计算的。

时间戳

时间戳的概念主要有以下几个:

  • ntp时间戳: NTP时间戳是绝对时间戳

  • 本地时间戳

  • RTP时间戳:RTP时间戳是相对时间戳

ntp时间戳

RTP的标准并没有规定音频、视频流的第一个包必须同时采集、发送,也就是说开始的一小段时间内可能只有音频或者视频,再加上可能的网络丢包,音频或者视频流的开始若干包可能丢失,那么不能简单认为接收端收到的第一个音频包和视频包是对齐的,需要一个共同的时间基准来做时间对齐,这就是NTP时间戳的作用。

NTP时间戳是从1900年1月1日00:00:00以来经过的秒数,发送端以一定的频率发送SR(Sender Report)这个RTCP包,分为视频SR和音频SR,SR包内包含一个RTP时间戳和对应的NTP时间戳,接收端收到后就可以确定某个流的RTP时间戳和NTP时间戳的对应关系,这样音频、视频的时间戳就可以统一到同一个时间基准下。

在这里插入图片描述

如上图,发送端的音视频流并没有对齐,但是周期地发送SR包,接收端得到音视频SR包的RTP时间戳、NTP时间戳后通过线性回归得到NTP时间戳Tntp和RTP时间戳Trtp时间戳的对应关系:

Tntp_audio = f(Trtp_audio)

Tntp_video = f(Trtp_video)

其中Tntp = f(Trtp) = kTrtp + b 为线性函数,这样接收端每收到一个RTP包,都可以将RTP时间戳换算成NTP时间戳,从而在同一时间基准下进行音视频同步。

本地时间戳

从系统启动启动开始计时。

RTP时间戳

RTP时间戳定义了负载数据的采样时刻,描述的是负载数据的帧间顺序

“The timestamp reflects the sampling instant of the first octet in the RTP data packet. The sampling instant must be derived from a clock that increments monotonically and linearly in time to allow synchronization and jitter calculations. The resolution of the clock must be sufficient for the desired synchronization accuracy and for measuring packet arrival jitter (one tick per video frame is typically not sufficient). ”

也就是说,RTP时间戳是从单调线性递增的时钟里面获得的,时钟的精度由采样频率决定,视频的采样一般为90kZ,时间戳增加1,实际时间增加1/90000s

等价于,本地时间增加1,采样的时间增加90000s。

在WebRTC内部,ntp时间的计算过程如下:

// Capture time may come from clock with an offset and drift from clock_.
int64_t capture_ntp_time_ms;
if (video_frame.ntp_time_ms() > 0) { //值为0,不会进入
  capture_ntp_time_ms = video_frame.ntp_time_ms();
} else if (video_frame.render_time_ms() != 0) {//render_time_ms由timestamp_us_换算过来,本地时间。在采集的时候已经赋值
  capture_ntp_time_ms = video_frame.render_time_ms() + delta_ntp_internal_ms_;
} else {
  capture_ntp_time_ms = current_time_ms + delta_ntp_internal_ms_;
}
incoming_frame.set_ntp_time_ms(capture_ntp_time_ms);
delta_ntp_internal_ms_(clock_->CurrentNtpInMilliseconds() - clock_->TimeInMilliseconds())
// Convert NTP time, in ms, to RTP timestamp.
const int kMsToRtpTimestamp = 90;
incoming_frame.set_timestamp(
    kMsToRtpTimestamp * static_cast<uint32_t>(incoming_frame.ntp_time_ms()));

重上述可以知道,ntp时间是从本地时间戳线性变化过来的,rtp时间戳是ntp时间戳线性变换过来的,所以ntp时间戳和rtp时间戳也存在线性变化的关系。ntp和本地时间戳本质上是一致的,只不过是在不同刻度下的表现。NTP时间和RTP时间戳是同一时刻的不同表示,区别在于精度不同。NTP时间是绝对时间,以毫秒为精度,而RTP时间戳则和媒体的采样频率有关。

音视频目标

不需要做到音视频绝对的同步,但是需要在一定的阈值内才能不被人所感知。

+ 代表声音比视频超前

(-100ms, +25ms) 无法感知

(-125ms, +45ms) 可以感知

(-, -185)U(+90, +) 影响体验

音频延迟于视频的阈值,相比于音频提前于视频的阈值更高。这个主要原因是人眼总是先看到光然后再听到声音,我们逐渐形成了这样的一个习惯。我们在理解信息的时候,也总是先视觉再听觉。如果声音先于视频较多,那么我们可能就无法将声音和画面对应上来,很容易感觉到音视频不同步。

不同步建模

音视频不同步的根本原因在于,音视频的传输通过的是不同的数据流,不同数据流之间是独立传输与处理的,拥有独立的时间戳。在不做任何处理的情况下,各自处理的延迟差异也会导致不完全同步。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tSPeISdk-1669986678991)(!%5B%5D%28https://zhongsy.oss-cn-beijing.aliyuncs.com/img/%28null%29-20221202205752737.png%29#pic_center)]

如图所示,如果要处理音视频同步(同步的本质是,接收到数据后,根据数据能够计算出数据的需要的pts)。就需要处理好以下几个:

  • 获取接收端音视频数据绝对的ntp时间差。

  • 获取发送端音视频采集绝对的NTP时间差。

  • 音视频渲染缓冲时间delay。

在webRTC中衡量音视频的不同步的统计量做syncdiff(含义:在不做同步的情况下,音视频不同步的情况)播放时间差 - 采集时间差。

不同步 = 采集到接收组帧后的不同步 + JItterBuffer delay + 音视频渲染时长(渲染时长是计算再jitterbuffer的jitter中的,所以可以和JitterBuffer delay合并)

// Calculate the difference between the lowest possible video delay and the
  // current audio delay.
  // 视频的jitter buffer当前延迟 -音频的jitter buffer当前延迟 + 相对延迟
  int current_diff_ms =current_video_delay_ms - current_audio_delay_ms + relative_delay_ms;

RelativeDelay相对时延,表示如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjtDEyRR-1669986028837)(https://zhongsy.oss-cn-beijing.aliyuncs.com/img/(null)]-20221202205800266.(null))

相对时延 = 接收时间差 - 采集时间差。

在webRTC中的rtp包中只能拿到rtp时间戳,但是接收端只能获取到接收端的本地时间戳,单位是ms,rtp的单位是采样步长,所以并不能直接使用,所以需要将rtp时间戳,转换成发送端为ms的单位,这时候就有了SR包,在接收端将数据包rtp时间戳映射为绝对的NTP时间戳。恰好RTCP SR的一个包里面有携带rtp timestamp和对应的NTP时间,SR包按照周期发送,接收端完全可以根据这些SR拟合出RTP timestamp和NTP timestamp的关系。这个转换由RtpToNtpEstimator完成。(这里其实并不关系发送时延)。

$$ 相 对 延 迟 = ( 视 频 帧 接 收 时 间 − 音 频 帧 接 收 时 间 ) − ( 视 频 帧 采 集 时 间 − 音 频 帧 采 集 时 间 ) 相对延迟 = (视频帧接收时间 - 音频帧接收时间) - (视频帧采集时间- 音频帧采集时间) 相对延迟=(视频帧接收时间−音频帧接收时间)−(视频帧采集时间−音频帧采集时间)$$

可以看出,在webRTC中,都是将时间戳换算为发送端的NTP时间戳,再计算relativeDelay。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G5CcNEhL-1669986028838)(https://zhongsy.oss-cn-beijing.aliyuncs.com/img/(null)]-20221202205805271.(null))

​ ntp转ntp

音视频同步原理

如果要做音视频同步,需要知道以下几个参数:

  • 音视频相对延迟

  • 期望视频目标延迟,其实就是jitterbuffer里面计算出来的jitter

  • 期望音频目标延迟

有了上述的参数以后,剩下的就是同步策略:

• 如果视频相对音频存在播放延迟:如果视频已经存在延迟,减小视频播放延迟,通过快放追上音频;如果视频没有延迟,无法再降低,增加音频延迟,让音频慢放等待视频。

• 如果音频相对视频存在播放延迟:如果音频已经存在延迟,减小音频播放延迟,通过快放追上音频;如果音频没有延迟,无法再降低,增加视频延迟,让视频慢放等待音频。

bool StreamSynchronization::ComputeDelays(int relative_delay_ms,
                                          int current_audio_delay_ms,
                                          int* total_audio_delay_target_ms,
                                          int* total_video_delay_target_ms) {
  int current_video_delay_ms = *total_video_delay_target_ms;

  RTC_LOG(LS_VERBOSE) << "Audio delay: " << current_audio_delay_ms
                      << " current diff: " << relative_delay_ms
                      << " for stream " << audio_stream_id_;

  // Calculate the difference between the lowest possible video delay and the
  // current audio delay.
  int current_diff_ms =
      current_video_delay_ms - current_audio_delay_ms + relative_delay_ms;
// 1. 做平滑处理
  avg_diff_ms_ =
      ((kFilterLength - 1) * avg_diff_ms_ + current_diff_ms) / kFilterLength;
  if (abs(avg_diff_ms_) < kMinDeltaMs) {
    // Don't adjust if the diff is within our margin.
    return false;
  }

  // Make sure we don't move too fast.
  // 2.控制步长,单次不超过80ms
  int diff_ms = avg_diff_ms_ / 2;
  diff_ms = std::min(diff_ms, kMaxChangeMs);
  diff_ms = std::max(diff_ms, -kMaxChangeMs);

  // Reset the average after a move to prevent overshooting reaction.
  avg_diff_ms_ = 0;
  // 3. diff_ms大于0,代表的是视频落后音频,视频相对于音频有延迟
  if (diff_ms > 0) {
    // The minimum video delay is longer than the current audio delay.
    // We need to decrease extra video delay, or add extra audio delay.
    // 视频已经有额外的延迟了,将视频的额外延迟减少,加快视频播放速度
    if (video_delay_.extra_ms > base_target_delay_ms_) {
      // We have extra delay added to ViE. Reduce this delay before adding
      // extra delay to VoE.
      video_delay_.extra_ms -= diff_ms;
      audio_delay_.extra_ms = base_target_delay_ms_;
    } else {  // video_delay_.extra_ms > 0
       // 没有额外延迟,增加音频的延迟,让音频等一下视频
      // We have no extra video delay to remove, increase the audio delay.
      audio_delay_.extra_ms += diff_ms;
      video_delay_.extra_ms = base_target_delay_ms_;
    }
  } else {  // if (diff_ms > 0)
     // 4. diff_ms < 0代表视频超过音频,也就是音频相对于视频有延迟
    // The video delay is lower than the current audio delay.
    // We need to decrease extra audio delay, or add extra video delay.
    // 音频为了音视频同步有额外延迟,降低低频延迟,此时diff_ms < 0
    if (audio_delay_.extra_ms > base_target_delay_ms_) {
      // We have extra delay in VoiceEngine.
      // Start with decreasing the voice delay.
      // Note: diff_ms is negative; add the negative difference.
      audio_delay_.extra_ms += diff_ms;
      video_delay_.extra_ms = base_target_delay_ms_;
    } else {  // audio_delay_.extra_ms > base_target_delay_ms_
       // 如果音频没有额外延迟,增大视频言延迟,让视频等一下
      // We have no extra delay in VoiceEngine, increase the video delay.
      // Note: diff_ms is negative; subtract the negative difference.
      video_delay_.extra_ms -= diff_ms;  // X - (-Y) = X + Y.
      audio_delay_.extra_ms = base_target_delay_ms_;
    }
  }

  // Make sure that video is never below our target.
  video_delay_.extra_ms =
      std::max(video_delay_.extra_ms, base_target_delay_ms_);

  int new_video_delay_ms;
  if (video_delay_.extra_ms > base_target_delay_ms_) {
    new_video_delay_ms = video_delay_.extra_ms;
  } else {
    // No change to the extra video delay. We are changing audio and we only
    // allow to change one at the time.
    new_video_delay_ms = video_delay_.last_ms;
  }

  // Make sure that we don't go below the extra video delay.
  new_video_delay_ms = std::max(new_video_delay_ms, video_delay_.extra_ms);

  // Verify we don't go above the maximum allowed video delay.
  new_video_delay_ms =
      std::min(new_video_delay_ms, base_target_delay_ms_ + kMaxDeltaDelayMs);

  int new_audio_delay_ms;
  if (audio_delay_.extra_ms > base_target_delay_ms_) {
    new_audio_delay_ms = audio_delay_.extra_ms;
  } else {
    // No change to the audio delay. We are changing video and we only allow to
    // change one at the time.
    new_audio_delay_ms = audio_delay_.last_ms;
  }

  // Make sure that we don't go below the extra audio delay.
  new_audio_delay_ms = std::max(new_audio_delay_ms, audio_delay_.extra_ms);

  // Verify we don't go above the maximum allowed audio delay.
  new_audio_delay_ms =
      std::min(new_audio_delay_ms, base_target_delay_ms_ + kMaxDeltaDelayMs);

  video_delay_.last_ms = new_video_delay_ms;
  audio_delay_.last_ms = new_audio_delay_ms;

  RTC_LOG(LS_VERBOSE) << "Sync video delay " << new_video_delay_ms
                      << " for video stream " << video_stream_id_
                      << " and audio delay " << audio_delay_.extra_ms
                      << " for audio stream " << audio_stream_id_;

  *total_video_delay_target_ms = new_video_delay_ms;
  *total_audio_delay_target_ms = new_audio_delay_ms;
  return true;
}

这里的extra_ms一开始是0,过程中会慢慢收敛。extra_ms的含义是音视频同步到底增加了多少的延迟,这里会不断累计,通过音画同步引入的“额外”延迟最终还需要恢复。extra_ms的作用在于此。

base_target_delay_ms_是基准,默认为0.

最后将目标延迟设置进入jitterbuffer

  if (!syncable_audio_->SetMinimumPlayoutDelay(target_audio_delay_ms)) {
    sync_->ReduceAudioDelay();
  }
  if (!syncable_video_->SetMinimumPlayoutDelay(target_video_delay_ms)) {
    sync_->ReduceVideoDelay();
  }
学习笔记:RTP/RTCP的音视频同步原理总结 参考文章::https://blog.youkuaiyun.com/lincaig/article/details/81209895https://www.cnblogs.com/x_wukong/p/5876645.html https://blog.youkuaiyun.com/wxl1986622/article/details/52758022?utm_medium=distribute.pc_relevan... 阅读详情

相关推荐

FFmpeg播放器-音视频同步原理

一、为什么要音视频同步 在播放器的开发中,音频和视频都是在各自独立的线程中解码播放的,理论上如果按照各自的帧率进行播放音视频的话是可以同步的,但实际由于受各种因素的影响,音频和视频会慢慢出现不同步的状况,所以解决音视频的同步非常重要 二、音视频同步的方式 音视频的不同方法一般有三种方式可以选择,如下: 1.参考一个外部时钟,将音频与视频同步至此时间 2.以视频为基准,音频去同步视频...

相忘于江湖 1502

音视频同步基础知识

音视频同步基础知识"

u012117034的博客 1万+

WebRTC音视频同步原理与实现详解(上)

音视频同步是指在播放音视频时,音频和视频的播放进度保持一致,不会出现音频和视频不同步。音视频同步一般有以下三种方法:1)音频同步到视频2)视频同步到音频3)音视频均同步到系统时间对于WebRTC中音频流与视频流互相独立的,在播放时尽量让同时刻采集的音频帧和视频帧,在接收端上的同一时刻去播放和显示,才能保证同步。

brrdg_sefg的博客 1325

音视频同步(播放)原理

1:音视频同步原理 每一帧音频或视频都有一个持续时间:duration: 采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。 。正常人听觉的频率范围大约在20Hz~20kHz之间,根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。常用的音频采样频率有8kHz、 11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48k

朱韦刚(zhuweigangzwg)的技术博客 3万+

WebRTC音视频同步原理与实践

在建立连接的过程中,双方需要交换一些关键信息,如ICE(Interactive Connectivity Establishment)候选地址,以便在网络中找到彼此。RTCP是RTP的控制协议,它负责在通信双方之间传递控制信息,如媒体流的同步、流量控制等。接收方收到RTCP数据包后,可以了解发送方的状态,并根据时间戳信息将接收到的帧按正确的顺序播放,从而实现音视频同步。接收方在接收到数据包后,会根据时间戳信息将帧按正确的顺序播放,从而保证音视频的同步。传输协议,主要用于实时数据的传输,如音频和视频流。

liveweb888的博客 644

FFmpeg音视频同步原理与实现

一、原理 如果简单的按照音频的采样率与视频的帧率去播放,由于机器运行速度,解码效率等种种造成时间差异的因素影响,很难同步,音视频时间差将会呈现线性增长。所以要做音视频的同步,有三种方式: 参考一个外部时钟,将音频与视频同步至此时间。我首先想到这种方式,但是并不好,由于某些生物学的原理,人对声音的变化比较敏感,但是对视觉变化不太敏感。所以频繁的去调整声音的播放会有些刺耳或者杂音吧影响用户体验。(ps:顺便科普生物学知识,自我感觉好高大上_)。 以视频为基准,音频去同步视频的时间。不采用,理由

yinshipin007的博客 1206

浅析音视频同步原理

音视频同步 今天我们来讲解一下音视频同步吧。讲完这篇,结合之前几篇博客,我们对音视频开发也算有一个入门级别的理解了。 首先思考几个问题: 1、为什么需要音视频同步? 因为视频和音频是两个独立的任务在运行,视频和音频的解码速度也不一样,解码出来的数据也不一定马上就可以显示出来。 视频:帧率,表示视频一秒显示的帧数。 音频:采样率,表示音频一秒播放的样本的个数。 从帧率及采样率,即可知道视频/音频播放速度。声卡和显卡均是以一帧数据来作为播放单位,如果单纯依赖帧率及采样率来进行播放,在理想条件下,应该是同步的

weixin_37873786的博客 528

字节跳动-谈下音视频同步原理,音频和视频能绝对同步吗?

面试官: 谈下音视频同步原理,音频和视频能绝对同步吗 心理分析:音视频同步本身比较难,一般使用ijkplayer 第三方做音视频同步。不排除有视频直播 视频通话需要用音视频同步,可以从三种 音频为准 视频为准 自定义时钟为准三种方式实现音视频同步 **求职者: **如果被问到 放正心态,能回答多少是多少。如果你看了这篇文章肯定是可以回答上的 音视频的直播系统是一个复杂的工程系统,要做到非常低延迟的直播,需要复杂的系统工程优化和对各组件非常熟悉的掌握。下面整理几个简单常用的调优技巧: 以f.

鸿蒙开发知识记录 1713

音视频同步原理解析;音频编码和解码原理

音视频同步原理解析;音频编码和解码原理 作者:yuyin86 视频流中的DTS/PTS到底是什么? DTS(解码时间戳)和PTS(显示时间戳)分别是解码器进行解码和显示帧时相对于SCR(系统参考)的时间戳。SCR可以理解为解码器应该开始从磁盘读取数据时的时间。 mpeg文件中的每一个包都有一个SCR时间戳并且这个时间戳就是读取这个数据包时的系统时间。通常情况下,解码器会在它开始

凌风探梅的专栏 7379

音视频时间戳获取与同步原理详解

音视频时间戳获取与同步原理详解

世界那么大,我想去看看~【随心更新,看世界去了😄】 2136

RI-CLPM vs CLPM:什么时候该用随机截距交叉滞后模型?

本文深入对比了交叉滞后模型(CLPM)与随机截距交叉滞后模型(RI-CLPM)的核心差异与应用场景。RI-CLPM通过分离个体间稳定特质与个体内动态波动,能更纯净地估计变量间的动态因果关系,避免CLPM可能存在的混淆。文章提供了清晰的模型选择决策框架,并辅以Mplus语法示例,帮助研究者根据变量特质稳定性与研究问题,正确选用模型以确保结论的严谨性。

weixin_29276847的博客 304
上一篇: CMake变量可见性学习
下一篇: Android Studio引用AAR的方式
黑猫爱小鹿
博客等级 码龄8年 1万+粉丝 205原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值