Android 4.2 Wifi Display核心分析 (一)

作者: Wolf Geek     转载请说明出处

        上一回,主要介绍了有关WifiDisplay设备连接和建立数据流的流程,这一回将接着向底层前进。由于涉及的内容较多,这里仅仅理清一个大概的头绪,细节的部分将不再展开,如果有什么错误的地方我会及时更正。

       当Source端通过RemoteDisplay.cpp的构造函数注册了Wifidisplay处理线程,并且ANetworkSession初始化了通信所用的数据管道并且开始监听数据流变化后,Source端将通过函数mSource->start(iface)开始建立RTSP连接并且向Sink端传递数据流。接下来,将具体分析其流程。mSource->start(iface)的具体实现在以下文件,

 frameworks/av/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp

[cpp]  view plain copy
  1. status_t WifiDisplaySource::start(const char *iface) {  
  2.     CHECK_EQ(mState, INITIALIZED);  
  3.   
  4.     sp<AMessage> msg = new AMessage(kWhatStart, id());  
  5.     msg->setString("iface", iface);  
  6.   
  7.     sp<AMessage> response;  
  8.     status_t err = msg->postAndAwaitResponse(&response);  
  9.   
  10.     if (err != OK) {  
  11.         return err;  
  12.     }  
  13.   
  14.     if (!response->findInt32("err", &err)) {  
  15.         err = OK;  
  16.     }  
  17.   
  18.     return err;  
  19. }  

该函数首先通过CHECK_EQ来判断当前Source端状态是否为 INITIALIZED,如果是将通过 AMessage创建 标识为kWhatStart的消息,用于在onMessageReceived处理分支中进行匹配,msg->setString(“iface”,iface)用于在传递消息过程中携带网络地址端口信息, msg->postAndAwaitResponse用于返回相应结果。这种方式在Android的流媒体类中相当常见,是一种异步消息处理框架。与该框架相关的类主要有ALooper、AHandler、ALooperRoster等,具体请见这里。

接下来,我们来看看当Source端接收到kWhatStart的消息后做何种处理,

[cpp]  view plain copy
  1. void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {  
  2.     switch (msg->what()) {  
  3.         case kWhatStart:  
  4.         {  
  5.             uint32_t replyID;  
  6.             CHECK(msg->senderAwaitsResponse(&replyID));  
  7.   
  8.             AString iface;  
  9.             CHECK(msg->findString("iface", &iface));  
  10.   
  11.             status_t err = OK;  
  12.   
  13.             ssize_t colonPos = iface.find(":");  //寻找“:”所在位置  
  14.   
  15.             unsigned long port;  
  16.   
  17.             if (colonPos >= 0) {  
  18.                 const char *s = iface.c_str() + colonPos + 1;  
  19.   
  20.                 char *end;  
  21.                 port = strtoul(s, &end, 10);  //得到port号  
  22.   
  23.                 if (end == s || *end != '\0' || port > 65535) {  
  24.                     err = -EINVAL;  
  25.                 } else {  
  26.                     iface.erase(colonPos, iface.size() - colonPos);    
  27.                 }  
  28.             } else {  
  29.                 port = kWifiDisplayDefaultPort;  
  30.             }  
  31.   
  32.             if (err == OK) {  
  33.                 if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {  //将IP地址转化为32位的网络序列IP地址  
  34.                     sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());//建立标识为 kWhatRTSPNotify的消息作为参数传递  
  35.   
  36.                     err = mNetSession->createRTSPServer(  
  37.                             mInterfaceAddr, port, notify, &mSessionID);  
  38.                 } else {  
  39.                     err = -EINVAL;  
  40.                 }  
  41.             }  
  42.   
  43.             if (err == OK) {  
  44.                 mState = AWAITING_CLIENT_CONNECTION;  
  45.             }  
  46.   
  47.             sp<AMessage> response = new AMessage;  
  48.             response->setInt32("err", err);  
  49.             response->postReply(replyID);  
  50.             break;  
  51.         }  
  52.       ...  
  53.    }  
  54. }  

首先,可以看到当Source端接收到消息标识为 kWhatStart的消息后,消息指针msg会通过函数msg->senderAwaitsResponse(&replyID)获取对应于postAndAwaitResponse函数的响应标识,并把处理中的错误信息作为消息载体通过response->postReply(replyID)传递回start(iface)函数。然后,该处理函数将接收到的网络地址端口信息iface拆分为IP地址和端口两个部分,并且利用 createRTSPServer创建RTSP服务端,函数会返回相应的Session编号。如果RTSP服务端创建成功,则将Source端状态更改为 AWAITING_CLIENT_CONNECTION,表示等待客户端连接。

 接着看创建RTSP服务端具体做了哪些动作,

frameworks/av/media/libstagefright/wifi-display/ANetworkSession.cpp

[cpp]  view plain copy
  1. status_t ANetworkSession::createRTSPServer(  
  2.         const struct in_addr &addr, unsigned port,  
  3.         const sp<AMessage> notify, int32_t *sessionID) {  
  4.     return createClientOrServer(  
  5.             kModeCreateRTSPServer,  
  6.             &addr,  
  7.             port,  
  8.             NULL /* remoteHost */,  
  9.             0 /* remotePort */,  
  10.             notify,  
  11.             sessionID);  
  12. }  

可以看到函数createRTSPServer具体又调用了 createClientOrServer函数。在此类中,与建立管道数据流相关的函数都会调用该函数,它们分别是createRTSPClient、createRTSPServer、createUDPSession、createTCPDatagramSession等函数。

