深入理解Google Cast探寻原理
Posted zhanghui_cuc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Google Cast探寻原理相关的知识,希望对你有一定的参考价值。
如何开发一个receiver application
先来简单说一下这个话题。
Receiver本质就是一个网页,由html+CSS和jacascript开发,如果要自定义receiver application,需要在 Google Cast SDK Developer Console注册appID,这个appID将会包含在sender的SDK方法中,标识receiver app,注册收费5$.For more details, see below:
https://developers.google.com/cast/docs/receiver_apps
去哪里找源码
1、
https://support.google.com/googlecast/answer/6121012?hl=zh-Hans
在上面这个页面我们能找到链接,只能用google drive全部下载下来才能看。
下载得到的是chromecast_media_shell_arm.tar.gz
解压源码,在readme中看到
To rebuild libcast_shell_android.so:
1) Extract this archive.
2) From the out_arm_android/Release directory of the archive, execute:
../../third_party/android_tools/ndk//toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ -shared -Wl,-z,now -Wl,-z,relro -Wl,--fatal-warnings -Wl,-z,defs -Wl,-z,noexecstack -fPIC -B../../third_party/binutils/Linux_x64/Release/bin -Wl,-z,relro -Wl,-z,now -fuse-ld=gold -static-libstdc++ -static-libgcc -Wl,--build-id=sha1 -Wl,--no-undefined --sysroot=../../third_party/android_tools/ndk//platforms/android-16/arch-arm -nostdlib -L../../third_party/android_tools/ndk//sources/cxx-stl/llvm-libc++/libs/armeabi-v7a -Wl,--exclude-libs=libgcc.a -Wl,--exclude-libs=libc++_static.a -Wl,--exclude-libs=libcommon_audio.a -Wl,--exclude-libs=libcommon_audio_neon.a -Wl,--exclude-libs=libcommon_audio_sse2.a -Wl,--exclude-libs=libiSACFix.a -Wl,--exclude-libs=libisac_neon.a -Wl,--exclude-libs=libopus.a -Wl,--exclude-libs=libvpx.a -Wl,--icf=all -Wl,-shared,-Bsymbolic -Wl,--version-script=../../build/android/android_no_jni_exports.lst -Wl,-O1 -Wl,--as-needed -Wl,--gc-sections -Wl,--warn-shared-textrel -o libcast_shell_android.so -Wl,-soname=libcast_shell_android.so @libcast_shell_android.so.rsp
所以这一套源码最终用于生成一个so库,那么这个库给谁用?
往下看
2、
https://chromium.googlesource.com/chromium/src/+/master/chromecast/
这里是chromium项目中的chromecast部分源码
在他的chromecast.gyp文件中,看到下面的内容
{
'target_name': 'cast_shell_apk',
'type': 'none',
'dependencies': [
'cast_shell_java',
'libcast_shell_android',
],
'variables': {
'apk_name': 'CastShell',
'manifest_package_name': 'org.chromium.chromecast.shell',
# Note(gunsch): there are no Java files in the android/ directory.
# Unfortunately, the java_apk.gypi target rigidly insists on having
# a java_in_dir directory, but complains about duplicate classes
# from the common cast_shell_java target (shared with internal APK)
# if the actual Java path is used.
# This will hopefully be removable after the great GN migration.
'java_in_dir': 'android',
'android_manifest_path': 'shell/android/apk/AndroidManifest.xml',
'package_name': 'org.chromium.chromecast.shell',
'native_lib_target': 'libcast_shell_android',
'asset_location': '<(PRODUCT_DIR)/assets',
'additional_input_paths': ['<(PRODUCT_DIR)/assets/cast_shell.pak'],
},
'includes': [ '../build/java_apk.gypi' ],
}
可以看到在build – cast_shell.apk的时候需要前面的libcast_shell_android.so
那么是否编出来的这个cast_shell.apk就是实际的AndroidTV中的那个Google Cast呢?
直接find查找一下,真的找到了
./data/data/com.google.android.apps.mediashell/app_cast_shell/cast_shell
但是我们自己build出来的,还跟实际电视中的不一样,详见
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/android_cast_build_instructions.md
此外,在https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/Me78B_4Q4jU 这里也看到
Build/install as described here: https://www.chromium.org/developers/how-tos/build-instructions-cast
As stated there, that apk is not functionally equivalent to Google's Cast apk (which is built with additional non-public code).
综上,关于源码的问题,总结如下:
- Google Cast SDK完全闭源
- Android TV的Google Cast Receiver部分开源,开源的部分在chromium项目源码的chromecast部分,既包含了libcast_shell_android.so依赖的c源文件,又包含了build apk的java文件,但是build这部分源码不能得到和实际TV中的Google Cast Receiver完全一样的东西,还是有一部分代码闭源。实际经验来说,build能成功,但是build出来的chromecast Android Shell apk会crash,不能用,找不到解决方案。
集合log和抓包文件做一些分析
如何找到局域网内的设备的
mDNS协议
Multicast DNS is a way of using familiar DNS programming interfaces, packet formats and operating semantics, in a small network where no conventional DNS server has been installed.
mDNS 协议规定了一个端口,5353。mDNS 基于 UDP 协议。每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁,和我的IP地址是多少。然后其他也有该服务的主机就会响应,也会告诉你,它是谁,它的IP地址是多少。
如何传递控制信息
TLS – 传输层安全协议,encrypted and Json encoded
Detail:https://github.com/thibauts/node-castv2#protocol-description
Example:{ “type”: “STOP”, sessionId: } stops a running instance of an application
Mediaplayback Message
For more details, see below
https://developers.google.com/cast/docs/reference/messages
随log结合java部分源码做流程简述
Line6603
08-30 19:18:32.102 3901 3901 D cr.CastShellActivity: Using scale factor 1.5 to set height 720
08-30 19:18:32.103 3901 3901 D cr.CastShellActivity: onCreate startupUrl: https://storage.googleapis.com/cast-reference-receiver/player.html
CastShellActivity:Activity for managing the Cast shell.
mNativeCastWindow = mCastWindowManager.launchCastWindow(url);
Line6645
08-30 19:18:32.881 3901 3901 I chromium: [3901:3901:INFO:CONSOLE(64)] " [ 0.152s] [cast.receiver.CastReceiverManager] Version: 2.0.0.0044", source: https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js (64)
加载js,receiver SDK
Line6646
08-30 19:18:32.893 3901 3901 I chromium: [3901:3901:INFO:v2_ipc_message_sender.cc(49)] Cast V2 IPC connected: app_id=4F8B3483
Appid:注册在google服务器的id,标识自制的receiver app
Line6650
08-30 19:18:33.182 3901 3901 V CastWindowAndroid: Broadcast ACTION_PAGE_LOADED: scheme=https, host=storage.googleapis.com
CastWindowAndroid:Container for the various UI components that make up a shell window.
called in:CastWindowAndroid.initFromNativeWebContents
initFromNativeWebContents:Initializes the ContentView based on the native tab contents pointer passed in nativeWebContents The pointer to the native tab contents object.
<org.chromium.chromecast.shell.CastWindowAndroid
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout android:id="@+id/contentview_holder"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</org.chromium.chromecast.shell.CastWindowAndroid>
Line6651
08-30 19:18:33.182 3901 3901 D MediaShellActivity: Received intent: action=castPageLoaded
Line6653
08-30 19:18:33.183 3901 3901 I chromium: [3901:3901:INFO:application_manager_impl.cc(770)] App started: 4F8B3483, instance=mediashell-8
08-30 19:18:33.185 3901 4049 I chromium: [3901:4049:INFO:v2_transport.cc(611)] Heartbeat for V2Transport started: max_inactivity=10000000, heartbeat_interval_=5000000
08-30 19:18:33.186 3901 3901 I chromium: [3901:3901:INFO:application_manager_impl.cc(775)] App running: 4F8B3483, instance=mediashell-8
Receiver application启动了
Line6705
08-30 19:18:36.971 611 633 W WebKit : HTMLMediaElement::loadResource(0x20c5c000, https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4, , )
08-30 19:18:36.971 611 633 W WebKit : HTMLMediaElement::loadResource(0x20c5c000) - m_currentSrc -> https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4
Line6715
08-30 19:18:37.524 1688 2743 V MstMetadataRetriever: [MstMeta] setDataSource(https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4)
08-30 19:18:37.529 1688 658 D Downloader: [StartTask] [0xab9b0080]url :https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4
download to parse metadata
Line7210
08-3008-30 19:18:40.889 611 633 W WebKit : HTMLMediaElement::play(0x20c5c000)
Line7237
08-30 19:18:40.906 1688 31668 D mstplayer: [31668] (0x0xab95a5d8)MstPlayer::setDataSource: https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4
简单的通过http下载流,系统播放器播放流,显示在刚才的framelayout上,也即是CastWindowAndroid
Line7271
08-30 19:18:40.908 3901 3901 V MediaPlayer: start, Application: org.chromium.chromecast.shell.CastApplication@bfcbad8
CastApplication:Entry point for the Android cast shell application
CastApplication is called by CastBrowserHelper in initializeBrowser function
CastBrowserHelper:Static, one-time initialization for the browser process.
initializeBrowser:Starts the browser process synchronously, returning success or failure. If the browser has already started, immediately returns true without performing any more initialization. This may only be called on the UI thread.
CastBrowserHelper is called by CastShellActivity in onCreate function
Line7321
08-30 19:18:40.913 1688 676 D Downloader: [StartTask] [0xab7b94b8]url :https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4
download for real play
Line 7771
08-30 19:18:42.666 1679 1896 D vsyncbridge: Init Overlay 0
render start
综上,Android TV上的这个Google Cast Receiver(cast_shell apk)有以下几个点值得注意
- receiver application是个网页,Google Cast SDK提供了js语言的API用于做一些控制操作,都对应到cast_shell apk的CastWindowAndroid,本质是一个framelayout,但是利用chromium / content_public / browser / WebContents.java方法将网页的css等元素以及javascript内容都包含了进去,用的是initFromNativeWebContents方法。这部分的原理是chromium的内容,不用管他。
- cast_shell apk中CastShellActivity, CastWindowAndroid, and native CastWindowAndroid have a one-to-one relationship,很好理解。
- 视频的解码播放还是用的系统原生播放器、解码器,显示在framelayout上。
关键log流程详述-包含sender端和receiver端
欢迎关注我的公众号灰度五十,分享各类音视频、移动开发知识~
文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~
以上是关于深入理解Google Cast探寻原理的主要内容,如果未能解决你的问题,请参考以下文章
深入理解Google Cast开发一个支持Google Cast的sender APP