webrtc jitter buffer

 一、jitter buffer 介绍
 二、jitter 估计
 三、buffer 处理 rtp 包逻辑
 四、接收和解码流程
 五、FrameBuffer 类介绍


/*
 ********************************************************************************
 一、jitter buffer 介绍

 jitter buffer 抖动缓冲区
   当网络不稳定时(发生抖动),增加buffer的长度,多缓存一些数据,以应对将来可能发生的抖动。
   它对数据包丢失、乱序、延迟到达等情况进行处理,平滑的向解码模块输出数据包/帧,
 抵抗各种弱网情况对播放/渲染造成的影响,降低卡顿,提高用户体验。

 视频从采集到渲染的流程如下:

 采集 -> 编码 -> 分包 -> 发送 ----------------> 网络
 渲染 <- 解码 <- Jitter Buffer <- 组帧 <- 接收 <-|

 ********************************************************************************
 */

/*
 ********************************************************************************
 二、jitter 估计
 
 jitter 就是一种抖动。
 RTP数据包从源地址发送到目的地址,会发生不一样的延迟,这样的延迟变动就是 jitter
 jitter 会让音视频的播放不稳定,如音频的颤音、视频的忽快忽慢
 解决 jitter 的方法是增加延时,这个延时称为抖动延迟(jitter delay)

 抖动延迟由网络延迟、解码延迟、渲染延迟构成。解码、渲染延迟比较稳定,网络抖动延迟是动态变化的。
 webrtc 认为网络抖动延迟由两部分构成:
 1、网络噪声带来的抖动延迟(网络排队延迟)
 2、传输大的视频帧(特别是关键帧)对网络造成冲击带来的抖动延迟
   (计算信道速率,根据信道速率计算大的视频帧对网络冲击带来的延迟)

 webrtc 使用卡尔曼滤波(kalman) 估算网络排队延迟和信道速率

 modules\video_coding\jitter_estimator.cc

 1、更新 jitter 估计
 void VCMJitterEstimator::UpdateEstimate(
   int64_t frameDelayMS, uint32_t frameSizeBytes, bool incompleteFrame = false);
 
 其中 frameDelayMS 帧间延迟,指的是一帧数据因为分包和网络传输所造成的延时。
 frameSizeBytes 指当前数据帧大小, incompleteFrame 指是否为完整的帧。

 1.1、噪声阈值计算
 VCMJitterEstimator::NoiseThreshold()
   double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;

 1.2、计算 jitter 估计值
 VCMJitterEstimator::CalculateEstimate()
   double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
 
 即
 jitterDelay = _theta[0] * (_maxFrameSize - _avgFrameSize)
               + _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset; 
 
 其中:
 _theta[0] 为信道传输速率的倒数
 _maxFrameSize 自会话开始以来所收到的最大帧大小
 _avgFrameSize 平均帧大小
 _noiseStdDevs 表示噪声系数,值为2.33
 _varNoise 表示噪声方差,默认值为4.0 EstimateRandomJitter()中会不断更新该值
 _noiseStdDevOffset 为噪声扣除常数,值为30.0

 2、获取 jitter 估计
 返回以毫秒为单位的当前抖动估计,并在重传情况下添加一个RTT相关项
 int VCMJitterEstimator::GetJitterEstimate(
   double rttMultiplier, absl::optional rttMultAddCapMs)
 其中: rttMultiplier 为RTT参数乘数

 参考: http://www.ctiforum.com/news/guonei/512085.html

 ********************************************************************************
 */