其中可以不用关注函数createTCPDatagramSession,这是因为Sink端默认选择了UDP方式进行传输。具体起关键性作用的是在WifiDisplaySink.h头文件中的static const bool sUseTCPInterleaving = false变量 ,具体而言,该变量为false就导致Sink端不会向Source端发送“Transport: RTP/AVP/TCP”这样的请求,这样在Source端就不会将Sender类中的mTransportMode变量设置为TRANSPORT_TCP或者 TRANSPORT_TCP_INTERLEAVED,所以在Sender类中最终并不会调用函数createTCPDatagramSession。        

接下来,将重点看看函数createClientOrServer,其中与建立RTSP服务端无关的步骤先省略不看。

[cpp]  view plain copy
  1. status_t ANetworkSession::createClientOrServer(  
  2.         Mode mode,  
  3.         const struct in_addr *localAddr,  
  4.         unsigned port,  
  5.         const char *remoteHost,  
  6.         unsigned remotePort,  
  7.         const sp<AMessage> notify,  
  8.         int32_t *sessionID) {  
  9.     Mutex::Autolock autoLock(mLock);  
  10.   
  11.     *sessionID = 0;  
  12.     status_t err = OK;  
  13.     int s, res;  
  14.     sp<Session> session;  
  15.   
  16.     s = socket(  
  17.             AF_INET,  
  18.             (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,  
  19.             0);    //建立类型为流套接字的socket  
  20.   
  21.   ...  
  22.   
  23.     if (mode == kModeCreateRTSPServer  
  24.             || mode == kModeCreateTCPDatagramSessionPassive) {  
  25.         const int yes = 1;  
  26.         res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));//允许socket和一个已在使用中的地址捆绑  
  27.     err = MakeSocketNonBlocking(s);  //设置socket为非阻塞方式  
  28.   
  29.     struct sockaddr_in addr;  
  30.     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));  
  31.     addr.sin_family = AF_INET;  
  32.   
  33.     if (mode == kModeCreateRTSPClient  
  34.             || mode == kModeCreateTCPDatagramSessionActive) {  
  35.      ...  
  36.     } else if (localAddr != NULL) {  
  37.         addr.sin_addr = *localAddr;  
  38.         addr.sin_port = htons(port);  
  39.     } else {  
  40.         ...  
  41.     }  
  42.   
  43.     if (mode == kModeCreateRTSPClient  
  44.             || mode == kModeCreateTCPDatagramSessionActive) {  
  45.        ...  
  46.          
  47.     } else {  
  48.         res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));//socket与  
  49. sockaddr结构体指针绑定,sockaddr对应与iface  
  50.         if (res == 0) {  //绑定成功  
  51.             if (mode == kModeCreateRTSPServer  
  52.                     || mode == kModeCreateTCPDatagramSessionPassive) {  
  53.                 res = listen(s, 4);  //作为服务端开始监听rtsp连接请求  
  54.             } else {  
  55.                   ...  
  56.                 }  
  57.             }  
  58.         }  
  59.     }  
  60.      ...  
  61.     Session::State state;  
  62.     switch (mode) {  
  63.         ...  
  64.   
  65.         case kModeCreateRTSPServer:  
  66.             state = Session::LISTENING_RTSP;   //设置Session状态为 LISTENING_RTSP  
  67.             break;  
  68.        ...  
  69.     }  
  70.   
  71.     session = new Session(  
  72.             mNextSessionID++,  
  73.             state,  
  74.             s,  
  75.             notify);    //创建一个session对象,sessionID加1  
  76.   
  77.     ...  
  78.     mSessions.add(session->sessionID(), session); //将该对象加入vector结构中保存  
  79.   
  80.     interrupt();  //向管道写端写数据  
  81.   
  82.     *sessionID = session->sessionID();  //由指针带出当前sessionID  
  83.   
  84.     goto bail;  
  85. ...  
  86.   
  87. bail:  
  88.     return err;  
  89. }  

可以看到建立RTSP服务端的步骤没什么特别的地方,无非是建立socket,绑定地址然后监听等步骤,只不过为了标识不同的请求和区分当前状态,这里用Session结构体和相应的状态来管理对应的socket。

回到RemoteDisplay.cpp的构造函数中,可以看到在Source端调用函数mSource->start(iface)之前就通过mNetSession->start()开启了ANetworkSession线程,接下来看一下mNetSession->start()究竟干了什么事。


[cpp]  view plain copy
  1. status_t ANetworkSession::start(){  
  2.    ...  
  3.    int res =pipe(mPipeFd);  //建立读写管道,控制threadLoop的执行  
  4.    if (res != 0) {  
  5.         mPipeFd[0] = mPipeFd[1] = -1;  
  6.         return -errno;              
  7.    }  
  8.    mThread = new NetworkThread(this); //构造ANetworkSession的内部结构线程  
  9.    status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);//开启该线程,将会调用NetworkThread线程中threadLoop,进一步会调用AnetworkSession::threadLoop()  
  10.    ...  
  11.    return OK;  
  12. }  

    可以看到ANetworkSession采用了管道的方式来控制AnetworkSession::threadLoop()的执行,

