diff options
Diffstat (limited to 'media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp')
-rw-r--r-- | media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp new file mode 100644 index 0000000..5db2099 --- /dev/null +++ b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp @@ -0,0 +1,917 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "WifiDisplaySink" +#include <utils/Log.h> + +#include "WifiDisplaySink.h" + +#include "DirectRenderer.h" +#include "MediaReceiver.h" +#include "ParsedMessage.h" +#include "TimeSyncer.h" + +#include <cutils/properties.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/Utils.h> + +namespace android { + +// static +const AString WifiDisplaySink::sUserAgent = MakeUserAgent(); + +WifiDisplaySink::WifiDisplaySink( + uint32_t flags, + const sp<ANetworkSession> &netSession, + const sp<IGraphicBufferProducer> &bufferProducer, + const sp<AMessage> ¬ify) + : mState(UNDEFINED), + mFlags(flags), + mNetSession(netSession), + mSurfaceTex(bufferProducer), + mNotify(notify), + mUsingTCPTransport(false), + mUsingTCPInterleaving(false), + mSessionID(0), + mNextCSeq(1), + mIDRFrameRequestPending(false), + mTimeOffsetUs(0ll), + mTimeOffsetValid(false), + mSetupDeferred(false), + mLatencyCount(0), + mLatencySumUs(0ll), + mLatencyMaxUs(0ll), + mMaxDelayMs(-1ll) { + // We support any and all resolutions, but prefer 720p30 + mSinkSupportedVideoFormats.setNativeResolution( + VideoFormats::RESOLUTION_CEA, 5); // 1280 x 720 p30 + + mSinkSupportedVideoFormats.enableAll(); +} + +WifiDisplaySink::~WifiDisplaySink() { +} + +void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) { + sp<AMessage> msg = new AMessage(kWhatStart, id()); + msg->setString("sourceHost", sourceHost); + msg->setInt32("sourcePort", sourcePort); + msg->post(); +} + +void WifiDisplaySink::start(const char *uri) { + sp<AMessage> msg = new AMessage(kWhatStart, id()); + msg->setString("setupURI", uri); + msg->post(); +} + +// static +bool WifiDisplaySink::ParseURL( + const char *url, AString *host, int32_t *port, AString *path, + AString *user, AString *pass) { + host->clear(); + *port = 0; + path->clear(); + user->clear(); + pass->clear(); + + if (strncasecmp("rtsp://", url, 7)) { + return false; + } + + const char *slashPos = strchr(&url[7], '/'); + + if (slashPos == NULL) { + host->setTo(&url[7]); + path->setTo("/"); + } else { + host->setTo(&url[7], slashPos - &url[7]); + path->setTo(slashPos); + } + + ssize_t atPos = host->find("@"); + + if (atPos >= 0) { + // Split of user:pass@ from hostname. + + AString userPass(*host, 0, atPos); + host->erase(0, atPos + 1); + + ssize_t colonPos = userPass.find(":"); + + if (colonPos < 0) { + *user = userPass; + } else { + user->setTo(userPass, 0, colonPos); + pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1); + } + } + + const char *colonPos = strchr(host->c_str(), ':'); + + if (colonPos != NULL) { + char *end; + unsigned long x = strtoul(colonPos + 1, &end, 10); + + if (end == colonPos + 1 || *end != '\0' || x >= 65536) { + return false; + } + + *port = x; + + size_t colonOffset = colonPos - host->c_str(); + size_t trailing = host->size() - colonOffset; + host->erase(colonOffset, trailing); + } else { + *port = 554; + } + + return true; +} + +void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatStart: + { + sleep(2); // XXX + + int32_t sourcePort; + CHECK(msg->findString("sourceHost", &mRTSPHost)); + CHECK(msg->findInt32("sourcePort", &sourcePort)); + + sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id()); + + status_t err = mNetSession->createRTSPClient( + mRTSPHost.c_str(), sourcePort, notify, &mSessionID); + CHECK_EQ(err, (status_t)OK); + + mState = CONNECTING; + break; + } + + case kWhatRTSPNotify: + { + int32_t reason; + CHECK(msg->findInt32("reason", &reason)); + + switch (reason) { + case ANetworkSession::kWhatError: + { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + int32_t err; + CHECK(msg->findInt32("err", &err)); + + AString detail; + CHECK(msg->findString("detail", &detail)); + + ALOGE("An error occurred in session %d (%d, '%s/%s').", + sessionID, + err, + detail.c_str(), + strerror(-err)); + + if (sessionID == mSessionID) { + ALOGI("Lost control connection."); + + // The control connection is dead now. + mNetSession->destroySession(mSessionID); + mSessionID = 0; + + if (mNotify == NULL) { + looper()->stop(); + } else { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatDisconnected); + notify->post(); + } + } + break; + } + + case ANetworkSession::kWhatConnected: + { + ALOGI("We're now connected."); + mState = CONNECTED; + + if (mFlags & FLAG_SPECIAL_MODE) { + sp<AMessage> notify = new AMessage( + kWhatTimeSyncerNotify, id()); + + mTimeSyncer = new TimeSyncer(mNetSession, notify); + looper()->registerHandler(mTimeSyncer); + + mTimeSyncer->startClient(mRTSPHost.c_str(), 8123); + } + break; + } + + case ANetworkSession::kWhatData: + { + onReceiveClientData(msg); + break; + } + + default: + TRESPASS(); + } + break; + } + + case kWhatStop: + { + looper()->stop(); + break; + } + + case kWhatMediaReceiverNotify: + { + onMediaReceiverNotify(msg); + break; + } + + case kWhatTimeSyncerNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == TimeSyncer::kWhatTimeOffset) { + CHECK(msg->findInt64("offset", &mTimeOffsetUs)); + mTimeOffsetValid = true; + + if (mSetupDeferred) { + CHECK_EQ((status_t)OK, + sendSetup( + mSessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0")); + + mSetupDeferred = false; + } + } + break; + } + + case kWhatReportLateness: + { + if (mLatencyCount > 0) { + int64_t avgLatencyUs = mLatencySumUs / mLatencyCount; + + ALOGV("avg. latency = %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + mLatencyMaxUs / 1000ll); + + sp<AMessage> params = new AMessage; + params->setInt64("avgLatencyUs", avgLatencyUs); + params->setInt64("maxLatencyUs", mLatencyMaxUs); + mMediaReceiver->informSender(0 /* trackIndex */, params); + } + + mLatencyCount = 0; + mLatencySumUs = 0ll; + mLatencyMaxUs = 0ll; + + msg->post(kReportLatenessEveryUs); + break; + } + + default: + TRESPASS(); + } +} + +void WifiDisplaySink::dumpDelay(size_t trackIndex, int64_t timeUs) { + int64_t delayMs = (ALooper::GetNowUs() - timeUs) / 1000ll; + + if (delayMs > mMaxDelayMs) { + mMaxDelayMs = delayMs; + } + + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; + + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); + + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); + + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } + + ALOGI("[%lld]: (%4lld ms / %4lld ms) %s", + timeUs / 1000, + delayMs, + mMaxDelayMs, + kPattern + kPatternSize - n); +} + +void WifiDisplaySink::onMediaReceiverNotify(const sp<AMessage> &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case MediaReceiver::kWhatInitDone: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGI("MediaReceiver initialization completed w/ err %d", err); + break; + } + + case MediaReceiver::kWhatError: + { + status_t err; + CHECK(msg->findInt32("err", &err)); + + ALOGE("MediaReceiver signaled error %d", err); + break; + } + + case MediaReceiver::kWhatAccessUnit: + { + if (mRenderer == NULL) { + mRenderer = new DirectRenderer(mSurfaceTex); + looper()->registerHandler(mRenderer); + } + + sp<ABuffer> accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + if (!mTimeOffsetValid && !(mFlags & FLAG_SPECIAL_MODE)) { + mTimeOffsetUs = timeUs - ALooper::GetNowUs(); + mTimeOffsetValid = true; + } + + CHECK(mTimeOffsetValid); + + // We are the timesync _client_, + // client time = server time - time offset. + timeUs -= mTimeOffsetUs; + + size_t trackIndex; + CHECK(msg->findSize("trackIndex", &trackIndex)); + + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayUs = nowUs - timeUs; + + mLatencySumUs += delayUs; + if (mLatencyCount == 0 || delayUs > mLatencyMaxUs) { + mLatencyMaxUs = delayUs; + } + ++mLatencyCount; + + // dumpDelay(trackIndex, timeUs); + + timeUs += 220000ll; // Assume 220 ms of latency + accessUnit->meta()->setInt64("timeUs", timeUs); + + sp<AMessage> format; + if (msg->findMessage("format", &format)) { + mRenderer->setFormat(trackIndex, format); + } + + mRenderer->queueAccessUnit(trackIndex, accessUnit); + break; + } + + case MediaReceiver::kWhatPacketLost: + { +#if 0 + if (!mIDRFrameRequestPending) { + ALOGI("requesting IDR frame"); + + sendIDRFrameRequest(mSessionID); + } +#endif + break; + } + + default: + TRESPASS(); + } +} + +void WifiDisplaySink::registerResponseHandler( + int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) { + ResponseID id; + id.mSessionID = sessionID; + id.mCSeq = cseq; + mResponseHandlers.add(id, func); +} + +status_t WifiDisplaySink::sendM2(int32_t sessionID) { + 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()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response); + + ++mNextCSeq; + + return OK; +} + +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; +} + +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); + + if (err != OK) { + return err; + } + + mState = PAUSED; + + return sendPlay( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); +} + +status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg) { + if (mUsingTCPTransport && !(mFlags & FLAG_SPECIAL_MODE)) { + // In "special" mode we still use a UDP RTCP back-channel that + // needs connecting. + 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; + } + + AString serverPortStr; + if (!ParsedMessage::GetAttribute( + transport.c_str(), "server_port", &serverPortStr)) { + ALOGE("Missing 'server_port' in Transport field."); + return ERROR_MALFORMED; + } + + int rtpPort, rtcpPort; + if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2 + || 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."); + } + + return mMediaReceiver->connectTrack( + 0 /* trackIndex */, sourceHost.c_str(), rtpPort, rtcpPort); +} + +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; + + (new AMessage(kWhatReportLateness, id()))->post(kReportLatenessEveryUs); + + return OK; +} + +status_t WifiDisplaySink::onReceiveIDRFrameRequestResponse( + int32_t sessionID, const sp<ParsedMessage> &msg) { + CHECK(mIDRFrameRequestPending); + mIDRFrameRequestPending = false; + + return OK; +} + +void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) { + int32_t sessionID; + CHECK(msg->findInt32("sessionID", &sessionID)); + + sp<RefBase> obj; + CHECK(msg->findObject("data", &obj)); + + sp<ParsedMessage> data = + static_cast<ParsedMessage *>(obj.get()); + + 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)) { + sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); + return; + } + + if (method.startsWith("RTSP/")) { + // 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 { + AString version; + data->getRequestField(2, &version); + if (!(version == AString("RTSP/1.0"))) { + 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); + } + } +} + +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); +} + +void WifiDisplaySink::onGetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp<ParsedMessage> &data) { + AString body; + + if (mState == CONNECTED) { + mUsingTCPTransport = false; + mUsingTCPInterleaving = false; + + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd-sink.tcp-mode", val, NULL)) { + if (!strcasecmp("true", val) || !strcmp("1", val)) { + ALOGI("Using TCP unicast transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = false; + } else if (!strcasecmp("interleaved", val)) { + ALOGI("Using TCP interleaved transport."); + mUsingTCPTransport = true; + mUsingTCPInterleaving = true; + } + } else if (mFlags & FLAG_SPECIAL_MODE) { + mUsingTCPTransport = true; + } + + body = "wfd_video_formats: "; + body.append(mSinkSupportedVideoFormats.getFormatSpec()); + + body.append( + "\r\nwfd_audio_codecs: AAC 0000000F 00\r\n" + "wfd_client_rtp_ports: RTP/AVP/"); + + if (mUsingTCPTransport) { + body.append("TCP;"); + if (mUsingTCPInterleaving) { + body.append("interleaved"); + } else { + body.append("unicast 19000 0"); + } + } else { + body.append("UDP;unicast 19000 0"); + } + + body.append(" mode=play\r\n"); + } + + AString response = "RTSP/1.0 200 OK\r\n"; + AppendCommonResponse(&response, cseq); + response.append("Content-Type: text/parameters\r\n"); + response.append(StringPrintf("Content-Length: %d\r\n", body.size())); + response.append("\r\n"); + response.append(body); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); +} + +status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) { + sp<AMessage> notify = new AMessage(kWhatMediaReceiverNotify, id()); + + mMediaReceiverLooper = new ALooper; + mMediaReceiverLooper->setName("media_receiver"); + + mMediaReceiverLooper->start( + false /* runOnCallingThread */, + false /* canCallJava */, + PRIORITY_AUDIO); + + mMediaReceiver = new MediaReceiver(mNetSession, notify); + mMediaReceiverLooper->registerHandler(mMediaReceiver); + + RTPReceiver::TransportMode rtpMode = RTPReceiver::TRANSPORT_UDP; + if (mUsingTCPTransport) { + if (mUsingTCPInterleaving) { + rtpMode = RTPReceiver::TRANSPORT_TCP_INTERLEAVED; + } else { + rtpMode = RTPReceiver::TRANSPORT_TCP; + } + } + + int32_t localRTPPort; + status_t err = mMediaReceiver->addTrack( + rtpMode, RTPReceiver::TRANSPORT_UDP /* rtcpMode */, &localRTPPort); + + if (err == OK) { + err = mMediaReceiver->initAsync(MediaReceiver::MODE_TRANSPORT_STREAM); + } + + if (err != OK) { + mMediaReceiverLooper->unregisterHandler(mMediaReceiver->id()); + mMediaReceiver.clear(); + + mMediaReceiverLooper->stop(); + mMediaReceiverLooper.clear(); + + return err; + } + + AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri); + + AppendCommonResponse(&request, mNextCSeq); + + if (rtpMode == RTPReceiver::TRANSPORT_TCP_INTERLEAVED) { + request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n"); + } else if (rtpMode == RTPReceiver::TRANSPORT_TCP) { + if (mFlags & FLAG_SPECIAL_MODE) { + // This isn't quite true, since the RTP connection is through TCP + // and the RTCP connection through UDP... + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d-%d\r\n", + localRTPPort, localRTPPort + 1)); + } else { + request.append( + StringPrintf( + "Transport: RTP/AVP/TCP;unicast;client_port=%d\r\n", + localRTPPort)); + } + } else { + request.append( + StringPrintf( + "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n", + localRTPPort, + localRTPPort + 1)); + } + + request.append("\r\n"); + + ALOGV("request = '%s'", request.c_str()); + + err = mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse); + + ++mNextCSeq; + + return OK; +} + +status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) { + AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri); + + AppendCommonResponse(&request, mNextCSeq); + + request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); + request.append("\r\n"); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse); + + ++mNextCSeq; + + return OK; +} + +status_t WifiDisplaySink::sendIDRFrameRequest(int32_t sessionID) { + CHECK(!mIDRFrameRequestPending); + + AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; + + AppendCommonResponse(&request, mNextCSeq); + + AString content = "wfd_idr_request\r\n"; + + request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str())); + request.append(StringPrintf("Content-Length: %d\r\n", content.size())); + request.append("\r\n"); + request.append(content); + + status_t err = + mNetSession->sendRequest(sessionID, request.c_str(), request.size()); + + if (err != OK) { + return err; + } + + registerResponseHandler( + sessionID, + mNextCSeq, + &WifiDisplaySink::onReceiveIDRFrameRequestResponse); + + ++mNextCSeq; + + mIDRFrameRequestPending = true; + + return OK; +} + +void WifiDisplaySink::onSetParameterRequest( + int32_t sessionID, + int32_t cseq, + const sp<ParsedMessage> &data) { + const char *content = data->getContent(); + + if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) { + if ((mFlags & FLAG_SPECIAL_MODE) && !mTimeOffsetValid) { + mSetupDeferred = true; + } else { + status_t err = + sendSetup( + sessionID, + "rtsp://x.x.x.x:x/wfd1.0/streamid=0"); + + CHECK_EQ(err, (status_t)OK); + } + } + + 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()); + CHECK_EQ(err, (status_t)OK); +} + +void WifiDisplaySink::sendErrorResponse( + int32_t sessionID, + const char *errorDetail, + int32_t cseq) { + AString response; + response.append("RTSP/1.0 "); + response.append(errorDetail); + response.append("\r\n"); + + AppendCommonResponse(&response, cseq); + + response.append("\r\n"); + + status_t err = mNetSession->sendRequest(sessionID, response.c_str()); + CHECK_EQ(err, (status_t)OK); +} + +// static +void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) { + time_t now = time(NULL); + struct tm *now2 = gmtime(&now); + char buf[128]; + strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2); + + response->append("Date: "); + response->append(buf); + response->append("\r\n"); + + response->append(StringPrintf("User-Agent: %s\r\n", sUserAgent.c_str())); + + if (cseq >= 0) { + response->append(StringPrintf("CSeq: %d\r\n", cseq)); + } +} + +} // namespace android |