/*
 ********************************************************************************
 三、buffer 处理 rtp 包逻辑

 modules\video_coding\jitter_buffer.h

 buffer 接收 rtp 包的处理逻辑主要使用到以下三个队列
 class VCMJitterBuffer {
   UnorderedFrameList free_frames_ RTC_GUARDED_BY(crit_sect_);
   FrameList decodable_frames_ RTC_GUARDED_BY(crit_sect_);
   FrameList incomplete_frames_ RTC_GUARDED_BY(crit_sect_);
 }
 free_frames_ 队列用于管理空的frame,弹出空的frame来存放rtp包,解码完成后的frame重置后再次存入该队列。
 incomplete_frames_ 队列用于存放尚未完整的frame,当frame完整时将其push到 decodable_frames_
 decodable_frames_ 队列用于存放完整的可以解码的frame
 
 1、第一次接收到一个rtp视频包,从 free_frames_ 队列中弹出一个空 frame 块,用来放置这个包。
    之后每次接收一个rtp包,根据时间戳在 incomplete_frames_ 和 decodable_frames_ 
    中寻找,看是否已经接收到过相同时间戳的包,如果找到则弹出该 frame 块。
    否则,从 free_frames_ 中弹出一个空 frame
 2、根据包的序列号,找到应该插入 frame 的位置将包插入,并更新 frame 的 state (frame中存放多个rtp包)
    其中 state 的状态为 
    enum VCMFrameBufferStateEnum {
      kStateEmpty,       // frame popped by the RTP receiver
      kStateIncomplete,  // frame that have one or more packet(s) stored
      kStateComplete,    // frame that have all packets
    };
 3、根据不同的 buffer_state 将 frame 帧 push 回到队列中。
    如果 buffer_state 为 kCompleteSession 并且 frame 已经在 decodable list (continuous 为 true)
    将 frame push 到 decodable_frames_ 队列 decodable_frames_.InsertFrame(frame);

    如果 buffer_state 为 kCompleteSession 或 kIncomplete
    将 frame push 到 incomplete_frames_ 队列 incomplete_frames_.InsertFrame(frame);

    如果 buffer_state 为 kNoError 或 kOutOfBoundsPacket 或 kDuplicatePacket
    将 frame push 到 frame_list 队列 frame_list->InsertFrame(frame);

    VCMJitterBuffer::InsertPacket -> VCMJitterBuffer::FindAndInsertContinuousFramesWithState() 
    将 incomplete_frames_ 队列中的 frame push 到 decodable_frames_ 队列
 
 4、free_frames_ 队列初始化大小为 kStartNumberOfFrames = 6 (在构造函数 VCMJitterBuffer() 中初始化)
    如果 free_frames_ 队列为空时,增加队列大小,最大值为 kMaxNumberOfFrames = 300
    定期从 incomplete_frames_ 和 decodable_frames_ 队列中清除一些过时的frame 
    push 到 free_frames_ 队列 VCMJitterBuffer::RecycleFramesUntilKeyFrame()
 
 5、解码线程取出 frame 并解码完成后,将 frame 重置并 push 到 free_frames_ 队列
    VideoReceiver::Decode() -> VCMReceiver::ReleaseFrame() -> VCMJitterBuffer::ReleaseFrame()
    -> VCMJitterBuffer::RecycleFrameBuffer() -> frame->Reset()/free_frames_.push_back(frame)

 ********************************************************************************
 */