[cpp]  view plain copy
  1. void ANetworkSession::threadLoop() {  
  2.     fd_set rs, ws;  
  3.     FD_ZERO(&rs);  
  4.     FD_ZERO(&ws);  
  5.     FD_SET(mPipeFd[0], &rs);  
  6.     int maxFd = mPipeFd[0];  
  7.     {     
  8.       ...  
  9.         for (size_t i = 0; i < mSessions.size(); ++i) {  
  10.             const sp<Session> &session = mSessions.valueAt(i);  
  11.             int s = session->socket();    //遍历并获取vector结构中保存的socket  
  12.             ...  
  13.             if (session->wantsToRead()) {  //判断当前session状态是否需要读  
  14.                 FD_SET(s, &rs);  
  15.                 if (s > maxFd) {  
  16.                     maxFd = s;  
  17.                 }  
  18.             }  
  19.             if (session->wantsToWrite()) {//判断当前session状态是否需要写  
  20.                 FD_SET(s, &ws);  
  21.                 if (s > maxFd) {  
  22.                     maxFd = s;  
  23.                 }  
  24.             }  
  25.         }  
  26.     }  
  27.         int res = select(maxFd + 1, &rs, &ws, NULL, NULL ); //阻塞查看是否有socket可读写  
  28.           ...  
  29.   
  30.     if (FD_ISSET(mPipeFd[0], &rs)) {  
  31.         char c;  
  32.         ssize_t n;  
  33.         do {  
  34.                     n = read(mPipeFd[0], &c, 1);  //只有当管道中有数值时才跳出循环,即类中有其他函数调用了 interrupt函数  
  35.         } while (n < 0 && errno == EINTR);  
  36.         ...  
  37.         --res;     
  38.     }  
  39.   
  40.     {  
  41.         ...   
  42.         List<sp<Session> > sessionsToAdd;  
  43.         for (size_t i = mSessions.size(); res > 0 && i-- > 0;) { //res>0判断是否有socket资源可进行读或写  
  44.             const sp<Session> &session = mSessions.valueAt(i);  
  45.             int s = session->socket();  
  46.                  
  47.             if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {  
  48.                 --res;  
  49.             }  
  50.             if (FD_ISSET(s, &rs)) {  
  51.                 if (session->isRTSPServer() || session->isTCPDatagramServer()) {  
  52. //如果当前状态Session状态为LISTENING_RTSP或LISTENING_TCP_DGRAMS执行下列操作  
  53.                     struct sockaddr_in remoteAddr;  
  54.                     socklen_t remoteAddrLen = sizeof(remoteAddr);  
  55.   
  56.                     int clientSocket = accept(  
  57.                             s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);//从处于listen状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求建立新的socket通道  
  58.                     if (clientSocket >= 0) {  
  59.                         status_t err = MakeSocketNonBlocking(clientSocket);  
  60.                          ...  
  61.   
  62.                             sp<Session> clientSession =  
  63.                                 new Session(  
  64.                                         mNextSessionID++,  
  65.                                         Session::CONNECTED,  
  66.                                         clientSocket,  
  67.                                         session->getNotificationMessage());  
  68. //把所建立RTSP连接的本地地址、客户地址以及端口等信息通过AMessage发送到Source端  
  69.                             clientSession->setIsRTSPConnection(  
  70.                                     session->isRTSPServer());  //mIsRTSPConnection变量设为false  
  71.                   sessionsToAdd.push_back(clientSession);  //将该Session加入到 sessionsToAdd队列尾部  
  72.                     }   
  73.                         ...   
  74.                 } else {  
  75.                     status_t err = session->readMore(); //在建立UDP连接或者RTSP连接已建立的状况且该socket可读,接收相应socket传来的信息,同时通过AMessage的形式与Source或Sink端做数据交换并且通知其做相应处理  
  76.                 }  
  77.             }  
  78.             if (FD_ISSET(s, &ws)) {  
  79.                 status_t err = session->writeMore();  //对有写需求的Session,并且该socket是可写的情况下,向UDP或RTSP连接的另一端发送由Souce或Sink端相应请求中获得的数据  
  80.           ...   
  81.             }  
  82.         }  
  83.         while (!sessionsToAdd.empty()) {  
  84.             sp<Session> session = *sessionsToAdd.begin();  
  85.             sessionsToAdd.erase(sessionsToAdd.begin());  
  86.             mSessions.add(session->sessionID(), session);  //按队列顺序把相关Session加入vector结构中保存  
  87.         }  
  88.     }  
  89. }  
  90. }   

 该 threadLoop()函数首先完成了Sink端RTSP客户端连接请求的接收,其次还负责完成在Source端和Sink端之间的RTSP连接和UDP连接的相关socket读写通信等工作。了解了ANetworkSession线程是如何管理Source端和Sink端之间数据通信的过程后,再次回到Wifi Display的开启流程上来。在threadLoop()函数中RTSP服务端会从处于listen状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求建立新的socket通道。可以看到这个连接请求由rtsp的客户端发出,具体而言,该请求是由createClientOrServer函数中相应的connect函数完成,

frameworks/av/media/libstagefright/wifi-display/ANetworkSession.cpp

[cpp]  view plain copy
  1. if (mode == kModeCreateRTSPClient  
  2.             || mode == kModeCreateTCPDatagramSessionActive) {  
  3.         struct hostent *ent= gethostbyname(remoteHost);  
  4.         addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;  
  5.         addr.sin_port = htons(remotePort);  
  6.         in_addr_t x = ntohl(addr.sin_addr.s_addr);  
  7.         ALOGI("connecting socket %d to %d.%d.%d.%d:%d",  
  8.               s,  
  9.               (x >> 24),  
  10.               (x >> 16) & 0xff,  
  11.               (x >> 8) & 0xff,  
  12.               x & 0xff,  
  13.               ntohs(addr.sin_port));  
  14.   
  15.         res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));  
  16.     }  

