wince +sl 群

Android 4.2wifidisplay采集

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<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);

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的连接建立吗?

wpsCD0B.tmp

其是mediaplayerservice进程当中,当一个客户端连接到wifidisplaySource充当的RTSP服务器当中,mediaplayerservice通过IRemoteDisplayClient的binder通信机制通过上层应用进程。

3. NativeRemoteDisplay类的作用,其是原生代码当中,在保存文件当中,并没有使用些类,而是使用一个类似作用的类:NativeRemoteDisplayEx,其两者的作用均是相同的,在native层保存与mediaplayerService进程的关键变量,暂将变量的地址保存在java层。

2.class NativeRemoteDisplay {

3.private:

4.    sp<IRemoteDisplay> mDisplay;

5.    sp<NativeRemoteDisplayClient> mClient;

6.}

其包含两个主要接口,其没有什么业务,只一个数据结构。

sp<IRemoteDisplay> mDisplay其是进程之间的客户端,是哪几个进程之间通信,SurfaceFlinger进程,其是操作Display的客户端接口。

sp<NativeRemoteDisplayClient> mClient其是进程之间的服务端,与media service进程之间通信。

其为什么需要这样一个包装的数据结构呢?主要是为了下面的native层处理当中创建一个C++对象,然后将此对象的地址发送到上层叠java service当中保存。

4.NativeRemoteDisplayEx与NativeRemoteDisplay的作用相同

2.NativeRemoteDisplayEx{

3.    sp<IRemoteDisplay> mDisplay;

4.    sp<RemoteDisplayClient> mClient;

5.}

其与NativeRemoteDisplay一样的,其的mClient对象类型是:RemoteDisplayClient,在保存文件当中,其是使用这个对象,其的作用只是一个对象的wrapper,供上java service使用。

3.RemoteDisplayClient : public BnRemoteDisplayClient

其与NativeRemoteDisplayClient均是用于进程之间的通信,bind的服务提供端。其当中的连接成功方法:

// 当客户端连接到服务器,上层应用进程通过binder接口调用surfaceFlinger进程创建一个虚拟显示设备,根据每一个连接显示的要求的不同创建不同的显示设备。

void RemoteDisplayClient::onDisplayConnected(

        const sp<ISurfaceTexture> &surfaceTexture,

        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<IServiceManager> sm = defaultServiceManager();

sp<IBinder> binder = sm->getService(String16("media.player"));

    sp<IMediaPlayerService> service =

        interface_cast<IMediaPlayerService>(binder);

    CHECK(service.get() != NULL);

// 音频策略起用remote submix方案

enableAudioSubmix(true /* enable */);

// 创建一个网络连接状态接口的回调,其是mediaserviceplay进程调用上层应用进程。

sp<RemoteDisplayClient> client = new RemoteDisplayClient(env, remoteDisplayObj);

// 通过调用mediaserviceplay进程创建一个remoteDisplay对象,其集成了屏幕采集,语音采集,音视频单独格式编码压缩,再按时间同步合成TS数据流,建立网络连接通信的基础,利用网络连接发送到客户端,或保存到本地文件当中,所以其本质是一个流媒体播放器,也就解释了remotedisplay其的代码是frameworks\av\media\libmediaplayerservice路径当中。这个是多媒体播放器的目录:在流媒体录制与播放,本地多媒体的录制与播放器。

注意remoteDisplay其并不是真正的在surfaceflinger进程当中创建了一个虚拟显示设备,RemoteDisplayClient::onDisplayConnected当中完成的创建一个虚拟显示设备,而remoteDisplay其只是创建一个多媒体播放器。

结论:一台android设备当中的相关进程关系

1. 上层java进程,如Setting进程,录屏幕进程

2. 多媒体进程,mediaplayerservice进程,其创建一个流媒体屏幕录制直播对象

3. 显示进程,surfaceflinger进程,其创建一个虚拟显示设备

// 通过mediaplayerservice进程创建一个流媒体屏幕录制直播模块对象

sp<IRemoteDisplay>display=service->listenForRemoteDisplay(client,String8(iface.c_str()));

// 将两个进程之间的bind接口保存起来。

NativeRemoteDisplayEx* wrapper = new NativeRemoteDisplayEx(display, client);

// 返回给java保存

return reinterpret_cast<jint>(wrapper);

}

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的数据保存为本地文件,主要是修改了此的初始化流程。将之前需要真正有客户连接才启动整个采集,编码,传输的流程,修改为直接调用

wpsCD2C.tmp

wpsCD4C.tmp

wpsCD5C.tmp

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数据保存为本地文件的时候,具体是需要了此类当中的代码

wpsCD7D.tmp

2. 由start进行初始化操作,在play当中启动。

A. 要建立起SOCKET连接成功,且双方约定好传输多媒体格式。

wpsCD8D.tmp

创建双向约定协议的 sender,且在PlaybackSession约定了一个AMessage与Sender进行通信。

wpsCDAD.tmp

wpsCDBE.tmpwpsCDCF.tmpwpsCDDF.tmp

wpsCDFF.tmp

wpsCE10.tmp

wpsCE30.tmp

建立成功之后,向上层进程调用连接成功的通知。

【display当中的数据怎么样读取与采集】

数据来源之屏幕

wpsCE41.tmp

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<BufferQueue> mBufferQueue;

sp<SurfaceMediaSource> source = new SurfaceMediaSource(1280, 720);

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<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id());

    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

posted @ 2016-08-12 10:54  pengxinglove  阅读(4223)  评论(0编辑  收藏  举报
wince +sl 群