summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/wifi-display
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2012-09-18 14:47:48 -0700
committerAndreas Huber <andih@google.com>2012-09-19 15:12:23 -0700
commit75a8df99f695784cd458c72c8e1a99510e8761b2 (patch)
treeff4c1b90de0e6c7084f4a75a8e5a3bfea65dc44c /media/libstagefright/wifi-display
parent59451f8ced48874427ff1550391c4f3cd2ba2e35 (diff)
downloadframeworks_av-75a8df99f695784cd458c72c8e1a99510e8761b2.zip
frameworks_av-75a8df99f695784cd458c72c8e1a99510e8761b2.tar.gz
frameworks_av-75a8df99f695784cd458c72c8e1a99510e8761b2.tar.bz2
Instantiate HDCP module, add PES_private_data for encrypted streams.
Change-Id: I970f561a56a1d489a392c3480dec13f4ed7b43bd
Diffstat (limited to 'media/libstagefright/wifi-display')
-rw-r--r--media/libstagefright/wifi-display/Android.mk1
-rw-r--r--media/libstagefright/wifi-display/Parameters.cpp90
-rw-r--r--media/libstagefright/wifi-display/Parameters.h41
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp99
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.h5
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.cpp56
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.h4
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp375
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h41
9 files changed, 572 insertions, 140 deletions
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 <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+// static
+sp<Parameters> Parameters::Parse(const char *data, size_t size) {
+ sp<Parameters> 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 <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct Parameters : public RefBase {
+ static sp<Parameters> Parse(const char *data, size_t size);
+
+ bool findParameter(const char *name, AString *value) const;
+
+protected:
+ virtual ~Parameters();
+
+private:
+ KeyedVector<AString, AString> 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 <binder/IServiceManager.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
+#include <media/IHDCP.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -63,6 +64,7 @@ struct WifiDisplaySource::PlaybackSession::Track : public RefBase {
Track(const sp<AMessage> &format);
sp<AMessage> getFormat();
+ bool isAudio() const;
const sp<Converter> &converter() const;
ssize_t packetizerTrackIndex() const;
@@ -83,6 +85,9 @@ private:
sp<AMessage> mFormat;
bool mStarted;
ssize_t mPacketizerTrackIndex;
+ bool mIsAudio;
+
+ static bool IsAudioFormat(const sp<AMessage> &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<AMessage> &format)
: mFormat(format),
- mPacketizerTrackIndex(-1) {
+ mPacketizerTrackIndex(-1),
+ mIsAudio(IsAudioFormat(mFormat)) {
}
WifiDisplaySource::PlaybackSession::Track::~Track() {
stop();
}
+// static
+bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat(
+ const sp<AMessage> &format) {
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+
+ return !strncasecmp(mime.c_str(), "audio/", 6);
+}
+
sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
if (mFormat != NULL) {
return mFormat;
@@ -117,6 +133,10 @@ sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
return mConverter->getOutputFormat();
}
+bool WifiDisplaySource::PlaybackSession::Track::isAudio() const {
+ return mIsAudio;
+}
+
const sp<Converter> &WifiDisplaySource::PlaybackSession::Track::converter() const {
return mConverter;
}
@@ -172,11 +192,13 @@ WifiDisplaySource::PlaybackSession::PlaybackSession(
const sp<ANetworkSession> &netSession,
const sp<AMessage> &notify,
const in_addr &interfaceAddr,
- bool legacyMode)
+ bool legacyMode,
+ const sp<IHDCP> &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<ABuffer> 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<AMessage> 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<ABuffer> 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<ANetworkSession> &netSession,
const sp<AMessage> &notify,
const struct in_addr &interfaceAddr,
- bool legacyMode);
+ bool legacyMode,
+ const sp<IHDCP> &hdcp);
enum TransportMode {
TRANSPORT_UDP,
@@ -98,6 +100,7 @@ private:
sp<AMessage> mNotify;
in_addr mInterfaceAddr;
bool mLegacyMode;
+ sp<IHDCP> 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<sp<ABuffer> > 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<ABuffer> TSPacketizer::Track::prependCSD(
@@ -278,7 +279,8 @@ status_t TSPacketizer::packetize(
size_t trackIndex,
const sp<ABuffer> &_accessUnit,
sp<ABuffer> *packets,
- uint32_t flags) {
+ uint32_t flags,
+ const uint8_t *PES_private_data, size_t PES_private_data_len) {
sp<ABuffer> accessUnit = _accessUnit;
packets->clear();
@@ -292,12 +294,13 @@ status_t TSPacketizer::packetize(
const sp<Track> &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<ABuffer> &accessUnit,
sp<ABuffer> *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 <binder/IServiceManager.h>
#include <gui/ISurfaceTexture.h>
-
+#include <media/IHDCP.h>
+#include <media/IMediaPlayerService.h>
#include <media/IRemoteDisplayClient.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -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<AMessage> &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<AMessage> &msg) {
disconnectClient(OK);
+#if REQUIRE_HDCP
+ if (mHDCP != NULL) {
+ mHDCP->shutdownAsync();
+ mHDCP.clear();
+ }
+#endif
+
status_t err = OK;
sp<AMessage> response = new AMessage;
@@ -312,6 +333,40 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &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<Parameters> 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<AMessage> &msg) {
+status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
int32_t sessionID;
CHECK(msg->findInt32("sessionID", &sessionID));
@@ -597,7 +710,7 @@ void WifiDisplaySource::onReceiveClientData(const sp<AMessage> &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<AMessage> &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<AMessage> &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<ParsedMessage> &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<ParsedMessage> &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<ParsedMessage> &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> 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<ParsedMessage> &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<ParsedMessage> &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<ParsedMessage> &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<ParsedMessage> &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<ParsedMessage> &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<AMessage> &notify);
+
+ virtual void notify(
+ int msg, int ext1, int ext2, const Parcel *obj);
+
+private:
+ sp<AMessage> mNotify;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
+};
+
+WifiDisplaySource::HDCPObserver::HDCPObserver(
+ const sp<AMessage> &notify)
+ : mNotify(notify) {
+}
+
+void WifiDisplaySource::HDCPObserver::notify(
+ int msg, int ext1, int ext2, const Parcel *obj) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("msg", msg);
+ notify->setInt32("ext1", ext1);
+ notify->setInt32("ext2", ext2);
+ notify->post();
+}
+
+status_t WifiDisplaySource::makeHDCP() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ CHECK(service != NULL);
+
+ mHDCP = service->makeHDCP();
+
+ if (mHDCP == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<AMessage> 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<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
+#if REQUIRE_HDCP
+ bool mIsHDCP2_0;
+ int32_t mHDCPPort;
+ sp<IHDCP> mHDCP;
+ sp<HDCPObserver> 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<AMessage> &msg);
-
- void onDescribeRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
+ status_t onReceiveClientData(const sp<AMessage> &msg);
- void onOptionsRequest(
+ status_t onOptionsRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onSetupRequest(
+ status_t onSetupRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onPlayRequest(
+ status_t onPlayRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onPauseRequest(
+ status_t onPauseRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onTeardownRequest(
+ status_t onTeardownRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onGetParameterRequest(
+ status_t onGetParameterRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);
- void onSetParameterRequest(
+ status_t onSetParameterRequest(
int32_t sessionID,
int32_t cseq,
const sp<ParsedMessage> &data);