到这里,关注的焦点转向到createRTSPClient也就是Rtsp客户端建立的过程,而客户端的建立是由Sink端完成的,因此开始分析Sink端程序。

客户端主程序目前只有测试程序,由于没有设备并没有进行相关测试,该程序编译好只能以命令方式运行,命令有两个选项作为选择,调用格式如下,

./wfd -c xx.xx.xx.xx:port

./wfd -u uri

第一条命令用于将Sink端连接到Source端,需要知道Source端的IP地址及Port端口,第二条命令用于测试sink端连接到rtsp uri地址的效果。这里只来看第一条命令,执行后其会调用以下关键的几个函数,

frameworks/av/media/libstagefright/wifi-display/wfd.cpp

[cpp]  view plain copy
  1. sp<ANetworkSession> session = new ANetworkSession;   
  2. session->start();  
  3. sp<WifiDisplaySink> sink = new WifiDisplaySink(session);  
  4. looper->registerHandler(sink);  
  5. sink->start(connectToHost.c_str(), connectToPort);  
  6.  looper->start(true);  

可以看到,Sink端的启动与Source端没有什么区别,依旧是开启如同RemoteDisplay.cpp中的那几个线程,下面来关注一下WifiDisplaySink线程的启动。首先是用构造函数new出一个操作对象,以下是构造函数,

[cpp]  view plain copy
  1. WifiDisplaySink::WifiDisplaySink(  
  2.         const sp<ANetworkSession> &netSession,  
  3.         const sp<ISurfaceTexture> &surfaceTex)  
  4.     : mState(UNDEFINED),  
  5.       mNetSession(netSession),  
  6.       mSurfaceTex(surfaceTex),  
  7.       mSessionID(0),  
  8.       mNextCSeq(1) {  
  9. }  

上面为WifiDisplaySink的构造函数,可以看到调用时并没有去初始化第二个参数,默认就会被置为空,这是为用户自己构造Sink端程序界面提供的参数。后面会看到在TunnelRenderer类中会直接创建Surface对象供填充Sink端播放器使用。也就是说,如果要写自己的Sink端应用,可以将应用层要作为显示Source端数据流的Surface直接用来填充该构造函数使用。之后就是利用构造出的对象去启动Sink端主线程,

[cpp]  view plain copy
  1. void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {  
  2.    sp<AMessage> msg = new AMessage(kWhatStart, id());  
  3.    msg->setString("sourceHost", sourceHost);  
  4.    msg->setInt32("sourcePort", sourcePort);  
  5.    msg->post();  
  6.               

 又看到了熟悉的异步消息处理框架AMessage类,这里就直接去看onMessageReceived的相应处理,

[cpp]  view plain copy
  1. void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {  
  2.     switch (msg->what()) {  
  3.         case kWhatStart:  
  4.         {  
  5.             int32_t sourcePort;  
  6.   
  7.                   ...  
  8.                 CHECK(msg->findString("sourceHost", &mRTSPHost));  
  9.                 CHECK(msg->findInt32("sourcePort", &sourcePort));  
  10.   
  11.             sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());  
  12.   
  13.             status_t err = mNetSession->createRTSPClient(  
  14.                     mRTSPHost.c_str(), sourcePort, notify, &mSessionID);  
  15. //建立RTSP客户端  
  16.             CHECK_EQ(err, (status_t)OK);  
  17.   
  18.             mState = CONNECTING;  
  19.             break;  
  20.         }  
  21.         ...  
  22.      }  
  23.      
  24. }  

到此,终于看到了Sink端调用ANetworkSession类中的createRTSPClient函数去建立Rtsp客户端。具体就是调用上面提到的connect函数将流套接字s连接至由命令指定的source端网络地址,并且将当前Session状态置为Session::CONNECTING。需要注意的是,只要调用了createClientOrServer的函数都会根据当前不同状态创建session并且加入到mSessions这一Vector结构中以供threadLoop和sendRequest等函数使用。当建立完RTSP服务端和客户端后,与RTSP服务端对应的session是需要读的,相应socket被加入到读文件描述符集合rs中;与RTSP客户端对应的session是需要写的,相应socket被加入到写文件描述符集合ws中。

 Rtsp服务端在监听到客户端连接请求后,在threadLoop中将调用accept函数接收该请求并创建新的clientSocket。如果clientSocket可以被置为非阻塞状态,则通过其创建clientSession,具体就是调用Session的构造函数 sp<Session> clientSession =new Session(mNextSessionID++,Session::CONNECTED,clientSocket,session->getNotificationMessage()),