/*
 ********************************************************************************
 四、接收和解码流程

 webrtc jitter buffer 被两个线程操作
 (1) 写线程负责组帧之后把数据写入 jitter buffer 里
 (2) 读线程负责从 jitter buffer 里读取数据然后解码

 1、接收数据
 1.1、读线程 创建流程
 WebRtcVideoChannel::WebRtcVideoReceiveStream::WebRtcVideoReceiveStream()
 -> WebRtcVideoChannel::WebRtcVideoReceiveStream::RecreateWebRtcVideoStream()
    [调用 stream_ = call_->CreateVideoReceiveStream(std::move(config));
     调用 stream_->Start()
     其中 stream_ 类型为 webrtc::VideoReceiveStream*
    ]
    [Call::CreateVideoReceiveStream()
     VideoReceiveStream::Start()
    ]
 -> webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream()
    [调用构造函数 
     VideoReceiveStream::VideoReceiveStream(module_process_thread_.get())
     其中 Call::Call() 设置为 module_process_thread_ 为 ModuleProcessThread 线程
    ]
    [Call::Create(const Call::Config& config)
        [调用 Create(config, Clock::GetRealTimeClock(),
                    ProcessThread::Create("ModuleProcessThread"),
                    ProcessThread::Create("PacerThread"));
         创建两个线程: ModuleProcessThread 和 PacerThread
        ]
     -> Call::Create()
        [调用 new internal::Call()]
     -> Call::Call()
        [其中设置 module_process_thread_ 为 ModuleProcessThread 线程]
    ]
    [VideoReceiveStream::VideoReceiveStream()
        [设置 rtp_stream_sync_(this) 即 rtp_stream_sync_ 为 VideoReceiveStream 类型
         设置 process_thread_ 为 ModuleProcessThread 线程
         调用 process_thread_->RegisterModule(&rtp_stream_sync_, RTC_FROM_HERE);
        ]
    ]
 -> VideoReceiveStream::Start()
 -> VideoReceiveStream::StartNextDecode()
    [将数据帧从 jitter buffer 中取出
     调用 frame_buffer_->NextFrame()
    ]

 1.2、写线程 创建流程
 VideoReceiveStream::VideoReceiveStream()
    [创建 rtp_video_stream_receiver_(process_thread_)
     其中 rtp_video_stream_receiver_ 类型为 RtpVideoStreamReceiver
     process_thread_ 为 ModuleProcessThread 线程
    ]
 -> RtpVideoStreamReceiver::RtpVideoStreamReceiver()
    [设置 process_thread_ 为 ModuleProcessThread 线程]
 -> RtpVideoStreamReceiver::OnCompleteFrame() [从网络接收到RTP数据]
 -> VideoReceiveStream::OnCompleteFrame()
    [调用 frame_buffer_->InsertFrame()]
 -> FrameBuffer::InsertFrame() [将数据帧存放到 jitter buffer ]


 2、解码流程 
 // 创建一个工作线程,用于解码数据帧
 // 从 frame_buffer_ 中获取数据帧进行解码
 VideoStreamDecoderImpl::VideoStreamDecoderImpl()
    [创建线程 PlatformThread 一个简单的工作线程
     decode_thread_(&DecodeLoop,
                    this,
                    "video_stream_decoder_decode_thread",
                    rtc::kHighestPriority)
     启动线程 decode_thread_.Start()
    ]
 -> VideoStreamDecoderImpl::DecodeLoop(void* ptr)
    [auto* vs_decoder = static_cast(ptr);
     启用循环 while (true)
     DecodeResult decode_result =
        vs_decoder->DecodeNextFrame(max_wait_time_ms, keyframe_required);
     如果 decode_result 为 kShutdown 则返回 
    ]
 -> VideoStreamDecoderImpl::DecodeNextFrame()
    [调用 frame_buffer_.NextFrame()
     VideoDecoder* decoder = GetDecoder(frame->PayloadType());
     decoder->Decode()
    ]
    [FrameBuffer::NextFrame()
     VideoStreamDecoderImpl::GetDecoder()
     VideoDecoder::Decode() 纯虚函数
     [视频编码方式(h264 vp8 vp9),下面以vp8为例]
    ]
 -> LibvpxVp8Decoder::Decode()

 // 创建一个任务队列,在上面的工作线程中以异步的方式执行任务
 // 将 frame 存放到 frame_buffer_
 VideoStreamDecoderImpl::VideoStreamDecoderImpl()
    [创建任务队列 bookkeeping_queue_(task_queue_factory->CreateTaskQueue())]
 VideoStreamDecoderImpl::OnFrame()
    [如果 bookkeeping_queue_.IsCurrent() 为 false
     调用 bookkeeping_queue_.PostTask(
            std::make_unique(std::move(frame), this));
     其中 OnFrameTask::Run() 调用 
     video_stream_decoder_->OnFrame() 即 VideoStreamDecoderImpl::OnFrame()
    ]
    [调用 frame_buffer_.InsertFrame(std::move(frame))]

 ********************************************************************************
 */


