WFD连接过程代码分析(Sink端)

WFD建立连接首先必需建立P2P连接,随后WFD使用P2P连接的IP和端口号建立RTSP连接。本文着重分析P2P连接建立后的RTSP连接建立过程,且为一个Source端对Primary Sink端连接,涉及两个Sink端耦合的过程请参阅其他博客,P2P的连接过程不在本文分析范围,请读者自行查阅相关博客。


目录

  • WFD连接过程代码分析(Sink端)
    • 1.建立Session连接
    • 2.RTSP协议交互
      • 2.1 WFD capability negotiation阶段(M1-M4)
      • 2.2 WFD session establishment阶段 (M5-M6)
      • 2.3 AV streaming and control阶段 (M7)
    • 3.RTP/RTCP交互

1.建立Session连接

在main函数会创建创建三个重要的类,一个是消息循环ALooper类,实现各个类之间的异步消息处理,因为代码中WifiDisplaySink,RTPSink,TunnelRenderer等类都继承于AHandler,并且都实现了onMessageReceived函数,所以相应的消息得以在对应的AHandler派生类处理,例如当收到Source端的RTSP Request后,会在WifiDisplaySink的onMessageReceived中做相应的处理;一个是网络通信部分的ANetworkSession类,用于处理Source端发送的网络数据;最后一个是WifiDisplaySink,RTSP协议的实现类。 wfd::main方法主要完成以下任务

  • 创建ALooper异步消息处理机制
  • 创建ANetworkSession网络通信线程
  • 创建WifiDisplaySink用于实现RTSP交互
//frameworks/avmedialibstagefright/wifi-display/wfd.cpp
int main(int argc, char **argv) {using namespace android;ProcessState::self()->startThreadPool();DataSource::RegisterDefaultSniffers();... ...sp<ANetworkSession> session = new ANetworkSession; //网络通信session->start();//开启网络消息监听循环sp<ALooper> looper = new ALooper;  //网络通信sp<WifiDisplaySink> sink = new WifiDisplaySink(session);  //RTSP协议交互显示类WifiDisplaySinklooper->registerHandler(sink); //注册消息处理类WifiDisplaySinkif (connectToPort >= 0) {sink->start(connectToHost.c_str(), connectToPort);//传递IP和端口,开启RTSP交互} else {sink->start(uri.c_str());}looper->start(true /* runOnCallingThread */);//启动消息循环return 0;
}

注:ALooper消息循环机制请参阅博客AHandler AMessage ALooper消息机制

接着看WifiDisplaySink的start方法,由于采用了AHandler消息机制,所以start里面并没有什么实际操作,而是创建一条消息发送出去,转给它的onMessageReceived方法进行处理。
WifiDisplaySink::start方法主要完成以下任务

  • 创建一条消息"kWhatStart"并发送
//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {sp<AMessage> msg = new AMessage(kWhatStart, this);  //创建消息msg->setString("sourceHost", sourceHost);msg->setInt32("sourcePort", sourcePort);msg->post();  //发送消息
}

在onMessageReceived()的"case kWhatStart:"里面,将创建任务交给了ANetworkSession类
WifiDisplaySink::onMessageReceived的case kWhatStart方法主要完成以下任务

  • 调用ANetworkSession创建RTSP客户端
//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {switch (msg->what()) {case kWhatStart:{   int32_t sourcePort;... ...sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this);  //创建一条notify消息,用于建立会话后回调status_t err = mNetSession->createRTSPClient(mRTSPHost.c_str(), sourcePort, notify, &mSessionID);  //调用ANetworkSession::createRTSPClient创建RTSP客户端mState = CONNECTING;  //设置当前状态为连接中break;}... ...default:TRESPASS();
}

ANetworkSession::createRTSPClient方法只是一个方法的封装,也没有实际操作,而是将任务交给了createClientOrServer集中进行处理。
ANetworkSession::createRTSPClient的方法主要完成以下任务

  • 调用createClientOrServer方法