[cpp]  view plain copy
  1. ANetworkSession::Session::Session(  
  2.         int32_t sessionID,  
  3.         State state,  
  4.         int s,  
  5.         const sp<AMessage> notify)  
  6.     : mSessionID(sessionID),  
  7.       mState(state),  
  8.       mIsRTSPConnection(false),  
  9.       mSocket(s),  
  10.       mNotify(notify),  
  11.       mSawReceiveFailure(false),  
  12.       mSawSendFailure(false) {  
  13.   if (mState == CONNECTED) {     
  14.         struct sockaddr_in localAddr;  
  15.         socklen_t localAddrLen = sizeof(localAddr);  
  16.         int res = getsockname(  
  17.                 mSocket, (struct sockaddr *)&localAddr, &localAddrLen);//根据clientSocket获得本地网络地址  
  18.         CHECK_GE(res, 0);  
  19.         struct sockaddr_in remoteAddr;  
  20.         socklen_t remoteAddrLen = sizeof(remoteAddr);  
  21.   
  22.         res = getpeername(  
  23.                 mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);//根据clientSocket获得连接到Rtsp服务端的客户端网络地址  
  24.         CHECK_GE(res, 0);  
  25.   
  26.         in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);  
  27.         AString localAddrString = StringPrintf(  
  28.                 "%d.%d.%d.%d",  
  29.                 (addr >> 24),  
  30.                 (addr >> 16) & 0xff,  
  31.                 (addr >> 8) & 0xff,  
  32.                 addr & 0xff);  
  33.   
  34.         addr = ntohl(remoteAddr.sin_addr.s_addr);  
  35.         AString remoteAddrString = StringPrintf(  
  36.                 "%d.%d.%d.%d",  
  37.                 (addr >> 24),  
  38.                 (addr >> 16) & 0xff,  
  39.                 (addr >> 8) & 0xff,  
  40.                 addr & 0xff);  
  41.   
  42.         sp<AMessage> msg = mNotify->dup();  //利用AMessage进行通知  
  43.         msg->setInt32("sessionID", mSessionID);  
  44.         msg->setInt32("reason", kWhatClientConnected);  //通知Source端相关信息  
  45.         msg->setString("server-ip", localAddrString.c_str());  
  46.         msg->setInt32("server-port", ntohs(localAddr.sin_port));  
  47.         msg->setString("client-ip", remoteAddrString.c_str());  
  48.         msg->setInt32("client-port", ntohs(remoteAddr.sin_port));  
  49.         msg->post();  
  50.     }  
  51.   }  

可以看到,在创建clientSession时,session的状态被置为 CONNECTED,随后会根据clientSocket获得本地网络地址和连接到Rtsp服务端的客户端网络地址。在构造函数的最后会将相关信息通知Source端。此外,clientSession
Source端在创建RTSP服务端后就一直处于AWAITING_CLIENT_CONNECTION的状态,并且等待接收kWhatRTSPNotify类型的消息。当clientSession创建并向Source端发送通知消息后,Source端就在查询reason类型为kWhatClientConnected的消息。
frameworks/av/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp

[cpp]  view plain copy
  1. case ANetworkSession::kWhatClientConnected:  
  2.                {  
  3.                    int32_t sessionID;  
  4.                    CHECK(msg->findInt32("sessionID", &sessionID));  
  5.                    if (mClientSessionID > 0) {  // mClientSessionID初始为0  
  6.                        ALOGW("A client tried to connect, but we already "  
  7.                              "have one.");  
  8.                        mNetSession->destroySession(sessionID);    
  9.                        break;  
  10.                    }  
  11.                    CHECK_EQ(mState, AWAITING_CLIENT_CONNECTION);  //检查状态  
  12.   
  13.                    CHECK(msg->findString("client-ip", &mClientInfo.mRemoteIP));//接收clientSession传来的信息  
  14.                    CHECK(msg->findString("server-ip", &mClientInfo.mLocalIP));  
  15.   
  16.                    if (mClientInfo.mRemoteIP == mClientInfo.mLocalIP) {  
  17.                      //出于安全考虑不接收由本地网络地址发来的连接  
  18.                        mNetSession->destroySession(sessionID);  
  19.                        break;  
  20.                    }  
  21.   
  22.                    CHECK(msg->findInt32(  
  23.                                "server-port", &mClientInfo.mLocalPort));  
  24.                    mClientInfo.mPlaybackSessionID = -1;  
  25.                    mClientSessionID = sessionID;  
  26.                    ALOGI("We now have a client (%d) connected.", sessionID);  
  27.                    mState = AWAITING_CLIENT_SETUP;  
  28.                    status_t err = sendM1(sessionID);  
  29.                    CHECK_EQ(err, (status_t)OK);  
  30.                    break;  
  31.                }  

  在Source端,onMessageReceived函数会接收由clientSession发送的通知消息,并且将Source端状态置为AWAITING_CLIENT_SETUP。与此同时,其会将clientSession对应的sessionID作为参数标识向RTSP服务端发送OPTIONS请求。

[cpp]  view plain copy
  1. status_t WifiDisplaySource::sendM1(int32_t sessionID) {  
  2.     AString request = "OPTIONS * RTSP/1.0\r\n";  
  3.     AppendCommonResponse(&request, mNextCSeq);  //添加基本消息,如时间、消息序号等等  
  4.   
  5.     request.append(  
  6.             "Require: org.wfa.wfd1.0\r\n"  
  7.             "\r\n");  
  8.   
  9.     status_t err =  
  10.         mNetSession->sendRequest(sessionID, request.c_str(), request.size());  
  11. //向ANetworkSession发送数据等待threadLoop处理  
  12.     if (err != OK) {  
  13.         return err;  
  14.     }  
  15.     registerResponseHandler(  
  16.             sessionID, mNextCSeq, &WifiDisplaySource::onReceiveM1Response);  
  17. //使用函数指针返回状态信息  
  18.     ++mNextCSeq;  //消息序号标识递增  
  19.   
  20.     return OK;  
  21. }  


 进一步,ANetworkSession类中的sendRequest函数无非是将消息保存在mOutBuffer或者mOutDatagrams供writeMore函数使用。具体而言,当sendRequest函数调用完毕时,threadLoop中的select函数发现写文件描述符集合ws有写变化,即有socket可写。因而会调用session->writeMore()函数,该session对应于RTSP客户端,所处状态仍旧为Session::CONNECTING。因此,在调用writeMore函数时,会执行以下语句,

