Android 4.2wifidisplay采集
1. 显示设置display,其是最终显示的地方,其的数据格式,其的缓存结构
2. Display其的数据来源,又怎么样操作这些Display当中的数据如截个图,其与SurfaceFlinger有关系,在wifidisplay其的数据来源surfaceMediaSource
3. Android平板当中其是有多个显示设备,如平板的显示屏,HDMI, wifidisplay. 其之间的并存的关系,每个显示设备又有不同的特点,如用户在输入密码的时候,可能希望只在显示屏上显示,不在其他屏幕上显示,这就是安全性。
4. 多个显示设备则需要有一个显示设备类,用来描述显示设备,要一个显示设备管理类来统一管理显示设备的创建,删除,数据的读取。
5. 多个显示设备是并存显示,android平板当中是有多个activity,android通过surfaceFlinger来混合,之后依次输出到各个显示设备当中。出于安全考虑,其应该是:先得到显示设备的特点,将特性输出给surfaceflinger, surfaceflinger要求windowManagerService按要求提供activity集合,如:有安全要求,则不要将有安全特性的activity发给surfaceflinger混合。
6. 用户层通过相应的java层接口来操作display,创建与删除,截个图。
Android frameworks,其是有DisplayManagerService,提供用户来操作display, 内部则是于java层向JNI,层,然后调用c++层native service,SurfaceFlinger ,此处是native的binder通信,是JVM进程与surfaceFlinger进程之间通过binder通信。
a) 在android当中截图有三种方式,A,通过直接读取framebuffer,B.通过display来截个图,首先通过
b) ScreenshotClient screenshot;
c) // 根据ID得到显示设备的操作接口,其的内部:
d) surfaceComposerClient,SurfaceFlinger,DisplayDevice,其进程之间的关系,应用进程与surfaceFlinger进程。
e) sp
f) if (display != NULL && screenshot.update(display) == NO_ERROR) {
g) base = screenshot.getPixels();//得到display缓存的地址方式
h) w = screenshot.getWidth();
i) h = screenshot.getHeight();
j) f = screenshot.getFormat();//ARGB是888还是565的位格式
k) size = screenshot.getSize();//缓存的大小
l) // screenshot.update其会引起surfaceFlinger来进行混屏操作。
C.还是display,但其直接读取surfaceflinger混合之后的数据,不会引起surfaceflinger再次混合,wifidisplay当中的surfaceMediaPlay就是读取此当中的数据。
7.java层:
8.frameworks\base\media\java\android\media\RemoteDisplay.java
上层的核心类RemoteDisplay, Remote + Display,Remote体现在远程,如在wifidisplay当中显示在另一台机器当中。Display其是一个显示设备。其native层的so库与native服务是什么。
首先查看其的JNI层代码
frameworks\base\core\jni\android_media_RemoteDisplay.cpp,其当中有几个类:
1. class NativeRemoteDisplayClient : public BnRemoteDisplayClient
1. 其是native层进程之间通信的服务提供层,其被哪个进程所有调用,应该是MediaPlayerService进程(media service)。Display怎么样与media service有关系呢。
流程:1.创建一个客户端IRemoteDisplayClient,然后传递media service,media Service就使用这个对象与服务端通信,主要用于将MediaService进程通知wifidisplay进程客户端的连接成功,连接失败,连接异常。
2. 此类主要是remoteDisplay建立连接,关闭连接,连接异常的回调。其将数据信息由native层调用JAVA层的回调接口。连接主要是哪个与哪个的连接,本地显示与另一台机器显示之间的连接吗。连接的基础是RTSP的连接建立吗?
其是mediaplayerservice进程当中,当一个客户端连接到wifidisplaySource充当的RTSP服务器当中,mediaplayerservice通过IRemoteDisplayClient的binder通信机制通过上层应用进程。
3. NativeRemoteDisplay类的作用,其是原生代码当中,在保存文件当中,并没有使用些类,而是使用一个类似作用的类:NativeRemoteDisplayEx,其两者的作用均是相同的,在native层保存与mediaplayerService进程的关键变量,暂将变量的地址保存在java层。
2.class NativeRemoteDisplay {
3.private:
4. sp
5. sp
6.}
其包含两个主要接口,其没有什么业务,只一个数据结构。
sp
sp
其为什么需要这样一个包装的数据结构呢?主要是为了下面的native层处理当中创建一个C++对象,然后将此对象的地址发送到上层叠java service当中保存。
4.NativeRemoteDisplayEx与NativeRemoteDisplay的作用相同
2.NativeRemoteDisplayEx{
3. sp
4. sp
5.}
其与NativeRemoteDisplay一样的,其的mClient对象类型是:RemoteDisplayClient,在保存文件当中,其是使用这个对象,其的作用只是一个对象的wrapper,供上java service使用。
3.RemoteDisplayClient : public BnRemoteDisplayClient
其与NativeRemoteDisplayClient均是用于进程之间的通信,bind的服务提供端。其当中的连接成功方法:
// 当客户端连接到服务器,上层应用进程通过binder接口调用surfaceFlinger进程创建一个虚拟显示设备,根据每一个连接显示的要求的不同创建不同的显示设备。
void RemoteDisplayClient::onDisplayConnected(
const sp
uint32_t width,uint32_t height,uint32_t flags) {
// 虚拟显示设备的数据来源,其来源于surfacemediaSource
mSurfaceTexture = surfaceTexture;
// 根据是否有安全性要求,通过surfaceflinger进程创建一个显示设备。
mDisplayBinder = mComposerClient->createDisplay(String8("foo"), false /* secure */);
// 设置显示设备的参数
SurfaceComposerClient::openGlobalTransaction();
mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
//Rect layerStackRect(1280, 720); // XXX fix this.
Rect layerStackRect(width, height); // XXX fix this.
//Rect displayRect(1280, 720);
Rect displayRect(width, height);
mComposerClient->setDisplayProjection(
mDisplayBinder, 0 /* 0 degree rotation */,
layerStackRect,
displayRect);
SurfaceComposerClient::closeGlobalTransaction();
}
4.供RemoteDisplay.java使用native方法
nativeStartRecord:
static jint nativeStartRecord(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
ScopedUtfChars iface(env, ifaceStr);
ALOGI("### nativeStartRecord ###");
sp
sp
sp
interface_cast
CHECK(service.get() != NULL);
// 音频策略起用remote submix方案
enableAudioSubmix(true /* enable */);
// 创建一个网络连接状态接口的回调,其是mediaserviceplay进程调用上层应用进程。
sp
// 通过调用mediaserviceplay进程创建一个remoteDisplay对象,其集成了屏幕采集,语音采集,音视频单独格式编码压缩,再按时间同步合成TS数据流,建立网络连接通信的基础,利用网络连接发送到客户端,或保存到本地文件当中,所以其本质是一个流媒体播放器,也就解释了remotedisplay其的代码是frameworks\av\media\libmediaplayerservice路径当中。这个是多媒体播放器的目录:在流媒体录制与播放,本地多媒体的录制与播放器。
注意remoteDisplay其并不是真正的在surfaceflinger进程当中创建了一个虚拟显示设备,RemoteDisplayClient::onDisplayConnected当中完成的创建一个虚拟显示设备,而remoteDisplay其只是创建一个多媒体播放器。
结论:一台android设备当中的相关进程关系
1. 上层java进程,如Setting进程,录屏幕进程
2. 多媒体进程,mediaplayerservice进程,其创建一个流媒体屏幕录制直播对象
3. 显示进程,surfaceflinger进程,其创建一个虚拟显示设备
// 通过mediaplayerservice进程创建一个流媒体屏幕录制直播模块对象
sp
// 将两个进程之间的bind接口保存起来。
NativeRemoteDisplayEx* wrapper = new NativeRemoteDisplayEx(display, client);
// 返回给java保存
return reinterpret_cast
}
nativePauseRecord
nativeResumeRecord,nativeStopRecord.
其均是控制mediaplayerservice进程当中屏幕直播模块的状态。如暂时直播,重新直播,关闭直播。
frameworks\av\media\libmediaplayerservice\RemoteDisplay.cpp
RemoteDisplay其是核心当中的核心,其处于libmediaplayerservice.so库,其处于的进程是
Mediaplayerservice进程。其的作用:流媒体屏幕直播模块,在此框架当中其应该有的部件:
1. 网络通信框架,负责创建服务器,负责接受客户端的socket连接,管理所有的客户端socket数据接收与发送,这是一个线程
2. 单独一个直播源的数据发送与命令控制
3. 采集数据模块,有视频采集模块,语音采集模块。这是一个线程
4. 数据压缩模块:将各种多媒体数据压缩如视频压缩成h264,语音压缩成AAC,这是一个线程
5. 多媒体数据合成模块:将多媒体数据按时间同步的方法合成为ts数据流,这是一个线程
6. 将多媒体数据按网络数据合成发送:在网络当中发送,以RTP协议格式通过socket发送到客户端。
注意:所有这些部件均在同一个进程当中,线程之间通信使用ALooper,AHandle,AMessager所组成的异步通信框架,其类似于android java层的looper, handler,Message
1. RemoteDisplay
屏幕流媒体直播服务器的入口,
a.创建了网络通信框架ANetworkSession,整体的业务框架WifiDisplaySource等对象。
b.继承了BnRemoteDisplay对象,负责上层进程与屏幕流媒体直播服务器进程的状态控制。
2.ANetworkSession
网络通信框架,负责创建服务器,负责接受客户端的socket连接,管理所有的客户端socket数据接收与发送,这是一个线程当中,不断的接收客户端的连接,不断的管理所有客户端的业务数据的发送与接收。所有客户端连接socket模型是select方式,没有一个连接一个线程,也没有使用epoll,所以其一个线程就搞定了。
其的类有:
A. ANetworkSession:负责创建服务器监控连接,管理所有session
B. NetworkThread:其继承一个thread就是一个线程,在线程回调方法threadLoop当中,调用ANetworkSession.threadLoop方法来接收客户端连接与客户端数据的读取与发送。
C. Session:负责与一个客户端的数据读取与发送。
3.WifiDisplaySource
各种业务的管理类,直播器的状态具体控制。将状态反馈到上层进程当中。
A. 调用onSetupRequestEx模拟一个客户端的连接,创建一个PlaybackSession,且传递其一个AMessage(kWhatPlaybackSessionNotify, id())给PlaybackSession,用于PlaybackSession异步地将消息传递给wifiDisplaySource,调用PlaybackSession::initEx()方法,在改造将wifidisplay的数据保存为本地文件,主要是修改了此的初始化流程。将之前需要真正有客户连接才启动整个采集,编码,传输的流程,修改为直接调用
B. 调用PlaybackSession::initEx()方法,确定视频采集者:SurfaceMediaSource其是原始数据,RepeaterSource:以固定采集频率从SurfaceMediaSource当中读取数据,音频的流程也类似。
C. 在音频,视频数据采集者的基础之上,统一出一个公共类以统一的接口为上层服务,
MediaPuller具体将音视频数据格式从MediaBuffer统一封装成ABuffer。
D. 对于从MediaPuller当中读取过来的数据需要进行压缩编码(codec),Converter具体负责对于音视频数据编码,然后将结果以Message:kWhatConverterNotify发送给PlaybackSession,音频的流程也类似,Converter转换之后的数据,已经是裸数据,如裸H264,AAC,我们就是直接在此处将数据通过LocalSocket发送到上层进程当中。
E. 将采集MediaPuller与编码Converter封装到统一的Track结构当中,方便上层处理,视频数据一个Track,音频数据一个Track.上层在将Track合成Ts数据流的时候很方便。此过程:Converter编码数据之后,发送消息给WifiDisplaySource,WifiDisplaySource根据数据类型调用相应的Track,判断是否需要HDCP加密,根据音视频数据时间同步要求,最终数据调用Sender通过socket发送数据到另一台机器的客户端上面。
F. 对于TS数据在网络上面传递,双向需要约定网络传输协议,在Sender当中具体实现,其是使用RTSP/RTP协议,我们在将TS数据保存为本地文件的时候,具体是需要了此类当中的代码
2. 由start进行初始化操作,在play当中启动。
A. 要建立起SOCKET连接成功,且双方约定好传输多媒体格式。
创建双向约定协议的 sender,且在PlaybackSession约定了一个AMessage与Sender进行通信。
建立成功之后,向上层进程调用连接成功的通知。
【display当中的数据怎么样读取与采集】
数据来源之屏幕
SurfaceMediaSource其相当于fremebuffer,其的读取与写入需要surfaceflinger实时地将混合的图片数据写入到虚拟显示设备当中,但现在还没有创建虚拟显示设备,简单地创建一个SurfaceMediaSource是没有作用的,其需要surfaceflinger实时将混合的界面数据写入到SurfaceMediaSource当中。
A.创建一个虚拟显示设备
mDisplayBinder = mComposerClient->createDisplay(String8("foo"), false /* secure */);
B.虚拟显示设备与surface相关,surface的两个buffer与虚拟显示设备关联,当surfaceflinger实时地将混合的界面数据写入虚拟显示设备当中,其本质就是写了surface的buffer当中,所以用户只要读取surface当中的数据就是读取屏幕当中的数据,也解决了为什么读取一个虚拟显示设备的display不会影响平板性能,而读取主显示设备会影响平板性能。因为虚拟显示设备其读取surfaceflinger混合之后的数据,而主显示设备会触发surfaceflinger进行混合从而影响到了性能。
mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
C.用户读取surface当中的数据
// BufferQueue是继承BnSurfaceTexture
sp
sp
mBufferQueue = source->getBufferQueue();
2. 虚拟显示设备与屏幕源结合
【Surface系统】
直接从native层看surface层怎么样操作,向surfaceflinger分配空间,得到surface的graphicbuffer的起始地址,向其中写入数据,通知surfaceflinger进行刷新显示。
frameworks\native\services\surfaceflinger\tests\resize.cpp作为一个最佳的分析入口,其没有JAVA层的activity.
进程之间的关系,上层应用进程,surfaceflinger进程,两者通过binder进行通信。服务端有两个binder,得到binder服务端的接口方法:通过serviceManager对象传入服务名称,得到客户端binder接口。
A.BnSurfaceComposerClient
B.BnSurfaceComposer
SurfaceComposerClient:
其封装了操作surfaceFlinger的方法,其不是Bn端,其组合Bn端,其最终调用Bn端来进行与surfaceFlinger通信。Bn端是Client.
Client : public BnSurfaceComposerClient
SurfaceControl
:
Surface
SurfaceFlinger
ISurfaceComposer
1. Surface的内部管理
A. GraphicBuffer封装了与硬件相关的操作,为上层应用程序提供了统一的图形内存分配接口。每一个Activity的Surface需要分配一个GraphicBuffer,这些GraphicBuffer怎么样统一的管理,怎么样统一的数据读取与写入。
2. Surface数据的读取
3. BufferQueue与GraphicBuffer之间的关系
WifiDisplaySource : public AHandler
有一个集成管理数据来源的管理类:采集,编码,其也是放置在一个线程当中。
同一个线程当中,使用线程的发送与接收数据
同一个进程当中的不同线程,数据的发送与接收
// 生成一个AMessage的时候绑定一个线程A的id,发送给B线程,B修改此message,如放置数据,B发送此消息,通过消息框架A线程就能够接收到数据。同一个进程的不同线程的内存空间的共享的就可以直接使用。
sp
notify->setInt32("playbackSessionID", playbackSessionID);
// 创建一个线程,且将此线程集成到异步通信框架当中,能够接收异步数据
looper()->registerHandler(playbackSession);
RemoteDisplay其的作用是什么。其的流程是什么呢。
ANetworkSession:其是一个socket service的线程,负责循环等待客户端连接,管理所有客户端连接的数据读写。如果只是保存文件则不需要此类的功能
WifiDisplaySource:其管理所有数据来源。
1. wifiDisplaySource调用playbackSession::initEx,创建一个PlaybackSession线程来创建一个音视频数据来源,视频数据来源于SurfaceMediaSource,音频来源于AudioSource。
2. 添加视频源SurfaceMediaSource,其是怎么样与显示设备所建立关联的,其读取的是surfaceFlinger之后,其怎么样与surfaceFlinger建立关联的。还是之前的数据,其数据的格式与大小。
3. 需要一个类以一定的帧率在surfaceMediaSource当中读取数据,其应该是一个线程且异步读取数据,其使用AHandler,但其没有继承AHandle而是组合使用,因为其继承了MediaSource,其就是RepeaterSource。其使用了装饰者设计模式。
4. 需要一个类以音视频时间同步的方式从视频,音频的数据提供者当中读取数据。
5. 数据的发送:先音频,音频按相应格式组合,如视频按H264,音频按AAC,这个是Track的工作,
6. 其将音视频数据混合且添加音视频时间同步的信息,拼成TS数据TSPacketizer,
7. 将数据按相应的协议发送,其是Sender负责按RTSP,RTP协议发送,其利用ANetworkSession基础设备最终发送数据。
8. SurfaceMediaSource其的数据来源最终来源于Gralloc Buffers
【流程打印】
相关连接
Android截屏浅析
http://blog.sina.com.cn/s/blog_69a04cf4010173fz.html
IGraphicBufferProducer
Android 4.4(KitKat)中的设计模式-Graphics子系统
http://blog.csdn.net/jinzhuojun/article/details/17427491
Android中的GraphicBuffer同步机制-Fence
http://blog.csdn.net/jinzhuojun/article/details/39698317
Android 4.4(KitKat)中的设计模式-Graphics子系统
http://blog.csdn.net/jinzhuojun/article/details/17427491
Stagefright 之 Buffer传输流程
http://blog.csdn.net/ayuppie/article/details/8668462
ScreenMirror buffer ->codec编码传输流程(1/3)
http://www.codes51.com/article/detail_341988.html
SurfaceFlinger GraphicBuffer内存共享缓冲区机制
http://blog.csdn.net/andyhuabing/article/details/7489776
双方设备准备就绪后,MediaPull会通过kWhatPull消息处理不断调用MediaSource的read函数。在SurfaceMediaSource实现的read函数中,来自SurfaceFlinger的混屏后的数据经由BufferQueue传递到MediaPull中
http://blog.csdn.net/innost/article/details/8474683
Android -- SurfaceFlinger 概要分析系列 (一)
http://blog.csdn.net/andyhuabing/article/details/7258113