Android 4.2 Wifi Display核心分析 (一.1)
来源:互联网 发布:淘宝新店的扶持期 编辑:程序博客网 时间:2024/05/22 18:32
- status_t ANetworkSession::Session::writeMore() {
- ...
- if (mState == CONNECTING) {
- int err;
- socklen_t optionLen = sizeof(err);
- CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
- CHECK_EQ(optionLen, (socklen_t)sizeof(err));
- if (err != 0) {
- notifyError(kWhatError, -err, "Connection failed");
- mSawSendFailure = true;
- return -err;
- }
- mState = CONNECTED;
- notify(kWhatConnected);
- return OK;
- }
- CHECK_EQ(mState, CONNECTED);
- CHECK(!mOutBuffer.empty());
- ssize_t n;
- do {
- n = send(mSocket, mOutBuffer.c_str(), mOutBuffer.size(), 0); //客户端向服务端发送OPTIONS请求
- } while (n < 0 && errno == EINTR);
- status_t err = OK;
- if (n > 0) {
- mOutBuffer.erase(0, n);
- } else if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- }
- if (err != OK) {
- notifyError(true , err, "Send failed.");
- mSawSendFailure = true;
- }
- return err;
- }
由于RTSP客户端所处状态为Session::CONNECTING,因此执行if中的语句。这里首先获得相关socket的错误信息,如果没有 任何错误信息,则更改RTSP客户端相应Session状态为 CONNECTED,并且会通知Sink端 kWhatConnected信息将Sink端的状态置为CONNECTED。之后,threadLoop函数会将clientSession加入到 mSessions集合中,并等待接收由客户端发送来的请求信息。也就是在下一次threadLoop执行时,clientSession将会调用 readMore函数从mSocket中接收由客户端发送的请求信息,具体过程如下,
- status_t ANetworkSession::Session::readMore() {
- ...
- char tmp[512];
- ssize_t n;
- do {
- n = recv(mSocket, tmp, sizeof(tmp), 0); //接收客户端请求信息
- } while (n < 0 && errno == EINTR);
- status_t err = OK;
- if (n > 0) {
- mInBuffer.append(tmp, n);
- } else if (n < 0) {
- err = -errno;
- } else {
- err = -ECONNRESET;
- }
- if (!mIsRTSPConnection) {
- ...
- } else {
- for (;;) {
- size_t length;
- if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
- //接收到类型为PlaybackSession::kWhatBinaryData且头部请求信息为'$'才会进入此判断体
- ...
- }
- sp<ParsedMessage> msg =
- ParsedMessage::Parse(
- mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
- //解析处理RTSP消息
- if (msg == NULL) {
- break;
- }
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatData); //向Sink端发送类型为kWhatData的消息请求
- notify->setObject("data", msg);
- notify->post();
- ...
- mInBuffer.erase(0, length);
- if (err != OK) {
- break;
- }
- }
- }
- if (err != OK) {
- notifyError(false /* send */, err, "Recv failed.");
- mSawReceiveFailure = true;
- }
- return err;
- }
该函数会首先接收客户端请求信息,然后利用解析类将信息解析成Sink端能够识别处理的信息,并且向Sink端发送类型为kWhatData的消息请求。现在先来看一下Sink端在接收到消息后是如何进行处理的,
- void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {
- ...
- case ANetworkSession::kWhatData:
- {
- onReceiveClientData(msg); //调用消息处理函数
- break;
- }
- ...
- }
- void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID)); //获得发送消息的sessionID
- sp<RefBase> obj;
- CHECK(msg->findObject("data", &obj)); //获得发送信息的object对象
- sp<ParsedMessage> data =
- static_cast<ParsedMessage *>(obj.get()); //根据object对象获取解析后的数据
- ALOGV("session %d received '%s'",
- sessionID, data->debugString().c_str());
- AString method;
- AString uri;
- data->getRequestField(0, &method);
- int32_t cseq;
- if (!data->findInt32("cseq", &cseq)) { //获取send时加入的消息序号
- sendErrorResponse(sessionID, "400 Bad Request", -1 );
- return ERROR_MALFORMED;
- }
- if (method.startsWith("RTSP/")) { //如果消息以"RTSP/"开头
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
- ssize_t index = mResponseHandlers.indexOfKey(id); //根据 ResponseID获取注册时的mResponseHandlers在KeyedVector<ResponseID, HandleRTSPResponseFunc>这个数据结构中的位置
- if (index < 0) {
- ALOGW("Received unsolicited server response, cseq %d", cseq);
- return ERROR_MALFORMED;
- }
- HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); //根据位置获取注册的回复函数
- mResponseHandlers.removeItemsAt(index);
- status_t err = (this->*func)(sessionID, data); //填充函数指针
- //判断回复中是否有错误信息
- CHECK_EQ(err, (status_t)OK);
- } else {
- AString version;
- data->getRequestField(2, &version);
- if (!(version == AString("RTSP/1.0"))) { //判断RTSP协议版本是否正确
- sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
- return;
- }
- if (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);
- }
- }
- }
首先,Sink端接收函数类型为kWhatData的消息请求,会调用onReceiveClientData函数进行消息处理。该函数在获得发送 信息的object对象、sessionID(此时的sessionID对应于clientSession)等信息后,根据object对象获取解析后的 数据。如果解析后的消息以"RTSP/"开头,注册的回复函数中也没有错误信息,那么就填充之前利用registerResponseHandler函数 注册的函数指针,否则就匹配处理方法类型调用相关处理函数。由于函数sendM1发送的是 OPTIONS请求,这样会直接去匹配处理方法类型,从而会调用onOptionsRequest函数。
- void WifiDisplaySink::onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString 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");
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
- err = sendM2(sessionID);
- CHECK_EQ(err, (status_t)OK);
- }
该函数会将Sink端支持的方法GET_PARAMETER, SET_PARAMETER发送至ANetworkSession。以采取上面的那种readMore,writeMore之间socket通信的方式将 Sink端支持的方法通知给Source端,Source端依旧采取形如Sink端的消息接收处理方式进行处理,
- void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
- ...
- case ANetworkSession::kWhatData:
- {
- status_t err = onReceiveClientData(msg); //调用消息处理函数
- if (err != OK) {
- mClient->onDisplayError(
- IRemoteDisplayClient::kDisplayErrorUnknown);
- }
- break;
- }
- ...
- }
- status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
- ...
- if (method.startsWith("RTSP/")) { //如果消息以"RTSP/"开头
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
- ssize_t index = mResponseHandlers.indexOfKey(id);
- ...
- HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
- mResponseHandlers.removeItemsAt(index);
- status_t err = (this->*func)(sessionID, data); //填充函数指针
- if (err != OK) {
- ALOGW("Response handler for session %d, cseq %d returned "
- "err %d (%s)",
- sessionID, cseq, err, strerror(-err));
- return err;
- }
- ...
- return OK;
- }
- status_t err;
- if (method == "OPTIONS") {
- err = onOptionsRequest(sessionID, cseq, data); 匹配处理方法类型
- } else if (method == "SETUP") {
- err = onSetupRequest(sessionID, cseq, data);
- } else if (method == "PLAY") {
- err = onPlayRequest(sessionID, cseq, data);
- } else if (method == "PAUSE") {
- err = onPauseRequest(sessionID, cseq, data);
- } else if (method == "TEARDOWN") {
- err = onTeardownRequest(sessionID, cseq, data);
- } else if (method == "GET_PARAMETER") {
- err = onGetParameterRequest(sessionID, cseq, data);
- } else if (method == "SET_PARAMETER") {
- err = onSetParameterRequest(sessionID, cseq, data);
- } else {
- sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
- err = ERROR_UNSUPPORTED;
- }
- return err;
- }
首先,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请求请求后,会同样调用以下函数。
- status_t WifiDisplaySource::onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- int32_t playbackSessionID;
- sp<PlaybackSession> playbackSession =
- findPlaybackSession(data, &playbackSessionID);
- if (playbackSession != NULL) {
- playbackSession->updateLiveness();
- }
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append(
- "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, "
- "GET_PARAMETER, SET_PARAMETER\r\n");
- response.append("\r\n");
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- if (err == OK) {
- err = sendM3(sessionID);
- }
- return err;
- }
该函数会将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连接状态回调。
- mClient->onDisplayConnected(
- mClientInfo.mPlaybackSession->getSurfaceTexture(),
- mClientInfo.mPlaybackSession->width(),
- mClientInfo.mPlaybackSession->height(),
- mUsingHDCP
- ? IRemoteDisplayClient::kDisplayFlagSecure
- : 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
- mStreamSource->doSomeWork();
更新Sink端测试播放器PlayerClient在内存中的相关数据。具体流程放在下一回去做分析。
- Android 4.2 Wifi Display核心分析 (一.1)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(一)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- Android 4.2 Wifi Display 之 Settings 源码分析(二)
- 【转载】StageFright框架流程解读
- Android 4.2 Wifi Display 之 Settings 源码分析
- Java 常用排序算法
- Android 4.2 Wifi Display之Settings源码分析(二)
- Android 4.2 Wifi Display核心分析 (一)
- Android 4.2 Wifi Display核心分析 (一.1)
- OMXCodec与OMX事件处理流程
- Java递归算法给基于dom4j创建一个完全相同的节点(包括属性、子节点和文本等)
- Android中图片的三级缓存详解
- Eigen矩阵运算库使用记录
- video/audio playback:setDataSource
- video/audio playback:prepare & start
- Android 联系人 数据库解析
- OpenMax IL: component 概述