[cpp]  view plain copy
  1. status_t ANetworkSession::Session::writeMore() {  
  2. ...  
  3. if (mState == CONNECTING) {  
  4.         int err;  
  5.         socklen_t optionLen = sizeof(err);  
  6.         CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);  
  7.         CHECK_EQ(optionLen, (socklen_t)sizeof(err));  
  8.         if (err != 0) {  
  9.             notifyError(kWhatError, -err, "Connection failed");  
  10.             mSawSendFailure = true;  
  11.             return -err;  
  12.         }  
  13.         mState = CONNECTED;  
  14.         notify(kWhatConnected);  
  15.         return OK;  
  16.     }  
  17.     CHECK_EQ(mState, CONNECTED);  
  18.     CHECK(!mOutBuffer.empty());  
  19.     ssize_t n;  
  20.     do {  
  21.         n = send(mSocket, mOutBuffer.c_str(), mOutBuffer.size(), 0); //客户端向服务端发送OPTIONS请求  
  22.     } while (n < 0 && errno == EINTR);  
  23.   
  24.     status_t err = OK;  
  25.   
  26.     if (n > 0) {  
  27.         mOutBuffer.erase(0, n);  
  28.     } else if (n < 0) {  
  29.         err = -errno;  
  30.     } else if (n == 0) {  
  31.         err = -ECONNRESET;  
  32.     }  
  33.   
  34.     if (err != OK) {  
  35.         notifyError(true , err, "Send failed.");  
  36.         mSawSendFailure = true;  
  37.     }  
  38.   
  39.     return err;  
  40. }  

由于RTSP客户端所处状态为Session::CONNECTING,因此执行if中的语句。这里首先获得相关socket的错误信息,如果没有任何错误信息,则更改RTSP客户端相应Session状态为 CONNECTED,并且会通知Sink端 kWhatConnected信息将Sink端的状态置为CONNECTED。之后,threadLoop函数会将clientSession加入到mSessions集合中,并等待接收由客户端发送来的请求信息。也就是在下一次threadLoop执行时,clientSession将会调用readMore函数从mSocket中接收由客户端发送的请求信息,具体过程如下,

[cpp]  view plain copy
  1. status_t ANetworkSession::Session::readMore() {  
  2.     ...  
  3.     char tmp[512];  
  4.     ssize_t n;  
  5.     do {  
  6.         n = recv(mSocket, tmp, sizeof(tmp), 0);   //接收客户端请求信息  
  7.     } while (n < 0 && errno == EINTR);  
  8.   
  9.     status_t err = OK;  
  10.   
  11.     if (n > 0) {  
  12.         mInBuffer.append(tmp, n);    
  13.   
  14.     } else if (n < 0) {  
  15.         err = -errno;  
  16.     } else {  
  17.         err = -ECONNRESET;  
  18.     }  
  19.   
  20.     if (!mIsRTSPConnection) {  
  21.                ...  
  22.     } else {  
  23.         for (;;) {  
  24.             size_t length;  
  25.   
  26.             if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {  
  27. //接收到类型为PlaybackSession::kWhatBinaryData且头部请求信息为'$'才会进入此判断体  
  28.                         ...  
  29.                    
  30.             }  
  31.   
  32.             sp<ParsedMessage> msg =  
  33.                 ParsedMessage::Parse(  
  34.                         mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);  
  35. //解析处理RTSP消息  
  36.   
  37.             if (msg == NULL) {  
  38.                 break;  
  39.             }  
  40.   
  41.             sp<AMessage> notify = mNotify->dup();  
  42.             notify->setInt32("sessionID", mSessionID);  
  43.             notify->setInt32("reason", kWhatData);  //向Sink端发送类型为kWhatData的消息请求  
  44.             notify->setObject("data", msg);  
  45.             notify->post();  
  46.              ...  
  47.             mInBuffer.erase(0, length);  
  48.   
  49.             if (err != OK) {  
  50.                 break;  
  51.             }  
  52.         }  
  53.     }  
  54.   
  55.     if (err != OK) {  
  56.         notifyError(false /* send */, err, "Recv failed.");  
  57.         mSawReceiveFailure = true;  
  58.     }  
  59.   
  60.     return err;  
  61. }  

该函数会首先接收客户端请求信息,然后利用解析类将信息解析成Sink端能够识别处理的信息,并且向Sink端发送类型为kWhatData的消息请求。现在先来看一下Sink端在接收到消息后是如何进行处理的,

