WebRTC Native M96 视频发送编码(OpenH264)流程以及接收视频包解码(FFmpeg)播放流程

Posted 一苇渡江694

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebRTC Native M96 视频发送编码(OpenH264)流程以及接收视频包解码(FFmpeg)播放流程相关的知识,希望对你有一定的参考价值。

H264 和 OpenH264

H.264,又称为MPEG-4第10部分,高级视频编码(英语:MPEG-4 Part 10, Advanced Video Coding,缩写为MPEG-4 AVC)是一种面向块,基于运动补偿的视频编码标准 。到2014年,它已经成为高精度视频录制、压缩和发布的最常用格式之一。第一版标准的最终草案于2003年5月完成。

H.264/AVC项目的目的是为了创建一个更佳的视频压缩标准,在更低的比特率的情况下依然能够提供良好视频质量的标准(如,一半或者更少于MPEG-2,H.263,或者MPEG-4 Part2 )。同时,还要不会太大的增加设计的复杂性。H.264的另外一个目标是提供足够的灵活性,以允许该标准能够应用于各种各样的网络和系统的各应用上,包括低和高比特率,低和高分辨率视频,广播,DVD存储,RTP / IP分组网络和ITU-T多媒体电话系统。H.264标准可以被视为由多个不同的应用框架 / 配置文件(profiles)组成的“标准系列”。

OpenH264是一个实时编码和解码视频流至H.264/MPEG-4 AVC格式的自由软件库。它采用简化BSD许可证发布

视频数据从采集到发送数据包的处理流程

视频采集线程将将采集的数据送到本地渲染和编码队列

线程:webrtc_video_capture

CaptureInputPin::Receive
CaptureSinkFilter::ProcessCapturedFrame
VideoCaptureImpl::IncomingFrame
VideoCaptureImpl::DeliverCapturedFrame(这里面有个_dataCallBack,就是看看你要把数据回调到哪,可以自己实现一个VcmCapture的,再实现一个VideoSourceImpl的类)
VcmCapturerWin::OnFrame(自己实现的类,也可以使用webrtc自带的test::VcmCapturer::OnFrame)
VideoSourceImpl::OnFrame(自己实现的类,也可以使用webrtc自带的test::TestVideoCapturer::OnFrame)
VideoBroadcaster::OnFrame
VideoStreamEncoder::OnFrame(在这里面将frame送到编码器队列encoder_queue_)

编码,视频编码器在视频帧编码结束后,发送到PacedSender队列

使用openH264进行视频编码
h264封装rtc包的细节,之后会专门去讲:fua stapa singlenalu
线程: EncoderQueue

VideoStreamEncoder::OnBitrateUpdated
VideoStreamEncoder::EncodeVideoFrame
EncoderSimulcastProxy::InitEncode
H264EncoderImpl::Encode(编码)---->CWelsH264SVCEncoder::EncodeFrame--->CWelsH264SVCEncoder::EncodeFrameInternal--->CWelsH264SVCEncoder::UpdateStatistics
VideoStreamEncoder::OnEncodedImage(编码结束)
VideoSendStreamImpl::OnEncodedImage
RtpVideoSender::OnEncodedImage
RTPSenderVideo::SendEncodedImage
RTPSenderVideo::SendVideo
打包:
RtpPacketizer::Create
RtpPacketizerH264::RtpPacketizerH264
RtpPacketizerH264::GeneratePackets
如果fragment_len > single_packet_capacity,调用RtpPacketizerH264::PacketizeFuA(例如,single_packet_capacity== 1176)
否则,调用RtpPacketizerH264::PacketizeStapA

发送到PacedSender队列:
RTPSenderVideo::LogAndSendToNetwork
RTPSender::EnqueuePackets
PacedSender::EnqueuePackets
PacingController::EnqueuePacket

PacedSender 将 RTP 包通过 MediaChannel 发送出去

线程:PacerThread

PacedSender::Process
PacingController::ProcessPackets
PacketRouter::SendPacket
ModuleRtpRtcpImpl2::TrySendPacket
RtpSenderEgress::SendPacket
RtpSenderEgress::SendPacketToNetwork
WebRtcVideoChannel::SendRtp
MediaChannel::SendRtp
MediaChannel::SendRtp() 最终将 RTP 包发送到网络的过程与音频相同

通过socket接口将数据包发送到SFU

线程:network_thread

MediaChannel::SendPacket
MediaChannel::DoSendPacket
BaseChannel::SendPacket
BaseChannel::SendPacket(bool rtcp, rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options)
SrtpTransport::SendRtpPacket
RtpTransport::SendPacket
DtlsTransport::SendPacket
P2PTransportChannel::SendPacket
ProxyConnection::Send
UDPPort::SendTo
AsyncUDPSocket::SendTo
PhysicalSocket::SendTo
PhysicalSocket::DoSendTo
::sendto

从接收视频数据包的到解码处理流程

视频接收处理从创建远端流的 VideoTrack 开始,然后为远端视频流的 VideoTrack 创建渲染器

从网络接收RTP包,识别视频的RTP

线程:network_thread
前面的调用流程跟音频一样

UDPPort::HandleIncomingPacket
UDPPort::OnReadPacket
Connection::OnReadPacket
P2PTransportChannel::OnReadPacket
DtlsTransport::OnReadPacket
RtpTransport::OnReadPacket
SrtpTransport::OnRtpPacketReceived
RtpTransport::DemuxPacket
RtpDemuxer::OnRtpPacket
BaseChannel::OnRtpPacket
WebRtcVideoChannel::OnPacketReceived

