webrtc 编译 - housekeeper-software/tech GitHub Wiki

准备

最好在买个云主机,位置在美国、新加坡、香港等地,最好是亚马逊的美国服务器,下载不要太快。 

webrtc 版本 (m88)

https://webrtc.googlesource.com/src.git/+log/refs/branch-heads/4324

注意

如果在同一台机器编译,每个平台都新建一个目录,互相不干扰
比如:
/build/rv1109
/build/linux
/build/android
需要编译debug/release,debug用于开发  
源码可以从上述链接下载,但是需要将编译环境中的 src/third_party/abseil-cpp拷贝出来,放在源码的 third_party目录下。  
因为源码头文件中也引用了absl头文件。 
每个平台编译出来的都是 libwebrtc.a,此库包含所有用到的目标文件,并没有其他依赖。  
通过下述方式编译的库,可以提供neteq, audioprocess,等模块的调用。但是一般去掉了audio device,因为我们使用外部的audio device

关于audio_device_alsa_linux.cc在低性能设备上经常锁死在录音线程的解决方案

简单说,就是把以下两个线程中的锁全部注释掉

bool AudioDeviceLinuxALSA::PlayThreadProcess() {
  if (!_playing)
    return false;

  int err;
  snd_pcm_sframes_t frames;
  snd_pcm_sframes_t avail_frames;

 // Lock();
  // return a positive number of frames ready otherwise a negative error code
  avail_frames = LATE(snd_pcm_avail_update)(_handlePlayout);
  if (avail_frames < 0) {
    RTC_LOG(LS_ERROR) << "playout snd_pcm_avail_update error: "
                      << LATE(snd_strerror)(avail_frames);
    ErrorRecovery(avail_frames, _handlePlayout);
   // UnLock();
    return true;
  } else if (avail_frames == 0) {
  //  UnLock();

    // maximum tixe in milliseconds to wait, a negative value means infinity
    err = LATE(snd_pcm_wait)(_handlePlayout, 2);
    if (err == 0) {  // timeout occured
      RTC_LOG(LS_VERBOSE) << "playout snd_pcm_wait timeout";
    }

    return true;
  }

  if (_playoutFramesLeft <= 0) {
   // UnLock();
    _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS);
   // Lock();

    _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer);
    assert(_playoutFramesLeft == _playoutFramesIn10MS);
  }

  if (static_cast<uint32_t>(avail_frames) > _playoutFramesLeft)
    avail_frames = _playoutFramesLeft;

  int size = LATE(snd_pcm_frames_to_bytes)(_handlePlayout, _playoutFramesLeft);
  frames = LATE(snd_pcm_writei)(
      _handlePlayout, &_playoutBuffer[_playoutBufferSizeIn10MS - size],
      avail_frames);

  if (frames < 0) {
    RTC_LOG(LS_VERBOSE) << "playout snd_pcm_writei error: "
                        << LATE(snd_strerror)(frames);
    _playoutFramesLeft = 0;
    ErrorRecovery(frames, _handlePlayout);
   // UnLock();
    return true;
  } else {
    assert(frames == avail_frames);
    _playoutFramesLeft -= frames;
  }

 // UnLock();
  return true;
}