[cpp]  view plain copy
  1. void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {  
  2.          ...  
  3.   case ANetworkSession::kWhatData:  
  4.                 {  
  5.                    onReceiveClientData(msg);  //调用消息处理函数  
  6.                 
  7.                     break;  
  8.                 }  
  9.           ...  
  10. }  
  11. void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {  
  12.      int32_t sessionID;  
  13.     CHECK(msg->findInt32("sessionID", &sessionID));  //获得发送消息的sessionID  
  14.     sp<RefBase> obj;  
  15.     CHECK(msg->findObject("data", &obj));  //获得发送信息的object对象  
  16.     sp<ParsedMessage> data =  
  17.         static_cast<ParsedMessage *>(obj.get());  //根据object对象获取解析后的数据  
  18.     ALOGV("session %d received '%s'",  
  19.           sessionID, data->debugString().c_str());  
  20.     AString method;  
  21.     AString uri;  
  22.     data->getRequestField(0, &method);      
  23.     int32_t cseq;  
  24.     if (!data->findInt32("cseq", &cseq)) {     //获取send时加入的消息序号  
  25.         sendErrorResponse(sessionID, "400 Bad Request", -1 );  
  26.         return ERROR_MALFORMED;  
  27.     }  
  28.   
  29.     if (method.startsWith("RTSP/")) {  //如果消息以"RTSP/"开头  
  30.   
  31.         ResponseID id;     
  32.         id.mSessionID = sessionID;  
  33.         id.mCSeq = cseq;  
  34.         ssize_t index = mResponseHandlers.indexOfKey(id);  //根据 ResponseID获取注册时的mResponseHandlers在KeyedVector<ResponseID, HandleRTSPResponseFunc>这个数据结构中的位置  
  35.         if (index < 0) {  
  36.             ALOGW("Received unsolicited server response, cseq %d", cseq);  
  37.             return ERROR_MALFORMED;  
  38.         }  
  39.   
  40.         HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); //根据位置获取注册的回复函数  
  41.         mResponseHandlers.removeItemsAt(index);  
  42.   
  43.         status_t err = (this->*func)(sessionID, data);  //填充函数指针  
  44.          //判断回复中是否有错误信息  
  45.         CHECK_EQ(err, (status_t)OK);  
  46.     } else {  
  47.         AString version;  
  48.         data->getRequestField(2, &version);  
  49.         if (!(version == AString("RTSP/1.0"))) {  //判断RTSP协议版本是否正确  
  50.             sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);  
  51.             return;  
  52.         }  
  53.   
  54.         if (method == "OPTIONS") {  
  55.             onOptionsRequest(sessionID, cseq, data);  
  56.         } else if (method == "GET_PARAMETER") {  
  57.             onGetParameterRequest(sessionID, cseq, data);  
  58.         } else if (method == "SET_PARAMETER") {  
  59.             onSetParameterRequest(sessionID, cseq, data);  
  60.         } else {  
  61.             sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);  
  62.         }  
  63.     }  
  64. }  

首先,Sink端接收函数类型为kWhatData的消息请求,会调用onReceiveClientData函数进行消息处理。该函数在获得发送信息的object对象、sessionID(此时的sessionID对应于clientSession)等信息后,根据object对象获取解析后的数据。如果解析后的消息以"RTSP/"开头,注册的回复函数中也没有错误信息,那么就填充之前利用registerResponseHandler函数注册的函数指针,否则就匹配处理方法类型调用相关处理函数。由于函数sendM1发送的是 OPTIONS请求,这样会直接去匹配处理方法类型,从而会调用onOptionsRequest函数。

[cpp]  view plain copy
  1. void WifiDisplaySink::onOptionsRequest(  
  2.         int32_t sessionID,  
  3.         int32_t cseq,  
  4.         const sp<ParsedMessage> &data) {  
  5.     AString response = "RTSP/1.0 200 OK\r\n";  
  6.     AppendCommonResponse(&response, cseq);  
  7.     response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n");  
  8.     response.append("\r\n");  
  9.   
  10.     status_t err = mNetSession->sendRequest(sessionID, response.c_str());  
  11.     CHECK_EQ(err, (status_t)OK);  
  12.   
  13.     err = sendM2(sessionID);  
  14.     CHECK_EQ(err, (status_t)OK);  
  15. }  

该函数会将Sink端支持的方法GET_PARAMETER, SET_PARAMETER发送至ANetworkSession。以采取上面的那种readMore,writeMore之间socket通信的方式将Sink端支持的方法通知给Source端,Source端依旧采取形如Sink端的消息接收处理方式进行处理,

 

[cpp]  view plain copy
  1. void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {  
  2.          ...  
  3.   case ANetworkSession::kWhatData:  
  4.                 {  
  5.                     status_t err = onReceiveClientData(msg);  //调用消息处理函数  
  6.   
  7.                     if (err != OK) {  
  8.                         mClient->onDisplayError(  
  9.                                 IRemoteDisplayClient::kDisplayErrorUnknown);  
  10.                     }  
  11.                     break;  
  12.                 }  
  13.           ...  
  14. }  
  15.   
  16. status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {  
  17.   
  18.        ...  
  19.         if (method.startsWith("RTSP/")) {  //如果消息以"RTSP/"开头  
  20.   
  21.         ResponseID id;     
  22.         id.mSessionID = sessionID;  
  23.         id.mCSeq = cseq;  
  24.         ssize_t index = mResponseHandlers.indexOfKey(id);    
  25.         ...  
  26.         HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);   
  27.         mResponseHandlers.removeItemsAt(index);  
  28.   
  29.         status_t err = (this->*func)(sessionID, data);  //填充函数指针  
  30.          if (err != OK) {  
  31.             ALOGW("Response handler for session %d, cseq %d returned "  
  32.                   "err %d (%s)",  
  33.                   sessionID, cseq, err, strerror(-err));  
  34.   
  35.             return err;  
  36.         }  
  37.           ...  
  38.         return OK;  
  39.     }  
  40.     status_t err;  
  41.     if (method == "OPTIONS") {  
  42.         err = onOptionsRequest(sessionID, cseq, data);   匹配处理方法类型  
  43.     } else if (method == "SETUP") {  
  44.         err = onSetupRequest(sessionID, cseq, data);  
  45.     } else if (method == "PLAY") {  
  46.         err = onPlayRequest(sessionID, cseq, data);  
  47.     } else if (method == "PAUSE") {  
  48.         err = onPauseRequest(sessionID, cseq, data);  
  49.     } else if (method == "TEARDOWN") {  
  50.         err = onTeardownRequest(sessionID, cseq, data);  
  51.     } else if (method == "GET_PARAMETER") {  
  52.         err = onGetParameterRequest(sessionID, cseq, data);  
  53.     } else if (method == "SET_PARAMETER") {  
  54.         err = onSetParameterRequest(sessionID, cseq, data);  
  55.     } else {  
  56.         sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);  
  57.   
  58.         err = ERROR_UNSUPPORTED;  
  59.     }  
  60.   
  61.     return err;  
  62. }  

 首先,Source端接收函数类型为kWhatData的消息请求,会调用onReceiveClientData函数进行消息处理。该函数在获得发送信息的object对象、sessionID(此时的sessionID对应于clientSession)等信息后,根据object对象获取解析后的数据。由于此时消息以"RTSP/"开头,则会填充函数sendM1 中利用registerResponseHandler函数注册的函数指针onReceiveM1Response。如果回复信息中没有错误信息,即RTSP服务端返回的状态码为200,则表示RTSP服务端提供Sink端需要的那些RTSP方法。

