WFD连接过程代码分析(Sink端)
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> ¬ify,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> ¬ify,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
一.概述 模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到内核中.对于每个模块,系统都要分配一个包含以下数据结构的内存区. 一个module对象,表示模块名的一个以null ...
- 07.显示系统:第005课_Vsync机制:第004节_surface使用vsync过程代码分析
前面的小节中,我们讲解了Vsync机制机制的框架,无论SurfaceFlinger还是APP应用程序,对Vsync的使用,都是按需进行的: 比如应用程序把新界面发送给SurfaceFlinger之后, ...
- Hadoop基于Protocol Buffer的RPC实现代码分析-Server端--转载
原文地址:http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.goog ...
- Brpc代码分析-Server端(九)
2021SC@SDUSC 回到CallMethod函数. 接下来将设置各种成员,如超时时间,response等,因为demo中场景没有设置loadbalancer,所以是SingleServer 通过 ...
- Struts2初始化过程代码分析
根据web.xml的配置 调用FilterDispatcher.init(FilterConfig filterConfig) 1. 创建org.apache.struts2.Dispatcher,并 ...
- WFD简介和连接过程
WiFI Display(WFD)是WiFI Alliance 开发出的一种规范,使多媒体设备之间建立和维持一个基于WiFi的连接,并且利用这个连接推进视频/音频的在目标设备的呈现播放. 1 WFD简 ...
- [WFD]播放DRM视频时,SINK端显示全黑画面或者默认图片
[WFD]播放DRM视频时,SINK端显示全黑画面或者默认图片 [DESCRIPTION] 假设WFD SINK为TV ,和手机通过WFD连接上后,TV画面显示正常. 但是打开第三方视频播放软件(如i ...
- netty 5 alph1源码分析(服务端创建过程)
研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开 ...
- Android WifiDisplay分析二:Wifi display连接过程
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] WifiDisplay之P2P的建立 WifiDisplay之RTSP server的创建 这一章中我们来看Wifi Displ ...
最新文章
- 比特币如何实现—《区块链历史链条》2
- AR智能提升工业效率的4大场景与应用实践!
- Java环境搭建若干问题
- 【转】windows 7系统安装与配置Tomcat服务器环境
- 微信小程序自定义组件 插槽
- python如何安装第三方库
- 年审是当月还是当天_汽车年检提前检车的日期是按原始的还是按检车当月的?...
- 挖掘经典:几乎被人遗忘的HTML七种用法
- 南方cass计算表面积_CASS-工程应用“计算表面积”教程
- 【ParaView教程】2.14 选择
- 老师我做完母带后混音更脏了。混音界四大邪术 | MZD Studios混音10问第2期
- JAVA求素数和模拟条件
- css如何实现自动换行,CSS实现自动换行的方法
- GPU运算能力对(2022.4.5更新)
- 动态添加element标签,数据操作
- FPGA的计数器设计
- 从AFX_MANAGE_STATE(AfxGetStaticModuleState())说起
- VERITA Netbackup日常巡检详细说明
- ElasticSearch实战系列02:中文+拼音混合检索,并高亮显示
- Vue中$refs 使用详解
热门文章
- mongodb 数据导入、导出
- 成为Emacs高手01-学习自带教程
- steins;gate世界线生成匹配器(结果并没有匹配到)
- 2 模拟通信之AM调制解调——理论篇(1)
- 杨焘鸣希腊成交大师班第一天
- HoloLens 2 打包发布过程中 常见问题汇总(长期更新)
- 关于android获取手机号码(主要是移动手机)
- must appear in the GROUP BY clause or be used in an aggregate function
- 51单片机型号命名规则
- [转帖]Marvell兵败中国4G 创始人去职未来几何