bool AudioDeviceLinuxALSA::RecThreadProcess() {
  if (!_recording)
    return false;

  int err;
  snd_pcm_sframes_t frames;
  snd_pcm_sframes_t avail_frames;
  int8_t buffer[_recordingBufferSizeIn10MS];

  //Lock();

  // return a positive number of frames ready otherwise a negative error code
  avail_frames = LATE(snd_pcm_avail_update)(_handleRecord);
  if (avail_frames < 0) {
    RTC_LOG(LS_ERROR) << "capture snd_pcm_avail_update error: "
                      << LATE(snd_strerror)(avail_frames);
    ErrorRecovery(avail_frames, _handleRecord);
  //  UnLock();
    return true;
  } else if (avail_frames == 0) {  // no frame is available now
  //  UnLock();

    // maximum time in milliseconds to wait, a negative value means infinity
    err = LATE(snd_pcm_wait)(_handleRecord, ALSA_CAPTURE_WAIT_TIMEOUT);
    if (err == 0)  // timeout occured
      RTC_LOG(LS_VERBOSE) << "capture snd_pcm_wait timeout";

    return true;
  }

  if (static_cast<uint32_t>(avail_frames) > _recordingFramesLeft)
    avail_frames = _recordingFramesLeft;

  frames = LATE(snd_pcm_readi)(_handleRecord, buffer,
                               avail_frames);  // frames to be written
  if (frames < 0) {
    RTC_LOG(LS_ERROR) << "capture snd_pcm_readi error: "
                      << LATE(snd_strerror)(frames);
    ErrorRecovery(frames, _handleRecord);
  //  UnLock();
    return true;
  } else if (frames > 0) {
    assert(frames == avail_frames);

    int left_size =
        LATE(snd_pcm_frames_to_bytes)(_handleRecord, _recordingFramesLeft);
    int size = LATE(snd_pcm_frames_to_bytes)(_handleRecord, frames);

    memcpy(&_recordingBuffer[_recordingBufferSizeIn10MS - left_size], buffer,
           size);
    _recordingFramesLeft -= frames;

    if (!_recordingFramesLeft) {  // buf is full
      _recordingFramesLeft = _recordingFramesIn10MS;

      // store the recorded buffer (no action will be taken if the
      // #recorded samples is not a full buffer)
      _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer,
                                         _recordingFramesIn10MS);

      // calculate delay
      _playoutDelay = 0;
      _recordingDelay = 0;
      if (_handlePlayout) {
        err = LATE(snd_pcm_delay)(_handlePlayout,
                                  &_playoutDelay);  // returned delay in frames
        if (err < 0) {
          // TODO(xians): Shall we call ErrorRecovery() here?
          _playoutDelay = 0;
          RTC_LOG(LS_ERROR)
              << "playout snd_pcm_delay: " << LATE(snd_strerror)(err);
        }
      }

      err = LATE(snd_pcm_delay)(_handleRecord,
                                &_recordingDelay);  // returned delay in frames
      if (err < 0) {
        // TODO(xians): Shall we call ErrorRecovery() here?
        _recordingDelay = 0;
        RTC_LOG(LS_ERROR) << "capture snd_pcm_delay: "
                          << LATE(snd_strerror)(err);
      }

      // TODO(xians): Shall we add 10ms buffer delay to the record delay?
      _ptrAudioBuffer->SetVQEData(_playoutDelay * 1000 / _playoutFreq,
                                  _recordingDelay * 1000 / _recordingFreq);

      _ptrAudioBuffer->SetTypingStatus(KeyPressed());

      // Deliver recorded samples at specified sample rate, mic level etc.
      // to the observer using callback.
      //UnLock();
      _ptrAudioBuffer->DeliverRecordedData();
      //Lock();
    }
  }

  //UnLock();
  return true;
}

添加 voip_core

