• 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.frameworksasemediajavaandroidmediaRemoteDisplay.java

    上层的核心类RemoteDisplay, Remote + Display,Remote体现在远程,如在wifidisplay当中显示在另一台机器当中。Display其是一个显示设备。其native层的so库与native服务是什么。

    首先查看其的JNI层代码

    frameworksasecorejniandroid_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其的代码是frameworksavmedialibmediaplayerservice路径当中。这个是多媒体播放器的目录:在流媒体录制与播放,本地多媒体的录制与播放器。

    注意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进程当中屏幕直播模块的状态。如暂时直播,重新直播,关闭直播。

    frameworksavmedialibmediaplayerserviceRemoteDisplay.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 ativeservicessurfaceflinger ests esize.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

  • 相关阅读:
    Java菜鸟学习笔记(3)--Java API 文档下载与文档注释的使用
    常用控制台命令大全-Ubuntu篇
    Java菜鸟学习笔记(2)--Ubuntu JDK环境变量配置与常见问题
    Java菜鸟学习笔记(1)--Windows JDK环境变量配置与常见问题
    Java菜鸟学习笔记(x)-《java学习圣经宝典》笔记
    自我管理&时间管理(转)
    方滨兴校长在2013届本科生毕业典礼上的讲话(转)
    static Map的使用
    好的测试网站
    经典css网站
  • 原文地址:https://www.cnblogs.com/pengxinglove/p/5764126.html
Copyright © 2020-2023  润新知