为什么要自己封装

其实目前WebRTC已经提供了封装好的API借口供第三方使用,包括前文提到的JsSIP等开源项目,都是使用此API,API的说明文档请参阅:
https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API
http://w3c.github.io/webrtc-pc/

其中源代码位于WebRTC源码目录下的src/api路径下,但是如果使用此API,天然的就包括需要调试实现https、dtls,ice,sdp等比较繁琐的步骤,如果你只需要关心或者使用WebRTC的采集、降噪、压缩、编码、传输这些核心功能,有自己的系统实现signal部分(SIP/IAX2/私有协议),同时服务端逻辑及用户身份认证需要自己掌握控制的话(如开发自己的IM、提供自己可控的VoIP业务等),那WebRTC本身的API就显得有些复杂,本系列文章主要基于此原因讲解如何封装属于自己的较轻量级的Native SDK API。

在源码src目录下,通过git查看各历史版本代码发现,release版本的分支r4x—>r5x—>r6x代码架构都有较大调整,r4x及以前的版本代码使用voe/vie/channel等概念进行audio/video的操作,r5x类版本代码废除了vie及channel概念,仅剩voe功能,而最新的r6x及以后,vie/voe/channel等概念都已经废除,而将底层代码的功能集成度进一步提高,截止目前,WebRTC最新版已经到r74,由于开始封装以及实现后续功能的时候,采用了r67 Release版本,所以本文及后续系列文章的代码实现都是基于r67版本,r67及以后代码框架变化不大,基本经过简单修改即可升级到最新版本。因此如要使用代码测试,请自行下载WebRTC源码之后切换到r67分支下。
源码目录如下:
WebRTC SDK API封装(2)-Linux/macOS平台

封装思路

既然目的是仅使用WebRTC的采集、处理、压缩、编码、传输这些核心功能,通过查看WebRTC源码得出,要实现这些功能,比较核心的几个头文件如下:

1.call/call.h

  • Create
    创建call对象,包括audio设备,audio采集后的处理(回音消除、增益等优秀算法),call对象的传输速率,此call使用中log系统的配置,以下4组流的创建及销毁都依赖于此call对象
  • CreateAudioSendStream/DestroyAudioSendStream
    创建、销毁本地发送端的音频流,包括rtp/rtcp transport,ssrc,codec,audio传输速率等的配置;
  • CreateAudioReceiveStream/DestroyAudioReceiveStream
    创建、销毁远端接收的音频流,包括rtp/rtcp transport,ssrc,codec等的配置;
  • CreateVideoSendStream/DestroyVideoSendStream
    创建、销毁本地发送端的视频流,包括rtp/rtcp transport,ssrc,codec,video传输速率,视频源等的配置;
  • CreateVideoReceiveStream/DestroyVideoReceiveStream
    创建、销毁远端接收的视频流,包括rtp/rtcp transport,ssrc,codec,接收视频的渲染等的配置;

2.api/transport.h

  • SendRtp/SendRtcp
    RTP/RTCP数据包的传输接口,此API为虚拟接口,在封装层回调自己实现控制传输逻辑即可*使用WebRTC的传输模块;

3.modules/audio_device/include/audio_device.h

  • Create
    创建音频设备模块,平台相关,不同平台(macOS/Linux/Android/iOS)有不同的实现方法;
  • InitSpeaker/Microphone
    麦克风扬声器的初始化;
  • InitPlayout/Recording
    采集播放的初始化;

4.test/vcm_capturer.h

  • Create
    创建视频采集模块(此模块不同平台有不同的实现,后续实现Android/iOS还会讲到,根据目录名字也可看到,WebRTC已经将这些实现集成到上层API,不再维护,如果需要使用需要自己移植)

5.test/video_renderer.h

  • Create
    创建视频渲染模块(此模块不同平台有不同的实现,后续实现Android/iOS还会讲到,根据目录名字也可看到,WebRTC已经将这些实现集成到上层API,不再维护,如果需要使用需要自己移植)

代码封装

根据上述5部分,各个部分相应代码摘选如下:

1.Call模块的创建

void CreateCall(void)
{
	webrtc::AudioState::Config audioStateConfig;
	audioStateConfig.audio_device_module = g_Adm;
	audioStateConfig.audio_mixer = webrtc::AudioMixerImpl::Create();
	audioStateConfig.audio_processing = webrtc::AudioProcessingBuilder().Create();

	webrtc::BitrateConstraints call_bitrate_config;
	call_bitrate_config.min_bitrate_bps = CALL_MIN_BPS;
	call_bitrate_config.start_bitrate_bps = CALL_START_BPS;
	call_bitrate_config.max_bitrate_bps = CALL_MAX_BPS;
	
	g_Event_log = webrtc::RtcEventLog::Create(webrtc::RtcEventLog::EncodingType::Legacy);
	webrtc::CallConfig callConfig(g_Event_log.get());
	callConfig.audio_state = webrtc::AudioState::Create(audioStateConfig);
	callConfig.bitrate_config = call_bitrate_config;
	
	g_Adm->RegisterAudioCallback(callConfig.audio_state->audio_transport());

	g_Call = webrtc::Call::Create(callConfig);

	g_Call->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkUp);
	g_Call->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkUp);
}