/*
 ********************************************************************************
 五、FrameBuffer 类介绍
 ********************************************************************************
 */

/**
 * jitter buffer实现 frame_buffer2.cc 
 * class FrameBuffer
 * 
*/

FrameBuffer(Clock* clock,
            VCMTiming* timing,
            VCMReceiveStatisticsCallback* stats_callback);

// Insert a frame into the frame buffer. Returns the picture id
// of the last continuous frame or -1 if there is no continuous frame.
int64_t InsertFrame(std::unique_ptr frame);

// Get the next frame for decoding. Will return at latest after
// |max_wait_time_ms|.
//  - If a frame is available within |max_wait_time_ms| it will return
//    kFrameFound and set |frame_out| to the resulting frame.
//  - If no frame is available after |max_wait_time_ms| it will return
//    kTimeout.
//  - If the FrameBuffer is stopped then it will return kStopped.
ReturnReason NextFrame(int64_t max_wait_time_ms,
                       std::unique_ptr* frame_out,
                       bool keyframe_required);
void NextFrame(
    int64_t max_wait_time_ms,
    bool keyframe_required,
    rtc::TaskQueue* callback_queue,
    std::function, ReturnReason)> handler);

// Tells the FrameBuffer which protection mode that is in use. Affects
// the frame timing. (影响帧定时)
void SetProtectionMode(VCMVideoProtection mode);

// Start the frame buffer, has no effect if the frame buffer is started.
// The frame buffer is started upon construction.
void Start();

// Stop the frame buffer, causing any sleeping thread in NextFrame to
// return immediately.
void Stop();

// Updates the RTT for jitter buffer estimation.
void UpdateRtt(int64_t rtt_ms);

// Clears the FrameBuffer, removing all the buffered frames.
void Clear();

class FrameBuffer {
 public:

 private:
  
  FrameMap frames_;  // 只存储未解码的帧
  int64_t latest_return_time_ms_;  // 下次返回帧的时间
};


void FrameBuffer::NextFrame(
    int64_t max_wait_time_ms,
    bool keyframe_required,
    rtc::TaskQueue* callback_queue,
    std::function, ReturnReason)> handler) {
  // 
  int64_t latest_return_time_ms =
      clock_->TimeInMilliseconds() + max_wait_time_ms;

  latest_return_time_ms_ = latest_return_time_ms;
  keyframe_required_ = keyframe_required;
  frame_handler_ = handler;
  callback_queue_ = callback_queue;
  StartWaitForNextFrameOnQueue();
}


void FrameBuffer::StartWaitForNextFrameOnQueue() {
  // 查找下一个待解码的帧存放到 frames_to_decode_ 并返回最大等待时间
  int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds());

  // 启动一个重复任务
  // 如果在等待时间内有可以解码的帧( frames_to_decode_ 不为空)
  // 则调用 frame_handler_ 处理帧
  callback_task_ = RepeatingTaskHandle::DelayedStart(
      callback_queue_->Get(), TimeDelta::ms(wait_ms), [this] {
        // 如果这个任务没有被取消,我们在等待时没有得到任何新的帧,
        // 则继续 delivery frame
        if (!frames_to_decode_.empty()) {
          // 还有 frame 继续 deliver
          frame_handler_(absl::WrapUnique(GetNextFrame()), kFrameFound);
          CancelCallback();
          return TimeDelta::Zero();  // Ignored.
        } else if (clock_->TimeInMilliseconds() >= latest_return_time_ms_) {
          // 超时,发送信号并停止重复任务
          frame_handler_(nullptr, kTimeout);
          CancelCallback();
          return TimeDelta::Zero();  // Ignored.
        } else {
          // 如果没有帧用于解码,并且还有时间
          // 这意味着在创建和执行此任务之间清除了帧缓冲区
          // 继续等待剩余时间
          int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds());
          return TimeDelta::ms(wait_ms);
        }
      });
}


