IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 分析PeerConnectionFactory.initialize (Android-RTC-3) -> 正文阅读

[移动开发]分析PeerConnectionFactory.initialize (Android-RTC-3)

上图是之前 Android-RTC-1?简单分析得出来的,一张关于PeerConnectionFactory的至下而上结构图。今天从PeerConnectionFactory入门Android-RTC,抽丝破茧层层深入。

PeerConnectionFactory之PeerConnectionFactory.initialize。

PeerConnectionFactory的创建流程大致分为两步:PeerConnectionFactory.initialize 和 PeerConnectionFactory.Builder.createPeerConnectionFactory。(Android-RTC大量的使用了各种设计模式的模板代码)
我们先来看PeerConnectionFactory.initialize:

final String fieldTrials = getFieldTrials(peerConnectionParameters);
PeerConnectionFactory.initialize(
        PeerConnectionFactory.InitializationOptions.builder(appContext)
        .setFieldTrials(fieldTrials)
        .setEnableInternalTracer(true)
        .createInitializationOptions());

/

public static class InitializationOptions {
        final Context applicationContext;
        final String fieldTrials;
        final boolean enableInternalTracer;
        final NativeLibraryLoader nativeLibraryLoader;
        final String nativeLibraryName;
        @Nullable
        Loggable loggable;
        @Nullable
        Severity loggableSeverity;
    ... ...
    public static class Builder {
        private final Context applicationContext;
        private String fieldTrials = "";
        private boolean enableInternalTracer;
        private NativeLibraryLoader nativeLibraryLoader = new DefaultLoader();
        private String nativeLibraryName = "jingle_peerconnection_so";
    }
}

fieldTrials变量是用于扩展配置,稍后再作讨论;至于DefaultLoader只是封装了一下System.loadLibrary(nativeLibraryName)没啥重要;记住enableInternalTracer这个开关,Tracer调试模块使用,也能适用于向外扩展。

public static void initialize(PeerConnectionFactory.InitializationOptions options) {
    ContextUtils.initialize(options.applicationContext); // 1
    NativeLibrary.initialize(options.nativeLibraryLoader, options.nativeLibraryName);//2
    nativeInitializeAndroidGlobals();//3
    nativeInitializeFieldTrials(options.fieldTrials);
    if (options.enableInternalTracer && !internalTracerInitialized) {
        initializeInternalTracer();//4
    }

    if (options.loggable != null) {
        Logging.injectLoggable(options.loggable, options.loggableSeverity);
        nativeInjectLoggable(new JNILogging(options.loggable), options.loggableSeverity.ordinal());
    } else {
        Logging.d("PeerConnectionFactory", "PeerConnectionFactory was initialized without an injected Loggable. Any existing Loggable will be deleted.");
        Logging.deleteInjectedLoggable();
        nativeDeleteLoggable();
    }
}

1、ContextUtils.initialize静态全局保存一个appContext,看着就行。

2、NativeLibrary.initialize => loader.load =>?System.loadLibrary(nativeLibraryName)