//frameworks/av/media/libstagefright/foundation/ANetworkSession.cpp
status_t ANetworkSession::createRTSPClient(const char *host, unsigned port, const sp<AMessage> &notify,int32_t *sessionID) {return createClientOrServer( //实际调用createClientOrServer方法kModeCreateRTSPClient,NULL /* addr */,0 /* port */,host,port,notify,sessionID);
}

ANetworkSession::createClientOrServer是一个复杂的方法,用于创建Socket和会话等
ANetworkSession::createClientOrServer方法主要完成以下任务

  • 和对端Server端(Source端)建立socket连接
  • 创建会话
//frameworks/av/media/libstagefright/foundation/ANetworkSession.cpp
status_t ANetworkSession::createClientOrServer(Mode mode,const struct in_addr *localAddr,unsigned port,const char *remoteHost,unsigned remotePort,const sp<AMessage> &notify,int32_t *sessionID) {Mutex::Autolock autoLock(mLock);*sessionID = 0;status_t err = OK;int s, res;sp<Session> session;//创建套接字描述符s = socket(AF_INET,(mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,0);... ...err = MakeSocketNonBlocking(s);//构建服务器地址struct sockaddr_in addr;memset(addr.sin_zero, 0, sizeof(addr.sin_zero));addr.sin_family = AF_INET;//将上面传送过来的IP和端口号设置为服务器设备地址if (mode == kModeCreateRTSPClient|| mode == kModeCreateTCPDatagramSessionActive) {struct hostent *ent= gethostbyname(remoteHost);... ...addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;addr.sin_port = htons(remotePort);}else if (localAddr != NULL) {... ...}//connect函数阻塞直到连接成功或发送错误if (mode == kModeCreateRTSPClient|| mode == kModeCreateTCPDatagramSessionActive) {in_addr_t x = ntohl(addr.sin_addr.s_addr);res = connect(s, (const struct sockaddr *)&addr, sizeof(addr)); } else {... ...}//设置会话状态为连接中Session::State state;switch (mode) {case kModeCreateRTSPClient:state = Session::CONNECTING; break;... ...}//创建会话session = new Session(mNextSessionID++,state,s,notify);  if (mode == kModeCreateTCPDatagramSessionActive) {session->setMode(Session::MODE_DATAGRAM);} else if (mode == kModeCreateRTSPClient) {session->setMode(Session::MODE_RTSP);  //设置会话模型为RTSP}mSessions.add(session->sessionID(), session);interrupt();*sessionID = session->sessionID();  //设置会话ID... ...
}

当Session成功建立后接收到来自服务端(Source端)的消息后会回调WifiDisplaySink::onMessageReceived的"case kWhatRTSPNotify"

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {... ...case kWhatRTSPNotify:{... ...switch (reason) {case ANetworkSession::kWhatConnected:{mState = CONNECTED;  //设置状态为已连接if (!mSetupURI.empty()) {status_t err =sendDescribe(mSessionID, mSetupURI.c_str());}break;}... ...}... ...
}

至此Sink端(Client端)和Source端(Server端)建立了Socket连接,或者说两个设备的WFD进程建立了连接。可以开始RTSP协议的交互了。

2.RTSP协议交互

省略UIBC和HDCP交互,RTSP协议交互也分为以下三个阶段

  • WFD capability negotiation
  • WFD session establishment
  • AV streaming and control

下表简单列出M1-M7消息的简述

Message Method Direct 简述
M1 OPTIONS Source -> Sink 打招呼
M2 OPTIONS Source <- Sink 打招呼
M3 GET_PARAMETER Source -> Sink 你支持什么音视频格式
M4 SET_PARAMETER Source -> Sink 我们使用这个格式吧
M5 SETUP Source -> Sink 建立连接吧
M6 SETUP Source <- Sink 建立连接吧
M7 PLAY Source <- Sink 开始发送数据吧

注:每个Message发送后,都会收到到对方发送的一个表明状态的Response

2.1 WFD capability negotiation阶段(M1-M4)

当接收到Source端发送的M1 Request以后

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {... ...case kWhatRTSPNotify:{... ...switch (reason) {case ANetworkSession::kWhatData:  //判断接收到的是数据{onReceiveClientData(msg);  //调用进行处理break;}... ...}... ...
}

调用WifiDisplaySink::onReceiveClientData进行处理

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {int32_t sessionID;CHECK(msg->findInt32("sessionID", &sessionID));  //获取sessionIDsp<RefBase> obj;CHECK(msg->findObject("data", &obj));  //获取数据sp<ParsedMessage> data =static_cast<ParsedMessage *>(obj.get()); //解析数据AString method;AString uri;data->getRequestField(0, &method);int32_t cseq;if (!data->findInt32("cseq", &cseq)) {  //如果没有cseq字段则错误sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);return;}if (method.startsWith("RTSP/")) {  //如果是"RTSP"开头,则是Response,检查是否OK// This is a response.ResponseID id;id.mSessionID = sessionID;id.mCSeq = cseq;ssize_t index = mResponseHandlers.indexOfKey(id);if (index < 0) {ALOGW("Received unsolicited server response, cseq %d", cseq);return;}HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);mResponseHandlers.removeItemsAt(index);status_t err = (this->*func)(sessionID, data);CHECK_EQ(err, (status_t)OK);} else {  //否则的话收到的是RequestAString version;data->getRequestField(2, &version);if (!(version == AString("RTSP/1.0"))) {  //检查RTSP版本sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);return;}if (method == "OPTIONS") {  //M1 Request的method字段是"OPTIONS",所以进入这个分支onOptionsRequest(sessionID, cseq, data);} else if (method == "GET_PARAMETER") {onGetParameterRequest(sessionID, cseq, data);} else if (method == "SET_PARAMETER") {onSetParameterRequest(sessionID, cseq, data);} else {sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);}}
}

接着会调用onOptionsRequest这个方法构建并发送M1 Response,紧接着发送M2 Request

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onOptionsRequest(int32_t sessionID,int32_t cseq,const sp<ParsedMessage> &data) {... ...//构建M1 ResponseAString response = "RTSP/1.0 200 OK\r\n";AppendCommonResponse(&response, cseq);response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n");response.append("\r\n");//发送M1 Responsestatus_t err = mNetSession->sendRequest(sessionID, response.c_str());  //调用ANetworkSession进行发送CHECK_EQ(err, (status_t)OK);//发送M2 Requesterr = sendM2(sessionID);CHECK_EQ(err, (status_t)OK);
}

接着看M2 Request的发送函数sendM2

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::sendM2(int32_t sessionID) {//构建request消息AString request = "OPTIONS * RTSP/1.0\r\n";AppendCommonResponse(&request, mNextCSeq);request.append("Require: org.wfa.wfd1.0\r\n""\r\n");status_t err =mNetSession->sendRequest(sessionID, request.c_str(), request.size()); //调用ANetworkSession进行发送if (err != OK) {return err;                                                                                   }registerResponseHandler(sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);  //注册M2 Response接收函数++mNextCSeq;return OK;
}

上面在发送M2 Request的时候已经注册了接收函数,此时当ANetworkSession接收到M2 Response后会通过ALooper转给WifiDisplaySink::onReceiveM2Response进行处理,处理也很简单,就是看是否OK

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::onReceiveM2Response(int32_t sessionID, const sp<ParsedMessage> &msg) {int32_t statusCode;if (!msg->getStatusCode(&statusCode)) {return ERROR_MALFORMED;}if (statusCode != 200) {return ERROR_UNSUPPORTED;}return OK;
}

接下来等待Source端发送过来M3 Request,和接收M1 Request的过程一样,先到达WifiDisplaySink::onMessageReceived,然后到WifiDisplaySink::onReceiveClientData进行辨识,判断是M3 Request,也就是GET_PARAMETER。

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {... ...if (method == "OPTIONS") {onOptionsRequest(sessionID, cseq, data);} else if (method == "GET_PARAMETER") {  //接收到M3 Request消息onGetParameterRequest(sessionID, cseq, data);} else if (method == "SET_PARAMETER") {onSetParameterRequest(sessionID, cseq, data);} else {sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);}... ...
}

接着看处理函数WifiDisplaySink::onGetParameterRequest,这里会构造并发送M3 Response

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onGetParameterRequest(int32_t sessionID,int32_t cseq,const sp<ParsedMessage> &data) {int32_t statusCode;//这里申明Sink端支持的音视频格式等,具体请参阅《Wi-Fi_Display_Technical_Specification_v2.1_0.pdf》 第6.1小节 RTSP data structuresAString body ="wfd_video_formats: xxx\r\n""wfd_audio_codecs: xxx\r\n""wfd_client_rtp_ports: RTP/AVP/UDP;unicast xxx 0 mode=play\r\n"; //构造M3 ResponseAString response = "RTSP/1.0 200 OK\r\n";AppendCommonResponse(&response, cseq);response.append("Content-Type: text/parameters\r\n");response.append(AStringPrintf("Content-Length: %d\r\n", body.size()));response.append("\r\n");response.append(body);status_t err = mNetSession->sendRequest(sessionID, response.c_str());   //调用ANetworkSession进行发送CHECK_EQ(err, (status_t)OK);
}

特别注意主要上面的body字符串,wfd_video_formats(视频格式)和wfd_audio_codecs(音频格式)后面的xxx需要结合自己平台支持的音视频格式按照《Wi-Fi_Display_Technical_Specification_v2.1_0.pdf》 协议 第6.1小节中的格式顺序自己组装成类似以下的字符串

"wfd_video_formats: 40 00 02 04 0001DEFF 053C7FFF 00000FFF 00 0000 0000 11 none none\r\n"
"wfd_audio_codecs: AAC 00000001 00\r\n"

Source端所有支持的WFD功能都在这里标明,包括是否支持HDCP、UIBC等,格式也是按照《Wi-Fi_Display_Technical_Specification_v2.1_0.pdf》 协议 第6.1小节中的格式顺序。

接下来等待Source端发送过来M4 Request,和接收M1 Request的过程一样,先到达WifiDisplaySink::onMessageReceived,然后到WifiDisplaySink::onReceiveClientData进行辨识,判断是M4 Request,也就是SET_PARAMETER。

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {... ...if (method == "OPTIONS") {onOptionsRequest(sessionID, cseq, data);} else if (method == "GET_PARAMETER") { onGetParameterRequest(sessionID, cseq, data);} else if (method == "SET_PARAMETER") {  //接收到M4 Request消息onSetParameterRequest(sessionID, cseq, data);} else {sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);}... ...
}

接着看onSetParameterRequest方法,这个方法会检查M4发送过来的Request参数本地(Sink端)是否支持,如果支持的话回复M4 Response

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onSetParameterRequest(int32_t sessionID,int32_t cseq,const sp<ParsedMessage> &data) {const char *content = data->getContent();onSetParameterRequest_CheckM4Parameter(content);  //M4的话会检查M4 Request的参数本端是否支持... ...//构造答复AString response = "RTSP/1.0 200 OK\r\n";AppendCommonResponse(&response, cseq);response.append("\r\n");status_t err = mNetSession->sendRequest(sessionID, response.c_str());  //调用ANetworkSession进行发送CHECK_EQ(err, (status_t)OK);
}

至此M1-M4交互完毕,WFD capability negotiation完毕,进入WFD session establishment阶段

2.2 WFD session establishment阶段 (M5-M6)

接下来等待Source端发送过来M5 Request,和接收M1 Request的过程一样,先到达WifiDisplaySink::onMessageReceived,然后到WifiDisplaySink::onReceiveClientData进行辨识,判断是M5 Request,也就是SET_PARAMETER。

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {... ...if (method == "OPTIONS") {onOptionsRequest(sessionID, cseq, data);} else if (method == "GET_PARAMETER") { onGetParameterRequest(sessionID, cseq, data);} else if (method == "SET_PARAMETER") {  //接收到M5 Request消息onSetParameterRequest(sessionID, cseq, data);} else {sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);}... ...
}

接着看onSetParameterRequest方法,首先判断是有方法"SETUP"的话,直接启动M6 Request发送,紧接着发送M5 Response

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
void WifiDisplaySink::onSetParameterRequest(int32_t sessionID,int32_t cseq,const sp<ParsedMessage> &data) {const char *content = data->getContent();//发送M6 Requestif (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {AString uri = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mPresentation_URL.c_str());status_t err =sendSetup(sessionID,uri.c_str());CHECK_EQ(err, (status_t)OK);}... ...//回复M5 ResponseAString response = "RTSP/1.0 200 OK\r\n";AppendCommonResponse(&response, cseq);response.append("\r\n");status_t err = mNetSession->sendRequest(sessionID, response.c_str());  //调用ANetworkSession进行发送CHECK_EQ(err, (status_t)OK);
}

接着看M6 Request的发送函数WifiDisplaySink::sendSetup()

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {//创建RTPSink类用于处理RTP/RTCP协议mRTPSink = new RTPSink(mNetSession, mSurfaceTex); mRTPSink->setHDCP(mHDCP);looper()->registerHandler(mRTPSink);  //RTPSink注册到消息循环status_t err = mRTPSink->init(sUseTCPInterleaving);  //初始化RTPSink,这里传入的sUseTCPInterleaving在头文件里面默认为falseif (err != OK) {looper()->unregisterHandler(mRTPSink->id());mRTPSink.clear();return err;}AString request = AStringPrintf("SETUP %s RTSP/1.0\r\n", uri);AppendCommonResponse(&request, mNextCSeq);if (sUseTCPInterleaving) {request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");} else {int32_t rtpPort = mRTPSink->getRTPPort();request.append(AStringPrintf("Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",rtpPort, rtpPort + 1));}request.append("\r\n");err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());    //调用ANetworkSession进行发送if (err != OK) {return err;}registerResponseHandler(sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);  //注册M6 Response接收函数++mNextCSeq;return OK;
}

上面的RTPSink创建和初始化以及下面的configureTransport用于创建传输RTP数据流的连接,我们先顺着主线看RTSP交互,后面分析这条线
接着看WifiDisplaySink::onReceiveSetupResponse,这里接收M6 Response

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::onReceiveSetupResponse(int32_t sessionID, const sp<ParsedMessage> &msg) {int32_t statusCode;//检查是否错误if (!msg->getStatusCode(&statusCode)) {return ERROR_MALFORMED;}if (statusCode != 200) {return ERROR_UNSUPPORTED;}if (!msg->findString("session", &mPlaybackSessionID)) {return ERROR_MALFORMED;}//获取超时if (!ParsedMessage::GetInt32Attribute(mPlaybackSessionID.c_str(),"timeout",&mPlaybackSessionTimeoutSecs)) {mPlaybackSessionTimeoutSecs = -1;}ssize_t colonPos = mPlaybackSessionID.find(";");if (colonPos >= 0) {// Strip any options from the returned session id.mPlaybackSessionID.erase(colonPos, mPlaybackSessionID.size() - colonPos);}status_t err = configureTransport(msg);  //配置Server端RTP和RTCP传送端口... ...mState = PAUSED;  //设置状态为暂停return sendPlay(sessionID,!mSetupURI.empty()? mSetupURI.c_str() : playCommand.c_str());  //发送M7 Request}

2.3 AV streaming and control阶段 (M7)

发送M7 Request

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) {AString request = AStringPrintf("PLAY %s RTSP/1.0\r\n", uri);AppendCommonResponse(&request, mNextCSeq);request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));request.append("\r\n");status_t err =mNetSession->sendRequest(sessionID, request.c_str(), request.size());   //调用ANetworkSession进行发送if (err != OK) {return err;}registerResponseHandler(sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);  //注册M7 Response接收函数++mNextCSeq;return OK;
}

接收M7 Response

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::onReceivePlayResponse(int32_t sessionID, const sp<ParsedMessage> &msg) {int32_t statusCode;if (!msg->getStatusCode(&statusCode)) {return ERROR_MALFORMED;}if (statusCode != 200) {return ERROR_UNSUPPORTED;}mState = PLAYING;  //设置状态为播放中return OK;
}

至此RTSP交互完毕,我们接着上面的RTPSink创建继续分析

3.RTP/RTCP交互

这里会在端口15550和15551分别创建RTP socket和RTCP socket。用于接收数据流

//frameworks/av/media/libstagefright/wifi-display/sink/RTPSink.cpp
status_t RTPSink::init(bool useTCPInterleaving) {if (useTCPInterleaving) {  //sUseTCPInterleaving传过来为falsereturn OK; } int clientRtp;sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, this);  //创建RTP通知消息sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, this);  //创建RTCP通知消息//创建RTP socketfor (clientRtp = 15550;; clientRtp += 2) { int32_t rtpSession;status_t err = mNetSession->createUDPSession(clientRtp, rtpNotify, &rtpSession); if (err != OK) {ALOGI("failed to create RTP socket on port %d", clientRtp);continue;}   //创建RTCP socketint32_t rtcpSession;err = mNetSession->createUDPSession(clientRtp + 1, rtcpNotify, &rtcpSession);if (err == OK) {mRTPPort = clientRtp;mRTPSessionID = rtpSession;mRTCPSessionID = rtcpSession;break;}   mNetSession->destroySession(rtpSession);} if (mRTPPort == 0) {return UNKNOWN_ERROR;}return OK;
}

配置M6 Response传送过来的server端RTP和RTCP端口

//frameworks/av/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg) {ALOGD("WifiDisplaySink configureTransport");if (sUseTCPInterleaving) {return OK;} AString transport;if (!msg->findString("transport", &transport)) {ALOGE("Missing 'transport' field in SETUP response.");return ERROR_MALFORMED;} AString sourceHost;if (!ParsedMessage::GetAttribute(transport.c_str(), "source", &sourceHost)) {sourceHost = mRTSPHost;} int rtpPort, rtcpPort;AString serverPortStr;if (ParsedMessage::GetAttribute(transport.c_str(), "server_port", &serverPortStr)) {if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) == 2) {if (rtpPort <= 0 || rtpPort > 65535|| rtcpPort <=0 || rtcpPort > 65535|| rtcpPort != rtpPort + 1) { ALOGE("Invalid server_port description '%s'.",serverPortStr.c_str());return ERROR_MALFORMED;}   if (rtpPort & 1) {ALOGW("Server picked an odd numbered RTP port.");}   } else if (sscanf(serverPortStr.c_str(), "%d", &rtpPort) == 1) {rtcpPort = rtpPort + 1;} else {ALOGE("Invalid server_port description '%s'.",serverPortStr.c_str());return ERROR_MALFORMED;}   } else {// ALOGI("Missing 'server_port' in Transport field. using default port");// rtpPort = 33633;// rtcpPort = 33634;ALOGI("Missing 'server_port' in Transport field. so not link to source.(RTP)");return OK;} return OK;  // Now, we not care the "server_port" parameter.// return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
}

等待Source发送数据流过来

//frameworks/av/media/libstagefright/wifi-display/sink/RTPSink.cpp
void RTPSink::onMessageReceived(const sp<AMessage> &msg) {switch (msg->what()) {case kWhatRTPNotify:case kWhatRTCPNotify:                                                                         {int32_t reason;CHECK(msg->findInt32("reason", &reason));switch (reason) {... ...case ANetworkSession::kWhatDatagram:  //是数据流的话{int32_t sessionID;CHECK(msg->findInt32("sessionID", &sessionID));sp<ABuffer> data;CHECK(msg->findBuffer("data", &data));status_t err;if (msg->what() == kWhatRTPNotify) {err = parseRTP(data);  //解析RTP数据} else {err = parseRTCP(data);  //解析RTCP数据}break;}default:TRESPASS();}... ...}
}

在这里解析RTP数据包,然后发送给TunnelRenderer呈现

//frameworks/av/media/libstagefright/wifi-display/sink/RTPSink.cpp
status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) {... ...ssize_t index = mSources.indexOfKey(srcId);if (index < 0) {if (mRenderer == NULL) {sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, this);notifyLost->setInt32("ssrc", srcId);mRenderer = new TunnelRenderer(notifyLost, mSurfaceTex);  //创建TunnelRenderermRenderer->setHDCP(mHDCP);looper()->registerHandler(mRenderer);}sp<AMessage> queueBufferMsg =new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer);  //创建queueBufferMsg消息sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);mSources.add(srcId, source);} else {mSources.valueAt(index)->updateSeq(seqNo, buffer);}return OK;
}

TunnelRenderer收到数据后调用initPlayer创建播放器

//frameworks/av/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) {switch (msg->what()) {case kWhatQueueBuffer:                                                                        {sp<ABuffer> buffer;CHECK(msg->findBuffer("buffer", &buffer));queueBuffer(buffer);  //数据入列if (mStreamSource == NULL) {if (mTotalBytesQueued > 0ll) {initPlayer();  //初始化播放设备} else {ALOGI("Have %lld bytes queued...", (long long)mTotalBytesQueued);}} else {mStreamSource->doSomeWork();}break;}default:TRESPASS();}
}

TunnelRenderer::initPlayer这里是创建播放器的地方,这里直接调用了Android原生的播放器

//frameworks/av/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
void TunnelRenderer::initPlayer() {if (mSurfaceTex == NULL) {mComposerClient = new SurfaceComposerClient;CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);DisplayInfo info;SurfaceComposerClient::getDisplayInfo(0, &info);ssize_t displayWidth = info.w;ssize_t displayHeight = info.h;mSurfaceControl =mComposerClient->createSurface(String8("A Surface"),displayWidth,displayHeight,PIXEL_FORMAT_RGB_565,0);CHECK(mSurfaceControl != NULL);CHECK(mSurfaceControl->isValid());//SurfaceComposerClient::openGlobalTransaction();//CHECK_EQ(mSurfaceControl->getClient()->setLayer(INT_MAX), (status_t)OK);//CHECK_EQ(mSurfaceControl->getClient()->show(), (status_t)OK);SurfaceComposerClient::Transaction{}.setLayer(mSurfaceControl, INT_MAX-1).show(mSurfaceControl).apply();//SurfaceComposerClient::closeGlobalTransaction();mSurface = mSurfaceControl->getSurface();CHECK(mSurface != NULL);}sp<IServiceManager> sm = defaultServiceManager();//获取ServiceManagersp<IBinder> binder = sm->getService(String16("media.player"));//通过binder拿到了名为media.player服务sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);//获得MediaPlayerService服务CHECK(service.get() != NULL);mStreamSource = new StreamSource(this); //数据流mStreamSource->setHDCP(mHDCP);mPlayerClient = new PlayerClient;  //创建播放器客户端mPlayer = service->create(mPlayerClient, (audio_session_t)0);//来创建一个playerCHECK(mPlayer != NULL);//CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK);CHECK_EQ(mPlayer->setDataSource((sp<IStreamSource>)mStreamSource), (status_t)OK);//设置数据流mPlayer->setVideoSurfaceTexture(mSurfaceTex != NULL ? mSurfaceTex : mSurface->getIGraphicBufferProducer());mPlayer->start(); //开始播放
}

这里是调到了Android的MediaPlayer进行实际的播放。

WFD连接过程代码分析(Sink端)相关推荐

  1. 模块加载过程代码分析1

    一.概述 模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到内核中.对于每个模块,系统都要分配一个包含以下数据结构的内存区. 一个module对象,表示模块名的一个以null ...

  2. 07.显示系统:第005课_Vsync机制:第004节_surface使用vsync过程代码分析

    前面的小节中,我们讲解了Vsync机制机制的框架,无论SurfaceFlinger还是APP应用程序,对Vsync的使用,都是按需进行的: 比如应用程序把新界面发送给SurfaceFlinger之后, ...

  3. Hadoop基于Protocol Buffer的RPC实现代码分析-Server端--转载

    原文地址:http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.goog ...

  4. Brpc代码分析-Server端(九)

    2021SC@SDUSC 回到CallMethod函数. 接下来将设置各种成员,如超时时间,response等,因为demo中场景没有设置loadbalancer,所以是SingleServer 通过 ...

  5. Struts2初始化过程代码分析

    根据web.xml的配置 调用FilterDispatcher.init(FilterConfig filterConfig) 1. 创建org.apache.struts2.Dispatcher,并 ...

  6. WFD简介和连接过程

    WiFI Display(WFD)是WiFI Alliance 开发出的一种规范,使多媒体设备之间建立和维持一个基于WiFi的连接,并且利用这个连接推进视频/音频的在目标设备的呈现播放. 1 WFD简 ...

  7. [WFD]播放DRM视频时,SINK端显示全黑画面或者默认图片

    [WFD]播放DRM视频时,SINK端显示全黑画面或者默认图片 [DESCRIPTION] 假设WFD SINK为TV ,和手机通过WFD连接上后,TV画面显示正常. 但是打开第三方视频播放软件(如i ...

  8. netty 5 alph1源码分析(服务端创建过程)

    研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开 ...

  9. Android WifiDisplay分析二:Wifi display连接过程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] WifiDisplay之P2P的建立 WifiDisplay之RTSP server的创建 这一章中我们来看Wifi Displ ...

最新文章

  1. 比特币如何实现—《区块链历史链条》2
  2. AR智能提升工业效率的4大场景与应用实践!
  3. Java环境搭建若干问题
  4. 【转】windows 7系统安装与配置Tomcat服务器环境
  5. 微信小程序自定义组件 插槽
  6. python如何安装第三方库
  7. 年审是当月还是当天_汽车年检提前检车的日期是按原始的还是按检车当月的?...
  8. 挖掘经典:几乎被人遗忘的HTML七种用法
  9. 南方cass计算表面积_CASS-工程应用“计算表面积”教程
  10. 【ParaView教程】2.14 选择
  11. 老师我做完母带后混音更脏了。混音界四大邪术 | MZD Studios混音10问第2期
  12. JAVA求素数和模拟条件
  13. css如何实现自动换行,CSS实现自动换行的方法
  14. GPU运算能力对(2022.4.5更新)
  15. 动态添加element标签,数据操作
  16. FPGA的计数器设计
  17. 从AFX_MANAGE_STATE(AfxGetStaticModuleState())说起
  18. VERITA Netbackup日常巡检详细说明
  19. ElasticSearch实战系列02:中文+拼音混合检索,并高亮显示
  20. Vue中$refs 使用详解

热门文章

  1. mongodb 数据导入、导出
  2. 成为Emacs高手01-学习自带教程
  3. steins;gate世界线生成匹配器(结果并没有匹配到)
  4. 2 模拟通信之AM调制解调——理论篇(1)
  5. 杨焘鸣希腊成交大师班第一天
  6. HoloLens 2 打包发布过程中 常见问题汇总(长期更新)
  7. 关于android获取手机号码(主要是移动手机)
  8. must appear in the GROUP BY clause or be used in an aggregate function
  9. 51单片机型号命名规则
  10. [转帖]Marvell兵败中国4G 创始人去职未来几何