/**
 * 从未解码的帧 frames_ 中查找 superframe 及其剩余帧,并将其存放到 frames_to_decode_
 * 计算帧的渲染时间戳和最大需要等待的时间
 * 返回等待时间
*/
int64_t FrameBuffer::FindNextFrame(int64_t now_ms) {
  // 根据下次返回帧的时间和当前时间计算等待时间
  int64_t wait_ms = latest_return_time_ms_ - now_ms;
  // 清理容器
  frames_to_decode_.clear();

  // 遍历未解码的帧 frames_
  for (auto frame_it = frames_.begin();
       frame_it != frames_.end() && frame_it->first <= last_continuous_frame_;
       ++frame_it) {
    if (!frame_it->second.continuous ||
        frame_it->second.num_missing_decodable > 0) {
      continue;
    }

    // 获取已编码的帧
    EncodedFrame* frame = frame_it->second.frame.get();

    // 如果需要关键帧,但当前帧不是关键帧,则跳过
    if (keyframe_required_ && !frame->is_keyframe())
      continue;

    // 只会返回 superframe 的所有部分
    // 因此如果不是 superframe 的开始,则跳过
    if (frame->inter_layer_predicted) {
      continue;
    }

    // 收集同一 superframe 的所有剩余帧 
    std::vector current_superframe;
    current_superframe.push_back(frame_it);

    // 判断下一个帧是否和当前帧为同一 superframe
    // 如果是,则将其存放到 current_superframe
    bool last_layer_completed = frame_it->second.frame->is_last_spatial_layer;
    FrameMap::iterator next_frame_it = frame_it;

    // 将同一 superframe 的所有帧存放到 current_superframe
    while (true) {
      ++next_frame_it;
      if (next_frame_it == frames_.end() ||
          next_frame_it->first.picture_id != frame->id.picture_id ||
          !next_frame_it->second.continuous) {
        break;
      }
      // Check if the next frame has some undecoded references other than
      // the previous frame in the same superframe.
      size_t num_allowed_undecoded_refs =
          (next_frame_it->second.frame->inter_layer_predicted) ? 1 : 0;
      if (next_frame_it->second.num_missing_decodable >
          num_allowed_undecoded_refs) {
        break;
      }
      // All frames in the superframe should have the same timestamp.
      if (frame->Timestamp() != next_frame_it->second.frame->Timestamp()) {
        RTC_LOG(LS_WARNING) << "Frames in a single superframe have different"
                               " timestamps. Skipping undecodable superframe.";
        break;
      }
      current_superframe.push_back(next_frame_it);
      last_layer_completed = next_frame_it->second.frame->is_last_spatial_layer;
    }
    
    // 检查当前的 superframe 是否完整 
    if (!last_layer_completed) {
      continue;
    }

    // 将当前 superframe 及其剩下的所有帧传递给 frames_to_decode_
    frames_to_decode_ = std::move(current_superframe);

    // 如果帧的渲染时间戳无效
    // 根据当前时间和帧的时间戳计算帧的渲染时间戳,并保存到帧信息中
    if (frame->RenderTime() == -1) {
      frame->SetRenderTime(timing_->RenderTimeMs(frame->Timestamp(), now_ms));
    }
    // 根据帧的渲染时间戳和当前时间计算最大需要等待的时间
    wait_ms = timing_->MaxWaitingTime(frame->RenderTime(), now_ms);

    break;
  }

  // 更新等待时间
  wait_ms = std::min(wait_ms, latest_return_time_ms_ - now_ms);
  wait_ms = std::max(wait_ms, 0);
  return wait_ms;
}