3、nativeInitializeAndroidGlobals();? nativeInitializeFieldTrials(fieldTrials);?nativeInitializeInternalTracer(); 都是native函数,接下来就分析一波native函数的查找方法。(native源码编译相关详情,请参考上一篇文章 Android-RTC2

=======================题外话分割线======================================

关于Android版本WebRTC的native源码,虽然没有Android系统源码那么庞大复杂,但是相关说明文档不多,模块组成也不明显。所以不借助工具,阅读起来还是挺费神的,推荐一款很有历史的国外阅读源码的工具—Source Insight—网上很多破解版,请自行下载。提几点重要:

A、Options—File Type Options,增加c++后缀? *.cc;去除掉一些没啥相关的文件类型。

B、Project—New Project 然后 Add and Remove Project Files,增加除data和resources文件夹以外其他的目录,并递归所有子目录。(Include top level sub-directories / Recursively add lower sub-directories)

(附赠附属源码?pan.baidu.com/s/1vrVdY8BDirY0uY7w15XSSg 提取码: rzhd?).

C、Project—Project Report,这一步生成当前工程目录下的文件符合表。

经过以上步骤,我们的前期工作就基本完成了。接着以PeerConnectionFactory.initialize当中的nativeInitializeAndroidGlobals为例,Alt+鼠标左键能跳转到识别到符号表的函数位置,但是JNI层的转接文件是识别不了的。可以这样查找Search—Search Proejcts—勾选两个skip开头的选项,其他的不勾选。OK

这样就能查找到当前工程下相关文件符号,可以看到查找目录,就是在前文提及到的,编译源码生成的out目录下的gen文件。套路就是这样,文章往后就不在详细描述了。

=======================题外话分割线======================================

继续看回nativeInitializeAndroidGlobals的代码部分,调用链路如下,对应文件也表明。这部分主要是初始化JVM / JNI环境,加载了几个org/webrtc/voiceengine包的类,包括BuildInfo / WebRtcAudioManager / WebRtcAudioRecord / WebRtcAudioTrack,如果自己想扩展其他java类引用,可以在这里添加。

// out\arm64-v8a\gen\sdk\android\generated_peerconnection_jni\PeerConnectionFactory_jni.h
JNI_GENERATOR_EXPORT void Java_org_webrtc_PeerConnectionFactory_nativeInitializeAndroidGlobals(
    JNIEnv* env,
    jclass jcaller) {
  return JNI_PeerConnectionFactory_InitializeAndroidGlobals(env);
}
===========================================================================
// sdk\android\src\jni\pc\peer_connection_factory.cc
static void JNI_PeerConnectionFactory_InitializeAndroidGlobals(JNIEnv* jni) {
  if (!factory_static_initialized) {
    JVM::Initialize(GetJVM());
    factory_static_initialized = true;
  }
}
===========================================================================
// modules\utility\source\jvm_android.cc
void JVM::Initialize(JavaVM* jvm) {
  RTC_LOG(INFO) << "JVM::Initialize";
  RTC_CHECK(!g_jvm);
  g_jvm = new JVM(jvm);
}
JVM::JVM(JavaVM* jvm) : jvm_(jvm) {
  RTC_LOG(INFO) << "JVM::JVM";
  RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread.";
  LoadClasses(jni());
}

// TODO(henrika): add more clases here if needed.
struct {
  const char* name;
  jclass clazz;
} loaded_classes[] = {
    {"org/webrtc/voiceengine/BuildInfo", nullptr},
    {"org/webrtc/voiceengine/WebRtcAudioManager", nullptr},
    {"org/webrtc/voiceengine/WebRtcAudioRecord", nullptr},
    {"org/webrtc/voiceengine/WebRtcAudioTrack", nullptr},
};
// Android's FindClass() is trickier than usual because the app-specific
// ClassLoader is not consulted when there is no app-specific frame on the
// stack.  Consequently, we only look up all classes once in native WebRTC.
void LoadClasses(JNIEnv* jni) {
  RTC_LOG(INFO) << "LoadClasses:";
  for (auto& c : loaded_classes) {
    jclass localRef = FindClass(jni, c.name);
    RTC_LOG(INFO) << "name: " << c.name;
    CHECK_EXCEPTION(jni) << "Error during FindClass: " << c.name;
    RTC_CHECK(localRef) << c.name;
    jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
    CHECK_EXCEPTION(jni) << "Error during NewGlobalRef: " << c.name;
    RTC_CHECK(globalRef) << c.name;
    c.clazz = globalRef;
  }
}

4、说完nativeInitializeAndroidGlobals() ,依葫芦画瓢继续看nativeInitializeFieldTrials的源码。

// out\arm64-v8a\gen\sdk\android\generated_peerconnection_jni\PeerConnectionFactory_jni.h
JNI_GENERATOR_EXPORT void Java_org_webrtc_PeerConnectionFactory_nativeInitializeFieldTrials(
    JNIEnv* env,
    jclass jcaller,
    jstring fieldTrialsInitString) {
  return JNI_PeerConnectionFactory_InitializeFieldTrials(env,
      base::android::JavaParamRef<jstring>(env, fieldTrialsInitString));
}
// sdk\android\src\jni\pc\peer_connection_factory.cc
static void JNI_PeerConnectionFactory_InitializeFieldTrials(
    JNIEnv* jni,
    const JavaParamRef<jstring>& j_trials_init_string) {
  std::unique_ptr<std::string>& field_trials_init_string =
      GetStaticObjects().field_trials_init_string;  // 1

  if (j_trials_init_string.is_null()) {
    field_trials_init_string = nullptr;
    field_trial::InitFieldTrialsFromString(nullptr);
    return;
  }
  field_trials_init_string = std::make_unique<std::string>(
      JavaToNativeString(jni, j_trials_init_string));
  RTC_LOG(LS_INFO) << "initializeFieldTrials: " << *field_trials_init_string;
  field_trial::InitFieldTrialsFromString(field_trials_init_string->c_str()); //2
}
// Place static objects into a container that gets leaked so we avoid
// non-trivial destructor.
struct StaticObjectContainer {
  // Field trials initialization string
  std::unique_ptr<std::string> field_trials_init_string;
  // Set in PeerConnectionFactory_InjectLoggable().
  std::unique_ptr<JNILogSink> jni_log_sink;
};
StaticObjectContainer& GetStaticObjects() {
  static StaticObjectContainer* static_objects = new StaticObjectContainer();
  return *static_objects;
}

StaticObjectContainer是一个静态全局引用的结构体,用于保存外部传入的fieldTrials(一个静态全局的结构体变量,为啥要这样写?仔细阅读注释领域代码的魅力。)

至于怎么理解这个field_trials_init_string,这是开启WebRTC的一些“试用特性”,不同平台可能有不一样的特性,贯穿整个webrtc的质量效果。在AndroidRTCDemo中的PeerConnectionClient.java可以看到有三个特性。

final String VIDEO_FLEXFEC_FIELDTRIAL = "WebRTC-FlexFEC-03-Advertised/Enabled/WebRTC-FlexFEC-03/Enabled/";
final String VIDEO_VP8_INTEL_HW_ENCODER_FIELDTRIAL = "WebRTC-IntelVP8/Enabled/";
final String DISABLE_WEBRTC_AGC_FIELDTRIAL = "WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/";

设置的格式一般为: 特性名字/Enabled or Disabled or Default)/ ?。往后具体到模块拆分会经常碰上 field_trials,之后再一一分析。