2.传输模块的实现(由于使用本地回环测试,因此采集压缩编码之后,直接调用Receiver->Deliver进行解压缩,并通过扬声器播放)

class AudioLoopTransport: public webrtc::Transport{
public:
    bool SendRtp(const uint8_t* packet,size_t length,const webrtc::PacketOptions& options) override
    {		
        int64_t send_time = webrtc::Clock::GetRealTimeClock()->TimeInMicroseconds();
		rtc::SentPacket sent_packet(options.packet_id,send_time);
    	g_Call->OnSentPacket(sent_packet);
		g_Call->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, rtc::CopyOnWriteBuffer(packet, length), webrtc::PacketTime(send_time, -1));
        return true;
    }

    bool SendRtcp(const uint8_t* packet, size_t length) override
    {
		int64_t send_time = webrtc::Clock::GetRealTimeClock()->TimeInMicroseconds();
        g_Call->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, rtc::CopyOnWriteBuffer(packet, length), webrtc::PacketTime(send_time, -1));
        return true;
    }
};

3.音频设备模块的创建

void AudioDeviceModule_Setup(void)
{	
	bool stereo_playout = false;
	bool stereo_record = false;

	if(!g_Adm)
		g_Adm = webrtc::AudioDeviceModule::Create(webrtc::AudioDeviceModule::kPlatformDefaultAudio);
	g_Adm->Init();
	g_Adm->SetPlayoutDevice(USED_AUDIO_DEVICE_ID);
	g_Adm->InitSpeaker();
	g_Adm->SetRecordingDevice(USED_AUDIO_DEVICE_ID);
	g_Adm->InitMicrophone();
	
	g_Adm->StereoPlayoutIsAvailable(&stereo_playout);
	g_Adm->SetStereoPlayout(stereo_playout);
	g_Adm->StereoRecordingIsAvailable(&stereo_record);
	g_Adm->SetStereoRecording(stereo_record);
	g_Adm->InitPlayout();
	g_Adm->InitRecording();
}

4.视频采集模块

void StartCapture(void)
{
	g_Video_capturers.reset(webrtc::test::VcmCapturer::Create(
            USED_VIDEO_WIDTH, USED_VIDEO_HEIGHT,
            USED_VIDEO_FPS,
            USED_VIDEO_DEVICE_ID));
	g_Video_capturers->Start();
}

5.视频渲染模块

void SetupLocalRender(void)
{
	g_Local_render.reset(webrtc::test::VideoRenderer::Create("Video Local Preview Render #1", USED_VIDEO_WIDTH, USED_VIDEO_HEIGHT));
}

void SetupRomoteRender(void)
{
	g_Remote_render.reset(webrtc::test::VideoRenderer::Create("Video Remote Render #2 ", USED_VIDEO_WIDTH, USED_VIDEO_HEIGHT));
}

编译代码

在WebRTC的src/目录下,创建新文件夹wtkrtc_linux_mac_loopback,创建wtk_rtc_api.cc/h,加入以上相关代码,创建BUILD.gn文件,gn文件在WebRTC中相当于Makefile,加入以下脚本:

import("//webrtc.gni")

#可执行文件,编译目标为wtkrtc_api_test
rtc_executable("wtkrtc_api_test") {
  sources = [
    "wtk_rtc_api.cc",
    "wtk_rtc_api.h",
  ]
  testonly = true
  deps = [
    "//modules/audio_coding:audio_coding",
    "//modules/audio_device:audio_device",
    "//modules/audio_processing:audio_processing",
    "//modules/audio_mixer:audio_mixer_impl",
    "//modules/video_capture:video_capture",
    "//modules/video_capture:video_capture_internal_impl",
    "//system_wrappers:metrics_default",
    "//system_wrappers:field_trial_default", 
    "//api/audio_codecs:builtin_audio_decoder_factory",
    "//call:call",
    "//call:bitrate_allocator",
    "//system_wrappers:runtime_enabled_features_default",
    "//logging:rtc_event_log_impl_base",
    "//api/video_codecs:video_codecs_api",
    "//media:media",
    
    "//test:test_renderer",
    "//test:video_test_common",
    "//test:run_test",
  ]

  if (is_clang) {
    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
    suppressed_configs += [
        "//build/config/clang:extra_warnings",
        "//build/config/clang:find_bad_constructs",
    ]
  }
}

修改src/BUILD.gn文件,加入对于wtkrtc_api_test目标的编译,如下:
WebRTC SDK API封装(2)-Linux/macOS平台

测试效果

WebRTC SDK API封装(2)-Linux/macOS平台

如果感兴趣,以上测试源码请参考:
https://github.com/wesley-fly/wtkrtc-sdk-api/tree/master/wtkrtc_linux_mac_loopback

欢迎大家Star/Fork/Issue/Report,本人水平一般,能力有限,欢迎大家指正留言交流!

分类:

技术点:

相关文章: