Android 4.2 Wifi Display核心分析 (一.1)

来源:互联网 发布:淘宝新店的扶持期 编辑:程序博客网 时间:2024/05/22 18:32
进一步,ANetworkSession类中的sendRequest函数无非是将消息保存在mOutBuffer或者 mOutDatagrams供writeMore函数使用。具体而言,当sendRequest函数调用完毕时,threadLoop中的select函 数发现写文件描述符集合ws有写变化,即有socket可写。因而会调用session->writeMore()函数,该session对应于 RTSP客户端,所处状态仍旧为Session::CONNECTING。因此,在调用writeMore函数时,会执行以下语句,
[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  1. mStreamSource->doSomeWork();  

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

阅读全文
0 0