5、回头看初始化参数option.enableInternalTracer = true;所以接着就是分析initializeInternalTracer,

//out\arm64-v8a\gen\sdk\android\generated_peerconnection_jni\PeerConnectionFactory_jni.h
JNI_GENERATOR_EXPORT void Java_org_webrtc_PeerConnectionFactory_nativeInitializeInternalTracer(
    JNIEnv* env,
    jclass jcaller) {
  return JNI_PeerConnectionFactory_InitializeInternalTracer(env);
}
// sdk\android\src\jni\pc\peer_connection_factory.cc
static void JNI_PeerConnectionFactory_InitializeInternalTracer(JNIEnv* jni) {
  rtc::tracing::SetupInternalTracer();
}
// rtc_base\event_tracer.cc
void SetupInternalTracer() {
  RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
                &g_event_logger, static_cast<EventLogger*>(nullptr),
                new EventLogger()) == nullptr);
  webrtc::SetupEventTracer(InternalGetCategoryEnabled, InternalAddTraceEvent);
}

namespace {
GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
AddTraceEventPtr g_add_trace_event_ptr = nullptr;
}  // namespace

void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
                      AddTraceEventPtr add_trace_event_ptr) {
  g_get_category_enabled_ptr = get_category_enabled_ptr;
  g_add_trace_event_ptr = add_trace_event_ptr;
}

设置两个函数指针,用于trace_event的回调。代码不复杂,整体代码的组织可以慢慢消化。

6、回头继续看initialize的最后一个初始化动作options.loggable,虽然demo当中设置为空,我们还是按照学习的方向,看看Log部分。

// org\webrtc\PeerConnectFactory.java
if (options.loggable != null) {
    Logging.injectLoggable(options.loggable, options.loggableSeverity);
    nativeInjectLoggable(new JNILogging(options.loggable), options.loggableSeverity.ordinal());
} else {
    Logging.d("PeerConnectionFactory", "PeerConnectionFactory was initialized without an injected Loggable. Any existing Loggable will be deleted.");
    Logging.deleteInjectedLoggable();
    nativeDeleteLoggable();
}
// out\arm64-v8a\gen\sdk\android\generated_peerconnection_jni\PeerConnectionFactory_jni.h
JNI_GENERATOR_EXPORT void Java_org_webrtc_PeerConnectionFactory_nativeInjectLoggable(
    JNIEnv* env,
    jclass jcaller,
    jobject jniLogging,
    jint severity) {
  return JNI_PeerConnectionFactory_InjectLoggable(env, base::android::JavaParamRef<jobject>(env,
      jniLogging), severity);
}
// sdk\android\src\jni\pc\peer_connection_factory.cc
static void JNI_PeerConnectionFactory_InjectLoggable(
    JNIEnv* jni,
    const JavaParamRef<jobject>& j_logging,
    jint nativeSeverity) {
  std::unique_ptr<JNILogSink>& jni_log_sink = GetStaticObjects().jni_log_sink;
  // If there is already a LogSink, remove it from LogMessage.
  if (jni_log_sink) {
    rtc::LogMessage::RemoveLogToStream(jni_log_sink.get());
  }
  jni_log_sink = std::make_unique<JNILogSink>(jni, j_logging);
  rtc::LogMessage::AddLogToStream(
      jni_log_sink.get(), static_cast<rtc::LoggingSeverity>(nativeSeverity));
  rtc::LogMessage::LogToDebug(rtc::LS_NONE);
}

这里看到了StaticObjectContainer 的另外一个成员jni_log_sink,底层是LogMessage是用一个静态成员变量 static LogSink* streams_; 维护着一个logSink列表,证明这个Loggable是可以多个。(但WebRTC现在没这样做)

至此,PeerConnectionFactory之PeerConnectionFactory.initialize 全部分析完毕,代码总结框图如下所示。

下一章分析PeerConnectionFactory.Builder.createPeerConnectionFactory,把整个PeerConnectionFactory抽象化理解。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:20:36  更:2021-07-27 16:22:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 6:44:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码