WebRTC我编译的97版本的webrtc_android,这里我就以Android视角从视频采集,渲染,编码,发送四个流程来答题说一下WebRtc的视频推流过程,
采集
Android摄像头采集可以调用WebRtc提供的VideoCapturer,他封装了Camera和Camera2的两套api在里面。
VideoCapturer
public interface VideoCapturer {
void initialize(
SurfaceTextureHelper surfaceTextureHelper,
Context applicationContext,
CapturerObserver capturerObserver);
}
SurfaceTextureHelper封装了SurfaceTexture,使用Camera2 api 可以直接调用CameraDevice.createCaptureSession()打开摄像头,把Surface传进去。
private class CameraStateCallback extends CameraDevice.StateCallback {
// other definitions...
@Override
public void onOpened(CameraDevice camera) {
// method body...
surfaceTextureHelper.setTextureSize(captureFormat.width, captureFormat.height);
surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
try {
camera.createCaptureSession(
Arrays.asList(surface), new CaptureSessionCallback(), cameraThreadHandler);
} catch (CameraAccessException e) {
// method body...
}
}
// other definitions...
}
摄像头就会将输出直接输出到绑定的Surface中去。
当SurfaceTexture缓冲区BufferQueue中存在帧数据的时候,会回调CapturerObserver.onFrameCaptured(frame)把帧数据通过NativeAndroidVideoTrackSource传到WebRTC的native层。
public class VideoSource extends MediaSource {
// other definitions...
private final CapturerObserver capturerObserver = new CapturerObserver() {
// other definitions..
@Override
public void onFrameCaptured(VideoFrame frame) {
// body method...
VideoFrame adaptedFrame = VideoProcessor.applyFrameAdaptationParameters(frame, parameters);
if (adaptedFrame != null) {
nativeAndroidVideoTrackSource.onFrameCaptured(adaptedFrame);
adaptedFrame.release();
}
}
};
}
在打印NavtiveAndroidVideoTrackSource的调用站
webrtc::jni::AndroidVideoTrackSource::onFrameCaptured
→ rtc::AdaptedVideoTrackSource::OnFrame
→ rtc::VideoBroadcaster::OnFrame
采集过程大致是这样子。
渲染过程
Android上的渲染主要是显示的预览画面内容。我们直接调用VideoTrack.addsink(sink)添加预览画面进去,这时候就相当于是订阅了VideoBroadCaster,当有数据进来的时候,VideoBroadCaster会回调VideoSink.onFrame(frame),然后回渲染到EGL中去了。
编码和发送
因为编码和发送大部分都是在native层实现的,这里我直接弄出他们的调用栈来。
webrtc::VideoStreamEncoder::OnFrame
→ webrtc::VideoStreamEncoder::MaybeEncodeVideoFrame
→ webrtc::VideoStreamEncoder::EncodeVideoFrame
→ webrtc::LibvpxVp8Encoder::Encode #1
→ webrtc::LibvpxVp8Encoder::GetEncodedPartitions
→ webrtc::VideoStreamEncoder::OnEncodedImage
→ webrtc::internal::VideoSendStreamImpl::OnEncodedImage
→ webrtc::RtpVideoSender::OnEncodedImage
→ webrtc::RTPSenderVideo::SendEncodedImage
→ webrtc::RTPSenderVideo::SendVideo
→ webrtc::RTPSenderVideo::LogAndSendToNetwork
→ webrtc::RTPSender::EnqueuePackets
→ webrtc::PacedSender::EnqueuePackets
→ webrtc::PacingController::EnqueuePacket
→ webrtc::PacingController::EnqueuePacketInternal
→ webrtc::PacedSender::Process #2
→ webrtc::PacingController::ProcessPackets
→ webrtc::PacedSender::SendRtpPacket
→ webrtc::ModuleRtpRtcpImpl2::TrySendPacket
→ webrtc::RtpSenderEgress::SendPacket
→ webrtc::RtpSenderEgress::SendPacketToNetwork
→ cricket::WebRtcVideoChannel::SendRtp
→ cricket::MediaChannel::SendPacket
→ cricket::MediaChannel::DoSendPacket
→ cricket::VideoChannel::SendPacket
→ webrtc::DtlsSrtpTransport::SendRtpPacket #3
从调用栈可以看到编码器是LibvpxVp8Encoder,当然可以自己换成H264Encoder.
然后就进入了PeerConnection的发送数据包流程了。
体外话:
1.其实这里我们可以做的事情很多,比如保存录制回放可以可以不用Camera2的信息,直接读取我们本地的信息进去。或者给摄像头添加滤镜什么的。还有一些事添加人脸识别进去的,拿到流检测书人脸,拿到人脸信息直接添加到流上去。