From b8c7bd418f0ee5b88923b0e0817e3a4acc53cf8d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 18 Sep 2012 14:47:48 -0700 Subject: Instantiate HDCP module, add PES_private_data for encrypted streams. Change-Id: I970f561a56a1d489a392c3480dec13f4ed7b43bd --- media/libstagefright/wifi-display/Android.mk | 1 + media/libstagefright/wifi-display/Parameters.cpp | 90 +++++ media/libstagefright/wifi-display/Parameters.h | 41 +++ .../wifi-display/source/PlaybackSession.cpp | 99 +++++- .../wifi-display/source/PlaybackSession.h | 5 +- .../wifi-display/source/TSPacketizer.cpp | 56 +-- .../wifi-display/source/TSPacketizer.h | 4 +- .../wifi-display/source/WifiDisplaySource.cpp | 375 +++++++++++++++------ .../wifi-display/source/WifiDisplaySource.h | 41 ++- 9 files changed, 572 insertions(+), 140 deletions(-) create mode 100644 media/libstagefright/wifi-display/Parameters.cpp create mode 100644 media/libstagefright/wifi-display/Parameters.h diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 681ba4f..a392532 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ANetworkSession.cpp \ + Parameters.cpp \ ParsedMessage.cpp \ sink/LinearRegression.cpp \ sink/RTPSink.cpp \ diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp new file mode 100644 index 0000000..f7118b3 --- /dev/null +++ b/media/libstagefright/wifi-display/Parameters.cpp @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#include "Parameters.h" + +#include + +namespace android { + +// static +sp Parameters::Parse(const char *data, size_t size) { + sp params = new Parameters; + status_t err = params->parse(data, size); + + if (err != OK) { + return NULL; + } + + return params; +} + +Parameters::Parameters() {} + +Parameters::~Parameters() {} + +status_t Parameters::parse(const char *data, size_t size) { + size_t i = 0; + while (i < size) { + size_t nameStart = i; + while (i < size && data[i] != ':') { + ++i; + } + + if (i == size || i == nameStart) { + return ERROR_MALFORMED; + } + + AString name(&data[nameStart], i - nameStart); + name.trim(); + name.tolower(); + + ++i; + + size_t valueStart = i; + + while (i + 1 < size && (data[i] != '\r' || data[i + 1] != '\n')) { + ++i; + } + + AString value(&data[valueStart], i - valueStart); + value.trim(); + + mDict.add(name, value); + + i += 2; + } + + return OK; +} + +bool Parameters::findParameter(const char *name, AString *value) const { + AString key = name; + key.tolower(); + + ssize_t index = mDict.indexOfKey(key); + + if (index < 0) { + value->clear(); + + return false; + } + + *value = mDict.valueAt(index); + return true; +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/Parameters.h b/media/libstagefright/wifi-display/Parameters.h new file mode 100644 index 0000000..a5e787e --- /dev/null +++ b/media/libstagefright/wifi-display/Parameters.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +namespace android { + +struct Parameters : public RefBase { + static sp Parse(const char *data, size_t size); + + bool findParameter(const char *name, AString *value) const; + +protected: + virtual ~Parameters(); + +private: + KeyedVector mDict; + + Parameters(); + status_t parse(const char *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(Parameters); +}; + +} // namespace android diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index e961518..ceea08f 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ struct WifiDisplaySource::PlaybackSession::Track : public RefBase { Track(const sp &format); sp getFormat(); + bool isAudio() const; const sp &converter() const; ssize_t packetizerTrackIndex() const; @@ -83,6 +85,9 @@ private: sp mFormat; bool mStarted; ssize_t mPacketizerTrackIndex; + bool mIsAudio; + + static bool IsAudioFormat(const sp &format); DISALLOW_EVIL_CONSTRUCTORS(Track); }; @@ -97,18 +102,29 @@ WifiDisplaySource::PlaybackSession::Track::Track( mMediaPuller(mediaPuller), mConverter(converter), mStarted(false), - mPacketizerTrackIndex(-1) { + mPacketizerTrackIndex(-1), + mIsAudio(IsAudioFormat(mConverter->getOutputFormat())) { } WifiDisplaySource::PlaybackSession::Track::Track(const sp &format) : mFormat(format), - mPacketizerTrackIndex(-1) { + mPacketizerTrackIndex(-1), + mIsAudio(IsAudioFormat(mFormat)) { } WifiDisplaySource::PlaybackSession::Track::~Track() { stop(); } +// static +bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat( + const sp &format) { + AString mime; + CHECK(format->findString("mime", &mime)); + + return !strncasecmp(mime.c_str(), "audio/", 6); +} + sp WifiDisplaySource::PlaybackSession::Track::getFormat() { if (mFormat != NULL) { return mFormat; @@ -117,6 +133,10 @@ sp WifiDisplaySource::PlaybackSession::Track::getFormat() { return mConverter->getOutputFormat(); } +bool WifiDisplaySource::PlaybackSession::Track::isAudio() const { + return mIsAudio; +} + const sp &WifiDisplaySource::PlaybackSession::Track::converter() const { return mConverter; } @@ -172,11 +192,13 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( const sp &netSession, const sp ¬ify, const in_addr &interfaceAddr, - bool legacyMode) + bool legacyMode, + const sp &hdcp) : mNetSession(netSession), mNotify(notify), mInterfaceAddr(interfaceAddr), mLegacyMode(legacyMode), + mHDCP(hdcp), mLastLifesignUs(), mVideoTrackIndex(-1), mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)), @@ -644,6 +666,73 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( sp accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); + bool isHDCPEncrypted = false; + uint64_t inputCTR; + uint8_t HDCP_private_data[16]; + if (mHDCP != NULL && !track->isAudio()) { + isHDCPEncrypted = true; + + status_t err = mHDCP->encrypt( + accessUnit->data(), accessUnit->size(), + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + + if (err != OK) { + ALOGI("Failed to HDCP-encrypt media data (err %d)", + err); + + // Inform WifiDisplaySource of our premature death + // (wish). + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatSessionDead); + notify->post(); + break; + } + + HDCP_private_data[0] = 0x00; + + HDCP_private_data[1] = + (((trackIndex >> 30) & 3) << 1) | 1; + + HDCP_private_data[2] = (trackIndex >> 22) & 0xff; + + HDCP_private_data[3] = + (((trackIndex >> 15) & 0x7f) << 1) | 1; + + HDCP_private_data[4] = (trackIndex >> 7) & 0xff; + + HDCP_private_data[5] = + ((trackIndex & 0x7f) << 1) | 1; + + HDCP_private_data[6] = 0x00; + + HDCP_private_data[7] = + (((inputCTR >> 60) & 0x0f) << 1) | 1; + + HDCP_private_data[8] = (inputCTR >> 52) & 0xff; + + HDCP_private_data[9] = + (((inputCTR >> 45) & 0x7f) << 1) | 1; + + HDCP_private_data[10] = (inputCTR >> 37) & 0xff; + + HDCP_private_data[11] = + (((inputCTR >> 30) & 0x7f) << 1) | 1; + + HDCP_private_data[12] = (inputCTR >> 22) & 0xff; + + HDCP_private_data[13] = + (((inputCTR >> 15) & 0x7f) << 1) | 1; + + HDCP_private_data[14] = (inputCTR >> 7) & 0xff; + + HDCP_private_data[15] = + ((inputCTR & 0x7f) << 1) | 1; + + flags |= TSPacketizer::IS_ENCRYPTED; + } + int64_t timeUs; CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); @@ -654,7 +743,9 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( sp packets; mPacketizer->packetize( - packetizerTrackIndex, accessUnit, &packets, flags); + packetizerTrackIndex, accessUnit, &packets, flags, + isHDCPEncrypted ? NULL : HDCP_private_data, + isHDCPEncrypted ? 0 : sizeof(HDCP_private_data)); for (size_t offset = 0; offset < packets->size(); offset += 188) { diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 0047842..0823ee6 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -24,6 +24,7 @@ namespace android { struct ABuffer; struct BufferQueue; +struct IHDCP; struct ISurfaceTexture; struct MediaPuller; struct MediaSource; @@ -39,7 +40,8 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { const sp &netSession, const sp ¬ify, const struct in_addr &interfaceAddr, - bool legacyMode); + bool legacyMode, + const sp &hdcp); enum TransportMode { TRANSPORT_UDP, @@ -98,6 +100,7 @@ private: sp mNotify; in_addr mInterfaceAddr; bool mLegacyMode; + sp mHDCP; int64_t mLastLifesignUs; diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index b9a3e9b..613cc28 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -66,6 +66,8 @@ private: AString mMIME; Vector > mCSD; + bool mAudioLacksATDSHeaders; + DISALLOW_EVIL_CONSTRUCTORS(Track); }; @@ -76,7 +78,8 @@ TSPacketizer::Track::Track( mPID(PID), mStreamType(streamType), mStreamID(streamID), - mContinuityCounter(0) { + mContinuityCounter(0), + mAudioLacksATDSHeaders(false) { CHECK(format->findString("mime", &mMIME)); if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC) @@ -89,6 +92,13 @@ TSPacketizer::Track::Track( mCSD.push(csd); } + + if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { + int32_t isADTS; + if (!mFormat->findInt32("is-adts", &isADTS) || isADTS == 0) { + mAudioLacksATDSHeaders = true; + } + } } } @@ -130,16 +140,7 @@ bool TSPacketizer::Track::isH264() const { } bool TSPacketizer::Track::lacksADTSHeader() const { - if (strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) { - return false; - } - - int32_t isADTS; - if (mFormat->findInt32("is-adts", &isADTS) && isADTS != 0) { - return false; - } - - return true; + return mAudioLacksATDSHeaders; } sp TSPacketizer::Track::prependCSD( @@ -278,7 +279,8 @@ status_t TSPacketizer::packetize( size_t trackIndex, const sp &_accessUnit, sp *packets, - uint32_t flags) { + uint32_t flags, + const uint8_t *PES_private_data, size_t PES_private_data_len) { sp accessUnit = _accessUnit; packets->clear(); @@ -292,12 +294,13 @@ status_t TSPacketizer::packetize( const sp &track = mTracks.itemAt(trackIndex); - if (track->isH264()) { + if (track->isH264() && !(flags & IS_ENCRYPTED)) { if (IsIDR(accessUnit)) { // prepend codec specific data, i.e. SPS and PPS. accessUnit = track->prependCSD(accessUnit); } } else if (track->lacksADTSHeader()) { + CHECK(!(flags & IS_ENCRYPTED)); accessUnit = track->prependADTSHeader(accessUnit); } @@ -336,11 +339,16 @@ status_t TSPacketizer::packetize( // reserved = b1 // the first fragment of "buffer" follows + size_t PES_packet_length = accessUnit->size() + 8; + if (PES_private_data_len > 0) { + PES_packet_length += PES_private_data_len + 1; + } + size_t numTSPackets; - if (accessUnit->size() <= 170) { + if (PES_packet_length <= 178) { numTSPackets = 1; } else { - numTSPackets = 1 + ((accessUnit->size() - 170) + 183) / 184; + numTSPackets = 1 + ((PES_packet_length - 178) + 183) / 184; } if (flags & EMIT_PAT_AND_PMT) { @@ -554,8 +562,7 @@ status_t TSPacketizer::packetize( uint32_t PTS = (timeUs * 9ll) / 100ll; - size_t PES_packet_length = accessUnit->size() + 8; - bool padding = (accessUnit->size() < (188 - 18)); + bool padding = (PES_packet_length < (188 - 10)); if (PES_packet_length >= 65536) { // This really should only happen for video. @@ -572,7 +579,7 @@ status_t TSPacketizer::packetize( *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter(); if (padding) { - size_t paddingSize = 188 - 18 - accessUnit->size(); + size_t paddingSize = 188 - 10 - PES_packet_length; *ptr++ = paddingSize - 1; if (paddingSize >= 2) { *ptr++ = 0x00; @@ -588,14 +595,23 @@ status_t TSPacketizer::packetize( *ptr++ = PES_packet_length >> 8; *ptr++ = PES_packet_length & 0xff; *ptr++ = 0x84; - *ptr++ = 0x80; - *ptr++ = 0x05; + *ptr++ = (PES_private_data_len > 0) ? 0x81 : 0x80; + + *ptr++ = (PES_private_data_len > 0) + ? (1 + PES_private_data_len + 0x05) : 0x05; + *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1; *ptr++ = (PTS >> 22) & 0xff; *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1; *ptr++ = (PTS >> 7) & 0xff; *ptr++ = ((PTS & 0x7f) << 1) | 1; + if (PES_private_data_len > 0) { + *ptr++ = 0x8e; // PES_private_data_flag, reserved. + memcpy(ptr, PES_private_data, PES_private_data_len); + ptr += PES_private_data_len; + } + // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload size_t sizeLeft = packetDataStart + 188 - ptr; diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h index 9dbeb27..7020fff 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.h +++ b/media/libstagefright/wifi-display/source/TSPacketizer.h @@ -40,11 +40,13 @@ struct TSPacketizer : public RefBase { enum { EMIT_PAT_AND_PMT = 1, EMIT_PCR = 2, + IS_ENCRYPTED = 4, }; status_t packetize( size_t trackIndex, const sp &accessUnit, sp *packets, - uint32_t flags); + uint32_t flags, + const uint8_t *PES_private_data, size_t PES_private_data_len); protected: virtual ~TSPacketizer(); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 8fead96..133b094 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -20,10 +20,13 @@ #include "WifiDisplaySource.h" #include "PlaybackSession.h" +#include "Parameters.h" #include "ParsedMessage.h" +#include #include - +#include +#include #include #include #include @@ -43,7 +46,14 @@ WifiDisplaySource::WifiDisplaySource( mSessionID(0), mClientSessionID(0), mReaperPending(false), - mNextCSeq(1) { + mNextCSeq(1) +#if REQUIRE_HDCP + ,mIsHDCP2_0(false) + ,mHDCPPort(0) + ,mHDCPInitializationComplete(false) + ,mSetupTriggerDeferred(false) +#endif +{ } WifiDisplaySource::~WifiDisplaySource() { @@ -203,7 +213,11 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { case ANetworkSession::kWhatData: { - onReceiveClientData(msg); + status_t err = onReceiveClientData(msg); + + if (err != OK) { + disconnectClient(err); + } break; } @@ -220,6 +234,13 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { disconnectClient(OK); +#if REQUIRE_HDCP + if (mHDCP != NULL) { + mHDCP->shutdownAsync(); + mHDCP.clear(); + } +#endif + status_t err = OK; sp response = new AMessage; @@ -312,6 +333,40 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } +#if REQUIRE_HDCP + case kWhatHDCPNotify: + { + int32_t msgCode, ext1, ext2; + CHECK(msg->findInt32("msg", &msgCode)); + CHECK(msg->findInt32("ext1", &ext1)); + CHECK(msg->findInt32("ext2", &ext2)); + + ALOGV("Saw HDCP notification code %d, ext1 %d, ext2 %d", + msgCode, ext1, ext2); + + switch (msgCode) { + case HDCPModule::HDCP_INITIALIZATION_COMPLETE: + { + mHDCPInitializationComplete = true; + + if (mSetupTriggerDeferred) { + mSetupTriggerDeferred = false; + + sendM5(mClientSessionID); + } + break; + } + + default: + { + disconnectClient(-EACCES); + break; + } + } + break; + } +#endif + default: TRESPASS(); } @@ -350,6 +405,9 @@ status_t WifiDisplaySource::sendM1(int32_t sessionID) { status_t WifiDisplaySource::sendM3(int32_t sessionID) { AString body = +#if REQUIRE_HDCP + "wfd_content_protection\r\n" +#endif "wfd_video_formats\r\n" "wfd_audio_codecs\r\n" "wfd_client_rtp_ports\r\n"; @@ -405,9 +463,13 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { transportString = "TCP"; } + // For 720p60: + // use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n" + // For 720p30: + // use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" AString body = StringPrintf( "wfd_video_formats: " - "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n" + "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" "wfd_audio_codecs: AAC 00000001 00\r\n" // 2 ch AAC 48kHz "wfd_presentation_URL: rtsp://%s:%d/wfd1.0/streamid=0 none\r\n" "wfd_client_rtp_ports: RTP/AVP/%s;unicast 19000 0 mode=play\r\n", @@ -512,6 +574,48 @@ status_t WifiDisplaySource::onReceiveM3Response( return ERROR_UNSUPPORTED; } + sp params = + Parameters::Parse(msg->getContent(), strlen(msg->getContent())); + + if (params == NULL) { + return ERROR_MALFORMED; + } + +#if REQUIRE_HDCP + AString value; + if (!params->findParameter("wfd_content_protection", &value)) { + ALOGE("Sink doesn't appear to support content protection."); + return -EACCES; + } + + if (value == "none") { + ALOGE("Sink does not support content protection."); + return -EACCES; + } + + bool isHDCP2_0 = false; + if (value.startsWith("HDCP2.0 ")) { + isHDCP2_0 = true; + } else if (!value.startsWith("HDCP2.1 ")) { + return ERROR_MALFORMED; + } + + int32_t hdcpPort; + if (!ParsedMessage::GetInt32Attribute(value.c_str() + 8, "port", &hdcpPort) + || hdcpPort < 1 || hdcpPort > 65535) { + return ERROR_MALFORMED; + } + + mIsHDCP2_0 = isHDCP2_0; + mHDCPPort = hdcpPort; + + status_t err = makeHDCP(); + if (err != OK) { + ALOGE("Unable to instantiate HDCP component."); + return err; + } +#endif + return sendM4(sessionID); } @@ -526,6 +630,15 @@ status_t WifiDisplaySource::onReceiveM4Response( return ERROR_UNSUPPORTED; } +#if REQUIRE_HDCP + if (!mHDCPInitializationComplete) { + ALOGI("Deferring SETUP trigger until HDCP initialization completes."); + + mSetupTriggerDeferred = true; + return OK; + } +#endif + return sendM5(sessionID); } @@ -577,7 +690,7 @@ void WifiDisplaySource::scheduleKeepAlive(int32_t sessionID) { msg->post(kPlaybackSessionTimeoutUs - 5000000ll); } -void WifiDisplaySource::onReceiveClientData(const sp &msg) { +status_t WifiDisplaySource::onReceiveClientData(const sp &msg) { int32_t sessionID; CHECK(msg->findInt32("sessionID", &sessionID)); @@ -597,7 +710,7 @@ void WifiDisplaySource::onReceiveClientData(const sp &msg) { int32_t cseq; if (!data->findInt32("cseq", &cseq)) { sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */); - return; + return ERROR_MALFORMED; } if (method.startsWith("RTSP/")) { @@ -611,7 +724,7 @@ void WifiDisplaySource::onReceiveClientData(const sp &msg) { if (index < 0) { ALOGW("Received unsolicited server response, cseq %d", cseq); - return; + return ERROR_MALFORMED; } HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index); @@ -623,76 +736,45 @@ void WifiDisplaySource::onReceiveClientData(const sp &msg) { ALOGW("Response handler for session %d, cseq %d returned " "err %d (%s)", sessionID, cseq, err, strerror(-err)); - } - } else { - AString version; - data->getRequestField(2, &version); - if (!(version == AString("RTSP/1.0"))) { - sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); - return; - } - if (method == "DESCRIBE") { - onDescribeRequest(sessionID, cseq, data); - } else if (method == "OPTIONS") { - onOptionsRequest(sessionID, cseq, data); - } else if (method == "SETUP") { - onSetupRequest(sessionID, cseq, data); - } else if (method == "PLAY") { - onPlayRequest(sessionID, cseq, data); - } else if (method == "PAUSE") { - onPauseRequest(sessionID, cseq, data); - } else if (method == "TEARDOWN") { - onTeardownRequest(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); + return err; } - } -} -void WifiDisplaySource::onDescribeRequest( - int32_t sessionID, - int32_t cseq, - const sp &data) { - int64_t nowUs = ALooper::GetNowUs(); - - AString sdp; - sdp.append("v=0\r\n"); - - sdp.append(StringPrintf( - "o=- %lld %lld IN IP4 0.0.0.0\r\n", nowUs, nowUs)); - - sdp.append( - "o=- 0 0 IN IP4 127.0.0.0\r\n" - "s=Sample\r\n" - "c=IN IP4 0.0.0.0\r\n" - "b=AS:502\r\n" - "t=0 0\r\n" - "a=control:*\r\n" - "a=range:npt=now-\r\n" - "m=video 0 RTP/AVP 33\r\n" - "a=rtpmap:33 MP2T/90000\r\n" - "a=control:\r\n"); + return OK; + } - AString response = "RTSP/1.0 200 OK\r\n"; - AppendCommonResponse(&response, cseq); + AString version; + data->getRequestField(2, &version); + if (!(version == AString("RTSP/1.0"))) { + sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq); + return ERROR_UNSUPPORTED; + } - response.append("Content-Type: application/sdp\r\n"); + 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); - // response.append("Content-Base: rtsp://0.0.0.0:7236\r\n"); - response.append(StringPrintf("Content-Length: %d\r\n", sdp.size())); - response.append("\r\n"); - response.append(sdp); + err = ERROR_UNSUPPORTED; + } - status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + return err; } -void WifiDisplaySource::onOptionsRequest( +status_t WifiDisplaySource::onOptionsRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -708,19 +790,21 @@ void WifiDisplaySource::onOptionsRequest( AppendCommonResponse(&response, cseq); response.append( - "Public: org.wfa.wfd1.0, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, " + "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()); - CHECK_EQ(err, (status_t)OK); - err = sendM3(sessionID); - CHECK_EQ(err, (status_t)OK); + if (err == OK) { + err = sendM3(sessionID); + } + + return err; } -void WifiDisplaySource::onSetupRequest( +status_t WifiDisplaySource::onSetupRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -729,13 +813,13 @@ void WifiDisplaySource::onSetupRequest( // We only support a single playback session per client. // This is due to the reversed keep-alive design in the wfd specs... sendErrorResponse(sessionID, "400 Bad Request", cseq); - return; + return ERROR_MALFORMED; } AString transport; if (!data->findString("transport", &transport)) { sendErrorResponse(sessionID, "400 Bad Request", cseq); - return; + return ERROR_MALFORMED; } PlaybackSession::TransportMode transportMode = @@ -767,7 +851,7 @@ void WifiDisplaySource::onSetupRequest( if (badRequest) { sendErrorResponse(sessionID, "400 Bad Request", cseq); - return; + return ERROR_MALFORMED; } transportMode = PlaybackSession::TRANSPORT_TCP; @@ -791,7 +875,7 @@ void WifiDisplaySource::onSetupRequest( if (badRequest) { sendErrorResponse(sessionID, "400 Bad Request", cseq); - return; + return ERROR_MALFORMED; } #if 1 // The older LG dongles doesn't specify client_port=xxx apparently. @@ -801,7 +885,7 @@ void WifiDisplaySource::onSetupRequest( #endif } else { sendErrorResponse(sessionID, "461 Unsupported Transport", cseq); - return; + return ERROR_UNSUPPORTED; } int32_t playbackSessionID = makeUniquePlaybackSessionID(); @@ -813,7 +897,13 @@ void WifiDisplaySource::onSetupRequest( sp playbackSession = new PlaybackSession( mNetSession, notify, mInterfaceAddr, - mClient == NULL /* legacyMode */); + mClient == NULL, /* legacyMode */ +#if REQUIRE_HDCP + mHDCP +#else + NULL +#endif + ); looper()->registerHandler(playbackSession); @@ -822,12 +912,12 @@ void WifiDisplaySource::onSetupRequest( if (strncasecmp("rtsp://", uri.c_str(), 7)) { sendErrorResponse(sessionID, "400 Bad Request", cseq); - return; + return ERROR_MALFORMED; } if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) { sendErrorResponse(sessionID, "404 Not found", cseq); - return; + return ERROR_MALFORMED; } status_t err = playbackSession->init( @@ -846,10 +936,10 @@ void WifiDisplaySource::onSetupRequest( break; case -ENOENT: sendErrorResponse(sessionID, "404 Not Found", cseq); - return; + return err; default: sendErrorResponse(sessionID, "403 Forbidden", cseq); - return; + return err; } mClientInfo.mPlaybackSessionID = playbackSessionID; @@ -891,13 +981,18 @@ void WifiDisplaySource::onSetupRequest( response.append("\r\n"); err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + + if (err != OK) { + return err; + } scheduleReaper(); scheduleKeepAlive(sessionID); + + return OK; } -void WifiDisplaySource::onPlayRequest( +status_t WifiDisplaySource::onPlayRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -907,7 +1002,7 @@ void WifiDisplaySource::onPlayRequest( if (playbackSession == NULL) { sendErrorResponse(sessionID, "454 Session Not Found", cseq); - return; + return ERROR_MALFORMED; } status_t err = playbackSession->play(); @@ -919,12 +1014,17 @@ void WifiDisplaySource::onPlayRequest( response.append("\r\n"); err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + + if (err != OK) { + return err; + } playbackSession->finishPlay(); + + return OK; } -void WifiDisplaySource::onPauseRequest( +status_t WifiDisplaySource::onPauseRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -934,7 +1034,7 @@ void WifiDisplaySource::onPauseRequest( if (playbackSession == NULL) { sendErrorResponse(sessionID, "454 Session Not Found", cseq); - return; + return ERROR_MALFORMED; } status_t err = playbackSession->pause(); @@ -945,10 +1045,11 @@ void WifiDisplaySource::onPauseRequest( response.append("\r\n"); err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + + return err; } -void WifiDisplaySource::onTeardownRequest( +status_t WifiDisplaySource::onTeardownRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -958,7 +1059,7 @@ void WifiDisplaySource::onTeardownRequest( if (playbackSession == NULL) { sendErrorResponse(sessionID, "454 Session Not Found", cseq); - return; + return ERROR_MALFORMED; } AString response = "RTSP/1.0 200 OK\r\n"; @@ -967,12 +1068,17 @@ void WifiDisplaySource::onTeardownRequest( response.append("\r\n"); status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + + if (err != OK) { + return err; + } disconnectClient(UNKNOWN_ERROR); + + return OK; } -void WifiDisplaySource::onGetParameterRequest( +status_t WifiDisplaySource::onGetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -982,7 +1088,7 @@ void WifiDisplaySource::onGetParameterRequest( if (playbackSession == NULL) { sendErrorResponse(sessionID, "454 Session Not Found", cseq); - return; + return ERROR_MALFORMED; } playbackSession->updateLiveness(); @@ -992,10 +1098,10 @@ void WifiDisplaySource::onGetParameterRequest( response.append("\r\n"); status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + return err; } -void WifiDisplaySource::onSetParameterRequest( +status_t WifiDisplaySource::onSetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data) { @@ -1005,11 +1111,12 @@ void WifiDisplaySource::onSetParameterRequest( if (playbackSession == NULL) { sendErrorResponse(sessionID, "454 Session Not Found", cseq); - return; + return ERROR_MALFORMED; } - // XXX check that the parameter is about that. - playbackSession->requestIDRFrame(); + if (strstr(data->getContent(), "wfd_idr_request\r\n")) { + playbackSession->requestIDRFrame(); + } playbackSession->updateLiveness(); @@ -1018,7 +1125,7 @@ void WifiDisplaySource::onSetParameterRequest( response.append("\r\n"); status_t err = mNetSession->sendRequest(sessionID, response.c_str()); - CHECK_EQ(err, (status_t)OK); + return err; } // static @@ -1103,5 +1210,71 @@ void WifiDisplaySource::disconnectClient(status_t err) { } } +#if REQUIRE_HDCP +struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver { + HDCPObserver(const sp ¬ify); + + virtual void notify( + int msg, int ext1, int ext2, const Parcel *obj); + +private: + sp mNotify; + + DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver); +}; + +WifiDisplaySource::HDCPObserver::HDCPObserver( + const sp ¬ify) + : mNotify(notify) { +} + +void WifiDisplaySource::HDCPObserver::notify( + int msg, int ext1, int ext2, const Parcel *obj) { + sp notify = mNotify->dup(); + notify->setInt32("msg", msg); + notify->setInt32("ext1", ext1); + notify->setInt32("ext2", ext2); + notify->post(); +} + +status_t WifiDisplaySource::makeHDCP() { + sp sm = defaultServiceManager(); + sp binder = sm->getService(String16("media.player")); + sp service = interface_cast(binder); + CHECK(service != NULL); + + mHDCP = service->makeHDCP(); + + if (mHDCP == NULL) { + return ERROR_UNSUPPORTED; + } + + sp notify = new AMessage(kWhatHDCPNotify, id()); + mHDCPObserver = new HDCPObserver(notify); + + status_t err = mHDCP->setObserver(mHDCPObserver); + + if (err != OK) { + ALOGE("Failed to set HDCP observer."); + + mHDCPObserver.clear(); + mHDCP.clear(); + + return err; + } + + ALOGI("initiating HDCP negotiation w/ host %s:%d", + mClientInfo.mRemoteIP.c_str(), mHDCPPort); + + err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort); + + if (err != OK) { + return err; + } + + return OK; +} +#endif + } // namespace android diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 3c8d50f..298cb9b 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -26,6 +26,9 @@ namespace android { +#define REQUIRE_HDCP 0 + +struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; @@ -48,6 +51,10 @@ protected: private: struct PlaybackSession; +#if REQUIRE_HDCP + struct HDCPObserver; +#endif + enum { kWhatStart, kWhatRTSPNotify, @@ -55,6 +62,7 @@ private: kWhatReapDeadClients, kWhatPlaybackSessionNotify, kWhatKeepAlive, + kWhatHDCPNotify, }; struct ResponseID { @@ -100,6 +108,18 @@ private: KeyedVector mResponseHandlers; +#if REQUIRE_HDCP + bool mIsHDCP2_0; + int32_t mHDCPPort; + sp mHDCP; + sp mHDCPObserver; + + bool mHDCPInitializationComplete; + bool mSetupTriggerDeferred; + + status_t makeHDCP(); +#endif + status_t sendM1(int32_t sessionID); status_t sendM3(int32_t sessionID); status_t sendM4(int32_t sessionID); @@ -124,44 +144,39 @@ private: void registerResponseHandler( int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func); - void onReceiveClientData(const sp &msg); - - void onDescribeRequest( - int32_t sessionID, - int32_t cseq, - const sp &data); + status_t onReceiveClientData(const sp &msg); - void onOptionsRequest( + status_t onOptionsRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onSetupRequest( + status_t onSetupRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onPlayRequest( + status_t onPlayRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onPauseRequest( + status_t onPauseRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onTeardownRequest( + status_t onTeardownRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onGetParameterRequest( + status_t onGetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data); - void onSetParameterRequest( + status_t onSetParameterRequest( int32_t sessionID, int32_t cseq, const sp &data); -- cgit v1.1