与此同时,Sink端还会接下去执行,也就是调用sendM2函数,该函数如同sendM1函数,依旧是发送OPTIONS请求,只不过方向正好相反,是由Sink端发送Source端接收。具体流程与上面的流程一致,Source端在接收到OPTIONS请求请求后,会同样调用以下函数。

[cpp]  view plain copy
  1. status_t WifiDisplaySource::onOptionsRequest(  
  2.         int32_t sessionID,  
  3.         int32_t cseq,  
  4.         const sp<ParsedMessage> &data) {  
  5.     int32_t playbackSessionID;  
  6.     sp<PlaybackSession> playbackSession =  
  7.         findPlaybackSession(data, &playbackSessionID);  
  8.   
  9.     if (playbackSession != NULL) {  
  10.         playbackSession->updateLiveness();  
  11.     }  
  12.   
  13.     AString response = "RTSP/1.0 200 OK\r\n";    
  14.     AppendCommonResponse(&response, cseq);  
  15.   
  16.     response.append(  
  17.             "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, "  
  18.             "GET_PARAMETER, SET_PARAMETER\r\n");     
  19.   
  20.     response.append("\r\n");  
  21.   
  22.     status_t err = mNetSession->sendRequest(sessionID, response.c_str());  
  23.   
  24.     if (err == OK) {  
  25.         err = sendM3(sessionID);  
  26.     }  
  27.   
  28.     return err;  
  29. }  

该函数会将Source端支持的所有方法通知给Sink端。同样的,Sink端也会填充类似的状态信息函数onReceiveM2Response。 如果回复信息中没有错误信息,即RTSP服务端返回的状态码为200,则表示RTSP服务端提供Source端需要的那些RTSP方法。

接下来,Source端会因为发送onOptionsRequest请求成功而继续调用sendM3函数以GET_PARAMETER方法发送获得Sink端所支持媒体流的内容保护(HDCP)支持性、视频格式信息、音频编码格式信息以及rtp客户端端口等信息的请求。Sink端则会调用onGetParameterRequest处理函数回复Sink端支持的相关信息。目前代码里可以看到Sink端并不支持内容保护特性。接下来,基本流程符合RTSP在进行媒体流通信的流程,这里就不做太多介绍。当流程调用到函数sendM5时,Source端会向Sink端发送SETUP命令,Sink端调用sendSetup启动sink端RTP线程,其中会利用createUDPSession创建UDP连接来传递音视频数据流。之后Source端会调用onSetupRequest函数做相应响应,如开启与编码Source端、打包相关的PlaybackSession线程,此过程算是Source端的核心代码,涉及到的内容较多如SurfaceFlinger等,希望有机会详细展开来介绍。当Setup流程结束后,Sink端通过sendPlay函数发送PLAY请求,Source端调用onPlayRequest函数做相应响应。如果playbackSession能够正常播放,则通过调用finishPlay函数完成开始播放的最后一些任务,如向Source端发送kWhatSessionEstablished消息,调用IRemoteDisplayClient的onDisplayConnected函数向应用层提供Wifi Display连接状态回调。

[cpp]  view plain copy
  1. mClient->onDisplayConnected(  
  2.                           mClientInfo.mPlaybackSession->getSurfaceTexture(),  
  3.                           mClientInfo.mPlaybackSession->width(),  
  4.                           mClientInfo.mPlaybackSession->height(),  
  5.                           mUsingHDCP  
  6.                               ? IRemoteDisplayClient::kDisplayFlagSecure  
  7.                               : 0);  

该函数执行成功后,还会将Source状态由ABOUT_TO_PLAY改变为PLAYING。

当Sink端接收到Source端的数据流后,会调用/av/media/libstagefright/wifi-display/sink/RTPSink.cpp

下的parseRTP函数向TunnelRenderer类发送kWhatQueueBuffer消息,使其通过调用以下函数

/av/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp

[cpp]  view plain copy
  1. mStreamSource->doSomeWork();  

更新Sink端测试播放器PlayerClient在内存中的相关数据。具体流程放在下一回去做分析。


 原文在 http://blog.csdn.net/mznewfacer/article/details/8500102

你可能感兴趣的:(Android 4.2 Wifi Display核心分析 (一))