文章轉載自:李煉 原文地址:http://blog.csdn.net/lilian0118/article/details/23337289
前面我們分析到WifiDisplaySource會調用ANetworkSession的接口去創建一個socket,並在這個socket上監聽是否有客戶端的連接請求。先來看看Wifi Display規範的一些流程圖:
從之前的一篇文章中,當ANetworkSession創建好RTSP的listen socket後,就會把它加入到selelct中等待對方的連接,那我們首先來看ANetworkSession的threadLoop方法:
- void ANetworkSession::threadLoop() {
- int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
- {
- Mutex::Autolock autoLock(mLock);
- List<sp<Session> > sessionsToAdd;
- for (size_t i = mSessions.size(); res > 0 && i-- > 0;) {
- const sp<Session> &session = mSessions.valueAt(i);
- int s = session->socket();
- if (s < 0) {
- continue;
- }
- if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
- --res;
- }
- if (FD_ISSET(s, &rs)) {
- if (session->isRTSPServer() || session->isTCPDatagramServer()) {
- struct sockaddr_in remoteAddr;
- socklen_t remoteAddrLen = sizeof(remoteAddr);
- int clientSocket = accept(
- s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
- if (clientSocket >= 0) {
- status_t err = MakeSocketNonBlocking(clientSocket);
- if (err != OK) {
- } else {
- in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
- ALOGI("incoming connection from %d.%d.%d.%d:%d "
- "(socket %d)",
- (addr >> 24),
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- addr & 0xff,
- ntohs(remoteAddr.sin_port),
- clientSocket);
- sp<Session> clientSession =
- new Session(
- mNextSessionID++,
- Session::CONNECTED,
- clientSocket,
- session->getNotificationMessage());
- clientSession->setMode(
- session->isRTSPServer()
- ? Session::MODE_RTSP
- : Session::MODE_DATAGRAM);
- sessionsToAdd.push_back(clientSession);
- }
- } else {
- ALOGE("accept returned error %d (%s)",
- errno, strerror(errno));
- }
- }
- }
- while (!sessionsToAdd.empty()) {
- sp<Session> session = *sessionsToAdd.begin();
- sessionsToAdd.erase(sessionsToAdd.begin());
- mSessions.add(session->sessionID(), session);
- ALOGI("added clientSession %d", session->sessionID());
- }
- }
上面在selelct循環中,首先只有剛創建的RTSP的listen socket,接着如果有客戶端的連接請求,就會跳出select語句,然後調用accept去接收對方的連接。接着會去創建一個新的Session會話,我們去看它的構造函數:
- ANetworkSession::Session::Session(
- int32_t sessionID,
- State state,
- int s,
- const sp<AMessage> ¬ify)
- : mSessionID(sessionID),
- mState(state),
- mMode(MODE_DATAGRAM),
- mSocket(s),
- mNotify(notify),
- mSawReceiveFailure(false),
- mSawSendFailure(false),
- mUDPRetries(kMaxUDPRetries),
- mLastStallReportUs(-1ll) {
- if (mState == CONNECTED) {
- struct sockaddr_in localAddr;
- socklen_t localAddrLen = sizeof(localAddr);
- int res = getsockname(
- mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
- CHECK_GE(res, 0);
- struct sockaddr_in remoteAddr;
- socklen_t remoteAddrLen = sizeof(remoteAddr);
- res = getpeername(
- mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
- CHECK_GE(res, 0);
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("sessionID", mSessionID);
- msg->setInt32("reason", kWhatClientConnected);
- msg->setString("server-ip", localAddrString.c_str());
- msg->setInt32("server-port", ntohs(localAddr.sin_port));
- msg->setString("client-ip", remoteAddrString.c_str());
- msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
- msg->post();
- }
- }
這裏的狀態mState爲CONNECTED,所以會構建一個AMessage並post出去,由前一章的知識,我們知道這裏的mNotify是一個kWhatRTSPNotify消息,會在WifiDisplaySource調用createRTSPServer時傳進來的一個參數,所以這裏創建的AMessage最終還是會被WifiDisplaySource去處理,跳過中間AMessage、ALooperRoster、ALooper的調用關係,我們直接到WifiDisplaySource的onMessageReceived去看如何處理:
- case kWhatRTSPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- break;
- }
- case ANetworkSession::kWhatClientConnected:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
- if (mClientSessionID > 0) {
- ALOGW("A client tried to connect, but we already "
- "have one.");
- mNetSession->destroySession(sessionID);
- break;
- }
- CHECK_EQ(mState, AWAITING_CLIENT_CONNECTION);
- CHECK(msg->findString("client-ip", &mClientInfo.mRemoteIP));
- CHECK(msg->findString("server-ip", &mClientInfo.mLocalIP));
- if (mClientInfo.mRemoteIP == mClientInfo.mLocalIP) {
- // Disallow connections from the local interface
- // for security reasons.
- mNetSession->destroySession(sessionID);
- break;
- }
- CHECK(msg->findInt32(
- "server-port", &mClientInfo.mLocalPort));
- mClientInfo.mPlaybackSessionID = -1;
- mClientSessionID = sessionID;
- ALOGI("We now have a client (%d) connected.", sessionID);
- mState = AWAITING_CLIENT_SETUP;
- status_t err = sendM1(sessionID);
- CHECK_EQ(err, (status_t)OK);
- break;
- }
- case ANetworkSession::kWhatData:
- {
- break;
- }
- case ANetworkSession::kWhatNetworkStall:
- {
- break;
- }
- default:
- TRESPASS();
- }
- break;
- }
這裏的reason是kWhatClientConnected,跳過前面不必要的case語句。如果先前已經連上其它的Sink device,這裏就先斷開之前的連接;如果沒有,將新的SessionID賦予給mClientSessionID,並更改狀態爲AWAITING_CLIENT_SETUP,接着去看sendM1消息,這時候就要開始WifiDisplay M1~M7消息的發送了。
下面列舉M1~M7消息的格式,有興趣的可以去對照代碼分析,我們後面着重分析M6(SetUp)和M7(Play)兩個消息。
M1 reqeust:
OPTIONS * RTSP/1.0
Date: Tue, 29 Fri 2014 02:41:24 +0000
Server: stagefright/1.2 (Linux;Android 4.4)
CSeq: 1
Require: org.wfa.wfd1.0
M1 respose:
RTSP/1.0 200 OK
CSeq: 1
Date: Fri, Jan 01 2014 09:02:37 GMT
Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER
M2 request:
OPTIONS * RTSP/1.0
CSeq: 2
Require: org.wfa.wfd1.0
M2 response:
RTSP/1.0 200 OK
Date: Tue, 29 Fri 2014 02:41:25 +0000
Server: stagefright/1.2 (Linux;Android 4.3)
CSeq: 2
Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER
M3 request:
GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0
Date: Tue, 29 Fri 2014 02:41:25 +0000
Server: stagefright/1.2 (Linux;Android 4.3)
CSeq: 2
Content-Type: text/parameters
Content-Length: 83
wfd_content_protection
wfd_video_formats
wfd_audio_codecs
wfd_client_rtp_ports
M3 response:
RTSP/1.0 200 OK
CSeq: 2
Content-Length: 124
Content-Type: text/parameters
wfd_audio_codecs: LPCM 00000003 00, AAC 00000007 00
wfd_video_formats: 00 00 02 02 0000FFFF 0FFFFFFF 00000FFF 00 0000 0000 01 none none
wfd_content_protection: none
wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19990 0 mode=play
M4 request:
SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0
Date: Tue, 29 Fri 2014 02:41:25 +0000
Server: stagefright/1.2 (Linux;Android 4.3)
CSeq: 3
Content-Type: text/parameters
Content-Length: 247
wfd_video_formats: 00 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none
wfd_audio_codecs: AAC 00000001 00
wfd_presentation_URL: rtsp://192.168.5.200/wfd1.0/streamid=0 none
wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19990 0 mode=play
M4 response:
RTSP/1.0 200 OK
CSeq: 3
M5 request:
SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0
Date: Tue, 29 Fri 2014 02:41:25 +0000
Server: stagefright/1.2 (Linux;Android 4.3)
CSeq: 4
Content-Type: text/parameters
Content-Length: 27
wfd_trigger_method: SETUP
M5 response:
RTSP/1.0 200 OK
CSeq: 4
M6 request:
SETUP rtsp://192.168.5.200/wfd1.0/streamid=0 RTSP/1.0
CSeq: 3
Transport: RTP/AVP/UDP;unicast;client_port=19990
M6 response:
RTSP/1.0 200 OK
Date: Tue, 29 Fri 2014 02:41:25 +0000
Server: stagefright/1.2 (Linux;Android 4.3)
CSeq: 3
Session: 988982966;timeout=30
Transport: RTP/AVP/UDP;unicast;client_port=19990;server_port=22220
我們先來看處理M6 request的方法,代碼在onSetupRequest中:
- status_t WifiDisplaySource::onSetupRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- CHECK_EQ(sessionID, mClientSessionID);
- if (mClientInfo.mPlaybackSessionID != -1) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
- int32_t playbackSessionID = makeUniquePlaybackSessionID();
- sp<AMessage> notify = new AMessage(kWhatPlaybackSessionNotify, id());
- notify->setInt32("playbackSessionID", playbackSessionID);
- notify->setInt32("sessionID", sessionID);
- sp<PlaybackSession> playbackSession =
- new PlaybackSession(
- mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
- looper()->registerHandler(playbackSession);
- AString uri;
- data->getRequestField(1, &uri);
- if (strncasecmp("rtsp://", uri.c_str(), 7)) {
- sendErrorResponse(sessionID, "400 Bad Request", cseq);
- return ERROR_MALFORMED;
- }
- if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) {
- sendErrorResponse(sessionID, "404 Not found", cseq);
- return ERROR_MALFORMED;
- }
- RTPSender::TransportMode rtcpMode = RTPSender::TRANSPORT_UDP;
- if (clientRtcp < 0) {
- rtcpMode = RTPSender::TRANSPORT_NONE;
- }
- status_t err = playbackSession->init(
- mClientInfo.mRemoteIP.c_str(),
- clientRtp,
- rtpMode,
- clientRtcp,
- rtcpMode,
- mSinkSupportsAudio,
- mUsingPCMAudio,
- mSinkSupportsVideo,
- mChosenVideoResolutionType,
- mChosenVideoResolutionIndex,
- mChosenVideoProfile,
- mChosenVideoLevel);
- if (err != OK) {
- looper()->unregisterHandler(playbackSession->id());
- playbackSession.clear();
- }
- mClientInfo.mPlaybackSessionID = playbackSessionID;
- mClientInfo.mPlaybackSession = playbackSession;
- mState = AWAITING_CLIENT_PLAY;
- scheduleReaper();
- scheduleKeepAlive(sessionID);
- return OK;
- }
跳過前面的關於RTSP回覆消息的組織,這裏還會創建一個PlaybackSession對象,並調用它的init方法做初始化。
根據前面的背景知識介紹,設備之間的交互將由Session來管理。在代碼中,Session的概念由WifiDisplaySource的內部類PlaybackSession來表示。先來看和其相關的類圖結構,如下圖所示:
由上圖可知:
- PlaybackSession及其內部類Track都從AHandler派生。故它們的工作也依賴於消息循環和處理。Track代表視頻流或音頻流。
- Track內部通過mMediaPull變量指向一個MediaPull對象。而MediaPull對象則保存了一個MediaSource對象。在PlaybackSession中,此MediaSource的真正類型爲SurfaceMediaSource。它表明該Media的源來自Surface。
- BufferQueue從ISurfaceTexure中派生,根據前面對SurfaceFlinger的介紹,它就是SurfaceFlinger代碼示例中代表虛擬設備的State的surface變量。
- 左圖中,MediaPull通過kWhatPull消息不斷調用MediaSource的read函數。
- 右圖中,SurfaceMediaSource的read函數由通過mBufferQueue來讀取數據。
-
那麼mBufferQueue的數據來自什麼地方呢?對,正是來自SurfaceFlinger。
當然,PlaybackSession拿到這些數據後還需要做編碼,然後才能發送給遠端設備。由於篇幅關係,本文就不再討論這些問題了。
當雙方設備準備就緒後,MediaPull會通過kWhatPull消息處理不斷調用MediaSource的read函數。在SurfaceMediaSource實現的read函數中,來自SurfaceFlinger的混屏後的數據經由BufferQueue傳遞到MediaPull中。