组帧并插入缓冲区

发送组装视频rtp包的时候,就有拆包,之后会详细说明。

所以,通常情况下,一个 UDP 包里是放不下一帧编码视频数据的,因而在发送端,需要分片,通过多个 RTP 包发出去,而在接收端则需要将这些 RTP 包组帧,组成一个完整的编码视频帧。
线程:worker_thread

Call::DeliverPacket
Call::DeliverRtp
RtpStreamReceiverController::OnRtpPacket
RtpDemuxer::OnRtpPacket
RtpVideoStreamReceiver2::OnRtpPacket
RtpVideoStreamReceiver2::ReceivePacket
RtpVideoStreamReceiver2::OnReceivedPayloadData
RtpVideoStreamReceiver2::OnInsertedPacket
RtpVideoStreamReceiver2::OnAssembledFrame
RtpVideoStreamReceiver2::OnCompleteFrames
VideoReceiveStream2::OnCompleteFrame
FrameBuffer::InsertFrame

视频解码

使用ffmpeg进行H264解码
线程: DecodingQueue

VideoReceiveStream2::HandleEncodedFrame
VideoReceiveStream2::DecodeAndMaybeDispatchEncodedFrame
VideoReceiver2::Decode
VCMGenericDecoder::Decode
H264DecoderImpl::Decode
VCMDecodedFrameCallback::Decoded
VideoStreamDecoder::FrameToRender
IncomingVideoStream::OnFrame(callback_->OnFrame(*frame_to_render);)

视频渲染

线程: IncomingVideoStream

IncomingVideoStream::Dequeue
VideoReceiveStream2::OnFrame
WebRtcVideoChannel::WebRtcVideoReceiveStream::OnFrame
VideoBroadcaster::OnFrame

VideoSinkImpl::OnFrame(VideoSinkImpl为自己写的类,渲染d3d)

d3d渲染代码:

    if (!inited_for_raw_) 
      m_d3d_ = Direct3DCreate9(D3D_SDK_VERSION);
      if (!m_d3d_)
        return;

      D3DPRESENT_PARAMETERS d3d_params = ;

      d3d_params.Windowed = true;
      d3d_params.SwapEffect = D3DSWAPEFFECT_COPY;

      IDirect3DDevice9* d3d_device;
      if (m_d3d_->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd_,
                               D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3d_params,
                               &d3d_device) != D3D_OK) 
        Destroy();
        return;
      
      m_d3d_device_ = d3d_device;
      d3d_device->Release();

      IDirect3DVertexBuffer9* vertex_buffer;
      const int kRectVertices = 4;
      if (m_d3d_device_->CreateVertexBuffer(
              kRectVertices * sizeof(D3dCustomVertex), 0, D3DFVF_CUSTOMVERTEX,
              D3DPOOL_MANAGED, &vertex_buffer, nullptr) != D3D_OK) 
        Destroy();
        return;
      
      m_vertex_buffer_ = vertex_buffer;
      vertex_buffer->Release();

      m_d3d_device_->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
      m_d3d_device_->SetRenderState(D3DRS_LIGHTING, FALSE);
      Resize(video_frame.width(), video_frame.height());
      inited_for_raw_ = true;
     else 
      HRESULT hr = m_d3d_device_->TestCooperativeLevel();
      if (FAILED(hr))
        if(hr == D3DERR_DEVICELOST)
          RTC_LOG(LS_WARNING) << "Device lost.";
        
        else if(hr == D3DERR_DEVICENOTRESET)
          Destroy();
          RTC_LOG(LS_WARNING) << "Device try to reinit.";
        
        else
          RTC_LOG(LS_WARNING) << "Device driver internal error.";
        
        
        return;
      
      
      if (static_cast<size_t>(video_frame.width()) != width_ ||
          static_cast<size_t>(video_frame.height()) != height_) 
        Resize(static_cast<size_t>(video_frame.width()),
               static_cast<size_t>(video_frame.height()));
      
      D3DLOCKED_RECT lock_rect;
      if (m_texture_->LockRect(0, &lock_rect, nullptr, 0) != D3D_OK)
        return;

      ConvertFromI420(video_frame, webrtc::VideoType::kARGB, 0,
                      static_cast<uint8_t*>(lock_rect.pBits));
      m_texture_->UnlockRect(0);

      m_d3d_device_->BeginScene();
      m_d3d_device_->SetFVF(D3DFVF_CUSTOMVERTEX);
      m_d3d_device_->SetStreamSource(0, m_vertex_buffer_, 0,
                                     sizeof(D3dCustomVertex));
      m_d3d_device_->SetTexture(0, m_texture_);
      m_d3d_device_->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
      m_d3d_device_->EndScene();

      m_d3d_device_->Present(nullptr, nullptr, wnd_, nullptr);
    
  

以上是关于WebRTC Native M96 视频发送编码(OpenH264)流程以及接收视频包解码(FFmpeg)播放流程的主要内容,如果未能解决你的问题,请参考以下文章

WebRTC Native M96 SDK接口封装--setVideoEncoderConfiguration设置本地视频的编码属性

WebRtc Native M96 远端视频接收之PacketBuffer-组帧原理分析

WebRtc Native M96 远端视频接收之PacketBuffer-组帧原理分析

WebRTC Native M96 SDK接口封装--muteLocalVideoStream开关本地视频发送

WebRTC Native M96 SDK接口封装--muteLocalVideoStream开关本地视频发送

WebRTC Native M96 SDK接口封装--muteLocalVideoStream开关本地视频发送