上图是之前 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抽象化理解。
|