/**
 * 根据首帧的时间戳和帧的接收时间,计算帧的延迟
 * 根据帧的延迟和帧的大小,更新抖动估计
 * 
 * 设置 frames_to_decode_ 中帧的渲染时间为首帧的渲染时间
 * 返回待编码帧 frames_to_decode_
 * 
*/
EncodedFrame* FrameBuffer::GetNextFrame() {
  int64_t now_ms = clock_->TimeInMilliseconds();
  // TODO(ilnik): remove |frames_out| use frames_to_decode_ directly.
  std::vector frames_out;

  bool superframe_delayed_by_retransmission = false;
  // 统计 superframe 大小,用于更新抖动
  size_t superframe_size = 0;
  // 获取 frames_to_decode_ 中第一个待解码的帧
  EncodedFrame* first_frame = frames_to_decode_[0]->second.frame.get();
  // 获取第一个待解码帧的渲染时间和接收时间
  int64_t render_time_ms = first_frame->RenderTime();
  int64_t receive_time_ms = first_frame->ReceivedTime();
  
  // 优雅地处理坏的RTP时间戳和渲染时间问题
  if (HasBadRenderTiming(*first_frame, now_ms)) {
    jitter_estimator_.Reset();
    timing_->Reset();
    render_time_ms = timing_->RenderTimeMs(first_frame->Timestamp(), now_ms);
  }

  // 遍历 frames_to_decode_
  for (FrameMap::iterator& frame_it : frames_to_decode_) {
    // 获取待解码的帧
    EncodedFrame* frame = frame_it->second.frame.release();
    // 设置帧的渲染时间
    frame->SetRenderTime(render_time_ms);

    superframe_delayed_by_retransmission |= frame->delayed_by_retransmission();
    // 计算接收时间
    receive_time_ms = std::max(receive_time_ms, frame->ReceivedTime());
    // 计算 superframe 帧大小
    superframe_size += frame->size();

    PropagateDecodability(frame_it->second);
    decoded_frames_history_.InsertDecoded(frame_it->first, frame->Timestamp());

    // Remove decoded frame and all undecoded frames before it.
    if (stats_callback_) {
      unsigned int dropped_frames = std::count_if(
          frames_.begin(), frame_it,
          [](const std::pair& frame) {
            return frame.second.frame != nullptr;
          });
      if (dropped_frames > 0) {
        stats_callback_->OnDroppedFrames(dropped_frames);
      }
    }

    frames_.erase(frames_.begin(), ++frame_it);

    // 将帧存放到 frames_out
    frames_out.push_back(frame);
  }

  if (!superframe_delayed_by_retransmission) {
    int64_t frame_delay;

    // 根据首帧的时间戳和接收时间 计算帧延迟并保存到 frame_delay
    if (inter_frame_delay_.CalculateDelay(first_frame->Timestamp(),
                                          &frame_delay, receive_time_ms)) {
      // 根据 帧延迟 和 superframe 帧大小 更新抖动估计
      jitter_estimator_.UpdateEstimate(frame_delay, superframe_size);
    }

    float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0;
    absl::optional rtt_mult_add_cap_ms = absl::nullopt;
    if (rtt_mult_settings_.has_value()) {
      rtt_mult = rtt_mult_settings_->rtt_mult_setting;
      rtt_mult_add_cap_ms = rtt_mult_settings_->rtt_mult_add_cap_ms;
    }
    // 设置 jitter 延迟
    timing_->SetJitterDelay(
        jitter_estimator_.GetJitterEstimate(rtt_mult, rtt_mult_add_cap_ms));
    // 更新当前延迟
    timing_->UpdateCurrentDelay(render_time_ms, now_ms);
  } else {
    if (RttMultExperiment::RttMultEnabled() || add_rtt_to_playout_delay_)
      jitter_estimator_.FrameNacked();
  }

  UpdateJitterDelay();
  UpdateTimingFrameInfo();

  if (frames_out.size() == 1) {
    return frames_out[0];
  } else {
    return CombineAndDeleteFrames(frames_out);
  }
}
 

你可能感兴趣的:(webrtc,#,webrtc视频技术,webrtc)