修改 src目录下的 BUILD.GN
if (!build_with_chromium) {
  # Target to build all the WebRTC production code.
  rtc_static_library("webrtc") {
    # Only the root target and the test should depend on this.
    visibility = [
      "//:default",
      "//:webrtc_lib_link_test",
    ]

    sources = []
    complete_static_lib = true
    suppressed_configs += [ "//build/config/compiler:thin_archive" ]
    defines = []

    deps = [
      "api:create_peerconnection_factory",
      "api:libjingle_peerconnection_api",
      "api:rtc_error",
      "api:transport_api",
      "api/crypto",
      "api/rtc_event_log:rtc_event_log_factory",
      "api/task_queue",
      "api/task_queue:default_task_queue_factory",
      "api/voip:voip_api", #!!!
      "api/voip:voip_engine_factory",#!!!
      "rtc_base:rtc_json", #为了编译jsoncpp
      "third_party/abseil-cpp/absl/flags:flag",
      "third_party/abseil-cpp/absl/flags:parse", #可能用到absl 命令行解析

代码同步

linux

mkdir webrtc
cd webrtc
fetch --nohooks webrtc
gclient sync

cd src
git checkout branch-heads/4324
gclient sync

通过 gn gen 之后,生成编译脚本
执行 ninja -C out/Debug or ninja -C out/Release即可
libwebrtc.a 位于 out/Debug/gen/obj目录下  
查看包含的符号:
nm -C libwebrtc.a | grep xxxx

android

mkdir webrtc
cd webrtc
fetch --nohooks webrtc_android
gclient sync

cd src
git checkout branch-heads/4324
gclient sync

rv1109 gen 参数

rv1109不支持clang编译,所以,我们要禁止clang。
判断arm_float_abi 是soft,hard,根据工具链判断:gnueabihf 有h的就是hard
libjpeg-turbo 在gcc上编译出错,我们用内置的libjpeg,因为我们不会用到这块
rv1109也不用支持H264编解码器,我们用rkmpp 把third_party/abseil-cpp/absl/base/config.h中得 ABSL_TLS等等注释

// ABSL_HAVE_THREAD_LOCAL
//
// Checks whether C++11's `thread_local` storage duration specifier is
// supported.
#ifdef ABSL_HAVE_THREAD_LOCAL
#error ABSL_HAVE_THREAD_LOCAL cannot be directly set
#elif defined(__APPLE__)
// Notes:
// * Xcode's clang did not support `thread_local` until version 8, and
//   even then not for all iOS < 9.0.
// * Xcode 9.3 started disallowing `thread_local` for 32-bit iOS simulator
//   targeting iOS 9.x.
// * Xcode 10 moves the deployment target check for iOS < 9.0 to link time
//   making ABSL_HAVE_FEATURE unreliable there.
//
#if ABSL_HAVE_FEATURE(cxx_thread_local) && \
    !(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define ABSL_HAVE_THREAD_LOCAL 1
#endif
#else  // !defined(__APPLE__)
#undef ABSL_HAVE_TLS
#undef ABSL_HAVE_THREAD_LOCAL //#define ABSL_HAVE_THREAD_LOCAL 1
#endif

如果无法禁止pulse_audio ,则需要修改/module/audio_deivce/BUILD.gn


  defines += [ "WEBRTC_ENABLE_LINUX_ALSA" ]
        libs = [ "dl" ]
        if (rtc_use_x11) {
          libs += [ "X11" ]
          defines += [ "WEBRTC_USE_X11" ]
        }
        if (rtc_include_pulse_audio) {
          defines += [ "WEBRTC_ENABLE_LINUX_PULSE" ]
        }
        if (rtc_include_pulse_audio) { #添加这个条件
          sources += [
            "linux/audio_device_pulse_linux.cc",
            "linux/audio_device_pulse_linux.h",
            "linux/audio_mixer_manager_pulse_linux.cc",
            "linux/audio_mixer_manager_pulse_linux.h",
            "linux/pulseaudiosymboltable_linux.cc",
            "linux/pulseaudiosymboltable_linux.h",
          ]
        }

debug版本:

gn gen out/Debug --args='target_os="linux" target_cpu="arm" arm_arch="armv7-a" 
 arm_tune="cortex-a7" 
 arm_version=7 
 arm_optionally_use_neon=true 
 arm_use_neon=true 
 arm_fpu="neon-vfpv4" 
 is_clang=false 
 is_debug=true  
 is_nacl_glibc=false  
 libyuv_use_neon=true 
 rtc_build_with_neon=true 
 rtc_use_gtk=false 
 strip_debug_info=false 
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_internal_audio_device=true  
 rtc_include_builtin_video_codecs=true 
 rtc_include_pulse_audio=false 
 use_glib=false   
 use_x11=false 
 rtc_use_x11=false 
 arm_float_abi="hard" 
 rtc_use_pipewire=false  
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 rtc_use_h264=false  
 rtc_enable_protobuf=false 
 use_rtti=true 
 rtc_build_json=true  
 rtc_enable_libevent=true  
 is_component_build=false 
 use_libjpeg_turbo=false 
 use_system_libjpeg=true 
 target_sysroot="/opt/qt-5.9.4-arm/host/arm-buildroot-linux-gnueabihf/sysroot"'

Release:

gn gen out/Release  --args='target_os="linux" target_cpu="arm" arm_arch="armv7-a" 
 arm_tune="cortex-a7" 
 arm_version=7 
 arm_optionally_use_neon=true 
 arm_use_neon=true 
 arm_fpu="neon-vfpv4" 
 is_clang=false 
 is_debug=false   
 is_nacl_glibc=false 
 libyuv_use_neon=true 
 rtc_build_with_neon=true 
 rtc_include_pulse_audio=false 
 rtc_use_gtk=false 
 strip_debug_info=true  
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_internal_audio_device=true 
 rtc_include_builtin_video_codecs=true 
 use_glib=false 
 rtc_use_x11=false 
 arm_float_abi="hard" 
 rtc_use_pipewire=false  
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 rtc_use_h264=false 
 rtc_enable_protobuf=false 
 use_rtti=true 
 rtc_build_json=true  
 rtc_enable_libevent=true  
 is_component_build=false 
 use_libjpeg_turbo=false 
 use_system_libjpeg=true 
 target_sysroot="/opt/qt-5.9.4-arm/host/arm-buildroot-linux-gnueabihf/sysroot"'

linux gen

rtc_enable_avx2=false 是在下电脑比较老,不支持avx2,一般情况下不需要禁止
我这里使用clang 编译,如果需要gcc编译,is_clang=false 即可 我们不用内置的h264 编解码器 linux使能 rtc_include_pulse_audio,这个工作的比alsa好,特别对vmware

Debug

gn gen out/Debug --args='target_os="linux" 
 target_cpu="x64" 
 is_clang=false  
 is_debug=true 
 is_nacl_glibc=false 
 rtc_use_gtk=false 
 strip_debug_info=false 
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_builtin_video_codecs=true 
 rtc_include_pulse_audio=true    
 use_glib=false   
 rtc_use_x11=false 
 rtc_use_pipewire=false  
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 rtc_use_h264=true 
 ffmpeg_branding="Chrome"  
 proprietary_codecs=true 
 rtc_enable_protobuf=false 
 rtc_enable_libevent=true 
 use_rtti=true 
 rtc_build_json=true    
 is_component_build=false 
 rtc_enable_avx2=false 
 rtc_include_internal_audio_device=true'

Release

gn gen out/Release --args='target_os="linux" 
 target_cpu="x64" 
 is_clang=false   
 is_debug=false 
 is_nacl_glibc=false 
 rtc_use_gtk=false 
 strip_debug_info=true 
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_builtin_video_codecs=true 
 rtc_include_pulse_audio=true   
 use_glib=false   
 rtc_use_x11=false 
 rtc_use_pipewire=false  
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 rtc_use_h264=true 
 ffmpeg_branding="Chrome"  
 proprietary_codecs=true 
 rtc_enable_protobuf=false 
 rtc_enable_libevent=true 
 use_rtti=true 
 rtc_build_json=true  
 is_component_build=false 
 rtc_enable_avx2=false 
 rtc_include_internal_audio_device=true'

android

Android中我们禁止: use_custom_libcxx=false use_custom_libcxx_for_host=false 原因是我们主程序要使用android NDK中的c++库,不然会出现C++库不一致的情况。

Debug

gn gen out/Debug --args='target_os="android" target_cpu="arm64" 
 is_debug=true 
 is_clang=true  
 arm_optionally_use_neon=true 
 arm_use_neon=true 
 strip_debug_info=false 
 android_full_debug=true 
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_builtin_video_codecs=true 
 rtc_use_h264=false 
 proprietary_codecs=true 
 rtc_enable_protobuf=false 
 rtc_enable_libevent=false 
 use_rtti=true 
 rtc_build_json=true  
 is_component_build=false 
 rtc_enable_android_aaudio=true 
 rtc_include_internal_audio_device=true'

Release

gn gen out/Release --args='target_os="android" target_cpu="arm64" 
 is_debug=false 
 is_clang=true  
 arm_optionally_use_neon=true 
 arm_use_neon=true 
 strip_debug_info=true 
 use_custom_libcxx=false 
 use_custom_libcxx_for_host=false 
 treat_warnings_as_errors=false 
 use_aura=false 
 use_dbus=false 
 use_gold=true 
 use_goma=false 
 use_lld=false 
 use_ozone=false 
 use_udev=false 
 rtc_build_examples=false 
 rtc_build_tools=false 
 rtc_include_tests=false 
 rtc_include_builtin_audio_codecs=true 
 rtc_include_builtin_video_codecs=true 
 rtc_use_h264=false 
 proprietary_codecs=true 
 rtc_enable_protobuf=false 
 rtc_enable_libevent=false 
 use_rtti=true 
 rtc_build_json=true   
 is_component_build=false 
 rtc_enable_android_aaudio=true  
 rtc_include_internal_audio_device=true'

android编译完整包

具体参考:https://www.jianshu.com/p/9456f45e9f91
debug版本:
arm64: gn gen out/Debug --args='target_os="android" target_cpu="arm64" is_debug=true  rtc_use_h264=true ffmpeg_branding="Chrome"   proprietary_codecs=true'
armv7: gn gen out/Debug --args='target_os="android" target_cpu="arm" is_debug=true  rtc_use_h264=true ffmpeg_branding="Chrome"   proprietary_codecs=true arm_optionally_use_neon=true 
 arm_use_neon=true '

release:
arm64: gn gen out/Release--args='target_os="android" target_cpu="arm64" is_debug=false strip_debug_info=true 
 rtc_use_h264=true ffmpeg_branding="Chrome"   proprietary_codecs=true'
armv7: gn gen out/Release --args='target_os="android" target_cpu="arm" is_debug=false strip_debug_info=true  rtc_use_h264=true ffmpeg_branding="Chrome"   proprietary_codecs=true arm_use_neon=true '

Android编译 aar

https://blog.jianchihu.net/webrtc-android-native-code-build-error.html
注意,还有一个ffmpeg openh264 license的问题要解决

得切到WebRTC源码/src目录下执行

build_aar.py 位于: \src\tools_webrtc\android
./tools_webrtc/android/build_aar.py --output "libwebrtc-m88.aar" --build-dir out --arch "armeabi-v7a" "arm64-v8a" --extra-gn-args 'is_debug=false strip_debug_info=true  rtc_use_h264=true ffmpeg_branding="Chrome" proprietary_codecs=true treat_warnings_as_errors=false'

⚠️ **GitHub.com Fallback** ⚠️