summaryrefslogtreecommitdiff
authorshipeng.sun <shipeng.sun@amlogic.com>2019-01-15 14:43:37 (GMT)
committer shipeng.sun <shipeng.sun@amlogic.com>2019-03-14 11:54:14 (GMT)
commit219a12a98ff06b177a9532e4e906604b3861fcc0 (patch)
tree9b8ca07c0227444e2e034460e1319ff98ae9d400
parent625b4c85596d21bcb0f6625467dba2826ad06007 (diff)
downloadav-219a12a98ff06b177a9532e4e906604b3861fcc0.zip
av-219a12a98ff06b177a9532e4e906604b3861fcc0.tar.gz
av-219a12a98ff06b177a9532e4e906604b3861fcc0.tar.bz2
Miracast: Add Miracast native funciton [1/9]
PD#SWPL-2139 Problem: Add Miracast native funciton Solution: Add Miracast native funciton Verify: Verify on darwin Change-Id: I58dc988fa6b2abb6ac571cbb5e680807f1c0c76f Signed-off-by: shipeng.sun <shipeng.sun@amlogic.com>
Diffstat
-rw-r--r--libstagefright/Android.mk1
-rw-r--r--libstagefright/wifi-display/Android.mk53
-rw-r--r--libstagefright/wifi-display/sink/AmANetworkSession.cpp1435
-rw-r--r--libstagefright/wifi-display/sink/AmANetworkSession.h140
-rw-r--r--libstagefright/wifi-display/sink/LinearRegression.cpp121
-rw-r--r--libstagefright/wifi-display/sink/LinearRegression.h55
-rw-r--r--libstagefright/wifi-display/sink/RTPSink.cpp999
-rw-r--r--libstagefright/wifi-display/sink/RTPSink.h109
-rw-r--r--libstagefright/wifi-display/sink/TunnelRenderer.cpp618
-rw-r--r--libstagefright/wifi-display/sink/TunnelRenderer.h111
-rw-r--r--libstagefright/wifi-display/sink/Utils.cpp34
-rw-r--r--libstagefright/wifi-display/sink/Utils.h17
-rw-r--r--libstagefright/wifi-display/sink/WifiDisplaySink.cpp1435
-rw-r--r--libstagefright/wifi-display/sink/WifiDisplaySink.h270
-rw-r--r--mediaextconfig/include/media/ammediaplayerext.h1
15 files changed, 5399 insertions, 0 deletions
diff --git a/libstagefright/Android.mk b/libstagefright/Android.mk
new file mode 100644
index 0000000..5053e7d
--- a/dev/null
+++ b/libstagefright/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libstagefright/wifi-display/Android.mk b/libstagefright/wifi-display/Android.mk
new file mode 100644
index 0000000..037d9bf
--- a/dev/null
+++ b/libstagefright/wifi-display/Android.mk
@@ -0,0 +1,53 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+include $(BOARD_AML_MEDIA_HAL_CONFIG)
+LOCAL_SRC_FILES:= \
+ sink/LinearRegression.cpp \
+ sink/RTPSink.cpp \
+ sink/TunnelRenderer.cpp \
+ sink/WifiDisplaySink.cpp \
+ sink/Utils.cpp \
+ sink/AmANetworkSession.cpp
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/frameworks/av/media/libstagefright \
+ $(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/av/media/libstagefright/mpeg2ts \
+ $(TOP)/frameworks/native/include/media/hardware \
+ $(TOP)/frameworks/native/headers/media_plugin
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28 && echo OK),OK)
+LOCAL_C_INCLUDES += \
+ $(TOP)/vendor/amlogic/common/frameworks/av/mediaextconfig/include
+else
+LOCAL_C_INCLUDES += \
+ $(TOP)/vendor/amlogic/frameworks/av/mediaextconfig/include
+endif
+
+LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+
+LOCAL_SHARED_LIBRARIES:= \
+ libbinder \
+ libcutils \
+ liblog \
+ libgui \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libui \
+ libutils
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28 && echo OK),OK)
+LOCAL_SHARED_LIBRARIES += \
+ vendor.amlogic.hardware.miracast_hdcp2@1.0 \
+ libhardware \
+ libhidlbase \
+ libhidltransport
+endif
+
+LOCAL_MODULE:= libstagefright_wfd_sink
+
+LOCAL_MODULE_TAGS:= optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libstagefright/wifi-display/sink/AmANetworkSession.cpp b/libstagefright/wifi-display/sink/AmANetworkSession.cpp
new file mode 100644
index 0000000..f8aae89
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/AmANetworkSession.cpp
@@ -0,0 +1,1435 @@
+/*
+ * 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 "Nu-NetworkSession"
+#include <utils/Log.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <linux/tcp.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ParsedMessage.h>
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+#include <media/stagefright/Utils.h>
+#else
+#include <media/stagefright/foundation/ByteUtils.h>
+#endif
+#include "AmANetworkSession.h"
+
+namespace android {
+
+static const size_t kMaxUDPSize = 1500;
+static const int32_t kMaxUDPRetries = 200;
+
+struct AmANetworkSession::NetworkThread : public Thread {
+ explicit NetworkThread(AmANetworkSession *session);
+
+protected:
+ virtual ~NetworkThread();
+
+private:
+ AmANetworkSession *mSession;
+
+ virtual bool threadLoop();
+
+ DISALLOW_EVIL_CONSTRUCTORS(NetworkThread);
+};
+
+struct AmANetworkSession::Session : public RefBase {
+ enum Mode {
+ MODE_RTSP,
+ MODE_DATAGRAM,
+ MODE_WEBSOCKET,
+ };
+
+ enum State {
+ CONNECTING,
+ CONNECTED,
+ LISTENING_RTSP,
+ LISTENING_TCP_DGRAMS,
+ DATAGRAM,
+ };
+
+ Session(int32_t sessionID,
+ State state,
+ int s,
+ bool isRTPSession,
+ const sp<AMessage> &notify);
+
+ int32_t sessionID() const;
+ int socket() const;
+ sp<AMessage> getNotificationMessage() const;
+
+ bool isRTSPServer() const;
+ bool isTCPDatagramServer() const;
+
+ bool wantsToRead();
+ bool wantsToWrite();
+
+ status_t readMore();
+ status_t writeMore();
+
+ status_t sendRequest(
+ const void *data, ssize_t size, bool timeValid, int64_t timeUs);
+
+ void setMode(Mode mode);
+
+ status_t switchToWebSocketMode();
+
+protected:
+ virtual ~Session();
+
+private:
+ enum {
+ FRAGMENT_FLAG_TIME_VALID = 1,
+ };
+ struct Fragment {
+ uint32_t mFlags;
+ int64_t mTimeUs;
+ sp<ABuffer> mBuffer;
+ };
+
+ int32_t mSessionID;
+ State mState;
+ bool mIsNeedRTPConnect;
+ Mode mMode;
+ int mSocket;
+ sp<AMessage> mNotify;
+ bool mSawReceiveFailure, mSawSendFailure;
+ int32_t mUDPRetries;
+
+ List<Fragment> mOutFragments;
+
+ AString mInBuffer;
+
+ int64_t mLastStallReportUs;
+
+ void notifyError(bool send, status_t err, const char *detail);
+ void notify(NotificationReason reason);
+
+ void dumpFragmentStats(const Fragment &frag);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Session);
+};
+////////////////////////////////////////////////////////////////////////////////
+
+AmANetworkSession::NetworkThread::NetworkThread(AmANetworkSession *session)
+ : mSession(session) {
+}
+
+AmANetworkSession::NetworkThread::~NetworkThread() {
+}
+
+bool AmANetworkSession::NetworkThread::threadLoop() {
+ mSession->threadLoop();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AmANetworkSession::Session::Session(
+ int32_t sessionID,
+ State state,
+ int s,
+ bool isRTPSession,
+ const sp<AMessage> &notify)
+ : mSessionID(sessionID),
+ mState(state),
+ mIsNeedRTPConnect(isRTPSession),
+ mMode(MODE_DATAGRAM),
+ mSocket(s),
+ mNotify(notify),
+ mSawReceiveFailure(false),
+ mSawSendFailure(false),
+ mUDPRetries(kMaxUDPRetries),
+ mLastStallReportUs(-1ll) {
+ if (mState == CONNECTED) {
+ struct sockaddr_in localAddr;
+ socklen_t localAddrLen = sizeof(localAddr);
+
+ int res = getsockname(
+ mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
+ CHECK_GE(res, 0);
+
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ res = getpeername(
+ mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+ CHECK_GE(res, 0);
+
+ in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);
+ AString localAddrString = AStringPrintf(
+ "%d.%d.%d.%d",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff);
+
+ addr = ntohl(remoteAddr.sin_addr.s_addr);
+ AString remoteAddrString = AStringPrintf(
+ "%d.%d.%d.%d",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff);
+
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatClientConnected);
+ msg->setString("server-ip", localAddrString.c_str());
+ msg->setInt32("server-port", ntohs(localAddr.sin_port));
+ msg->setString("client-ip", remoteAddrString.c_str());
+ msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
+ msg->post();
+ }
+}
+
+AmANetworkSession::Session::~Session() {
+ ALOGV("Session %d gone", mSessionID);
+
+ close(mSocket);
+ mSocket = -1;
+}
+
+int32_t AmANetworkSession::Session::sessionID() const {
+ return mSessionID;
+}
+
+int AmANetworkSession::Session::socket() const {
+ return mSocket;
+}
+
+void AmANetworkSession::Session::setMode(Mode mode) {
+ mMode = mode;
+}
+
+status_t AmANetworkSession::Session::switchToWebSocketMode() {
+ if (mState != CONNECTED || mMode != MODE_RTSP) {
+ return INVALID_OPERATION;
+ }
+
+ mMode = MODE_WEBSOCKET;
+
+ return OK;
+}
+
+sp<AMessage> AmANetworkSession::Session::getNotificationMessage() const {
+ return mNotify;
+}
+
+bool AmANetworkSession::Session::isRTSPServer() const {
+ return mState == LISTENING_RTSP;
+}
+
+bool AmANetworkSession::Session::isTCPDatagramServer() const {
+ return mState == LISTENING_TCP_DGRAMS;
+}
+
+bool AmANetworkSession::Session::wantsToRead() {
+ return !mSawReceiveFailure && mState != CONNECTING;
+}
+
+bool AmANetworkSession::Session::wantsToWrite() {
+ return !mSawSendFailure
+ && (mState == CONNECTING
+ || (mState == CONNECTED && !mOutFragments.empty())
+ || (mState == DATAGRAM && !mOutFragments.empty()));
+}
+
+status_t AmANetworkSession::Session::readMore() {
+ if (mState == DATAGRAM) {
+ CHECK_EQ(mMode, MODE_DATAGRAM);
+
+ status_t err;
+ do {
+ sp<ABuffer> buf = new ABuffer(kMaxUDPSize);
+
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ ssize_t n;
+ do {
+ n = recvfrom(
+ mSocket, buf->data(), buf->capacity(), 0,
+ (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+ } while (n < 0 && errno == EINTR);
+
+ err = OK;
+ if (mIsNeedRTPConnect) {
+ mIsNeedRTPConnect = false;
+ uint32_t connect_ip = ntohl(remoteAddr.sin_addr.s_addr);
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("reason", kWhatRTPConnect);
+ notify->setString("fromAddr", AStringPrintf(
+ "%u.%u.%u.%u",
+ connect_ip >> 24,
+ (connect_ip >> 16) & 0xff,
+ (connect_ip >> 8) & 0xff,
+ connect_ip & 0xff).c_str());
+ notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
+ notify->post();
+ }
+ if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ } else {
+ buf->setRange(0, n);
+
+ int64_t nowUs = ALooper::GetNowUs();
+ buf->meta()->setInt64("arrivalTimeUs", nowUs);
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatDatagram);
+
+ uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
+ notify->setString(
+ "fromAddr",
+ AStringPrintf(
+ "%u.%u.%u.%u",
+ ip >> 24,
+ (ip >> 16) & 0xff,
+ (ip >> 8) & 0xff,
+ ip & 0xff).c_str());
+
+ notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
+
+ notify->setBuffer("data", buf);
+ notify->post();
+ }
+ } while (err == OK);
+
+ if (err == -EAGAIN) {
+ err = OK;
+ }
+
+ if (err != OK) {
+ if (!mUDPRetries) {
+ notifyError(false /* send */, err, "Recvfrom failed.");
+ mSawReceiveFailure = true;
+ } else {
+ mUDPRetries--;
+ ALOGE("Recvfrom failed, %d/%d retries left",
+ mUDPRetries, kMaxUDPRetries);
+ err = OK;
+ }
+ } else {
+ mUDPRetries = kMaxUDPRetries;
+ }
+
+ return err;
+ }
+
+ 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);
+
+#if 0
+ ALOGI("in:");
+ hexdump(tmp, n);
+#endif
+ } else if (n < 0) {
+ err = -errno;
+ } else {
+ err = -ECONNRESET;
+ }
+
+ if (mMode == MODE_DATAGRAM) {
+ // TCP stream carrying 16-bit length-prefixed datagrams.
+
+ while (mInBuffer.size() >= 2) {
+ size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
+
+ if (mInBuffer.size() < packetSize + 2) {
+ break;
+ }
+
+ sp<ABuffer> packet = new ABuffer(packetSize);
+ memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
+
+ int64_t nowUs = ALooper::GetNowUs();
+ packet->meta()->setInt64("arrivalTimeUs", nowUs);
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatDatagram);
+ notify->setBuffer("data", packet);
+ notify->post();
+
+ mInBuffer.erase(0, packetSize + 2);
+ }
+ } else if (mMode == MODE_RTSP) {
+ for (;;) {
+ size_t length;
+
+ if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
+ if (mInBuffer.size() < 4) {
+ break;
+ }
+
+ length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
+
+ if (mInBuffer.size() < 4 + length) {
+ break;
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatBinaryData);
+ notify->setInt32("channel", mInBuffer.c_str()[1]);
+
+ sp<ABuffer> data = new ABuffer(length);
+ memcpy(data->data(), mInBuffer.c_str() + 4, length);
+
+ int64_t nowUs = ALooper::GetNowUs();
+ data->meta()->setInt64("arrivalTimeUs", nowUs);
+
+ notify->setBuffer("data", data);
+ notify->post();
+
+ mInBuffer.erase(0, 4 + length);
+ continue;
+ }
+
+ sp<ParsedMessage> msg =
+ ParsedMessage::Parse(
+ mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
+
+ if (msg == NULL) {
+ break;
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatData);
+ notify->setObject("data", msg);
+ notify->post();
+
+#if 1
+ // XXX The (old) dongle sends the wrong content length header on a
+ // SET_PARAMETER request that signals a "wfd_idr_request".
+ // (17 instead of 19).
+ const char *content = msg->getContent();
+ if (content
+ && !memcmp(content, "wfd_idr_request\r\n", 17)
+ && length >= 19
+ && mInBuffer.c_str()[length] == '\r'
+ && mInBuffer.c_str()[length + 1] == '\n') {
+ length += 2;
+ }
+#endif
+
+ mInBuffer.erase(0, length);
+
+ if (err != OK) {
+ break;
+ }
+ }
+ } else {
+ CHECK_EQ(mMode, MODE_WEBSOCKET);
+
+ const uint8_t *data = (const uint8_t *)mInBuffer.c_str();
+ // hexdump(data, mInBuffer.size());
+
+ while (mInBuffer.size() >= 2) {
+ size_t offset = 2;
+
+ uint64_t payloadLen = data[1] & 0x7f;
+ if (payloadLen == 126) {
+ if (offset + 2 > mInBuffer.size()) {
+ break;
+ }
+
+ payloadLen = U16_AT(&data[offset]);
+ offset += 2;
+ } else if (payloadLen == 127) {
+ if (offset + 8 > mInBuffer.size()) {
+ break;
+ }
+
+ payloadLen = U64_AT(&data[offset]);
+ offset += 8;
+ }
+
+ uint32_t mask = 0;
+ if (data[1] & 0x80) {
+ // MASK==1
+ if (offset + 4 > mInBuffer.size()) {
+ break;
+ }
+
+ mask = U32_AT(&data[offset]);
+ offset += 4;
+ }
+
+ if (payloadLen > mInBuffer.size() || offset > mInBuffer.size() - payloadLen) {
+ break;
+ }
+
+ // We have the full message.
+
+ sp<ABuffer> packet = new ABuffer(payloadLen);
+ memcpy(packet->data(), &data[offset], payloadLen);
+
+ if (mask != 0) {
+ for (size_t i = 0; i < payloadLen; ++i) {
+ packet->data()[i] =
+ data[offset + i]
+ ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+ }
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatWebSocketMessage);
+ notify->setBuffer("data", packet);
+ notify->setInt32("headerByte", data[0]);
+ notify->post();
+
+ mInBuffer.erase(0, offset + payloadLen);
+ }
+ }
+
+ if (err != OK) {
+ notifyError(false /* send */, err, "Recv failed.");
+ mSawReceiveFailure = true;
+ }
+
+ return err;
+}
+
+void AmANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
+#if 0
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
+
+ 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) %s\n",
+ frag.mTimeUs / 1000,
+ delayMs,
+ kPattern + kPatternSize - n);
+#endif
+}
+
+status_t AmANetworkSession::Session::writeMore() {
+ if (mState == DATAGRAM) {
+ CHECK(!mOutFragments.empty());
+
+ status_t err;
+ do {
+ const Fragment &frag = *mOutFragments.begin();
+ const sp<ABuffer> &datagram = frag.mBuffer;
+
+ int n;
+ do {
+ n = send(mSocket, datagram->data(), datagram->size(), 0);
+ } while (n < 0 && errno == EINTR);
+
+ err = OK;
+
+ if (n > 0) {
+ if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+ dumpFragmentStats(frag);
+ }
+
+ mOutFragments.erase(mOutFragments.begin());
+ } else if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ }
+ } while (err == OK && !mOutFragments.empty());
+
+ if (err == -EAGAIN) {
+ if (!mOutFragments.empty()) {
+ ALOGI("%zu datagrams remain queued.", mOutFragments.size());
+ }
+ err = OK;
+ }
+
+ if (err != OK) {
+ if (!mUDPRetries) {
+ notifyError(true /* send */, err, "Send datagram failed.");
+ mSawSendFailure = true;
+ } else {
+ mUDPRetries--;
+ ALOGE("Send datagram failed, %d/%d retries left",
+ mUDPRetries, kMaxUDPRetries);
+ err = OK;
+ }
+ } else {
+ mUDPRetries = kMaxUDPRetries;
+ }
+
+ return err;
+ }
+
+ 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(!mOutFragments.empty());
+
+ ssize_t n = -1;
+ while (!mOutFragments.empty()) {
+ const Fragment &frag = *mOutFragments.begin();
+
+ do {
+ n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0);
+ } while (n < 0 && errno == EINTR);
+
+ if (n <= 0) {
+ break;
+ }
+
+ frag.mBuffer->setRange(
+ frag.mBuffer->offset() + n, frag.mBuffer->size() - n);
+
+ if (frag.mBuffer->size() > 0) {
+ break;
+ }
+
+ if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+ dumpFragmentStats(frag);
+ }
+
+ mOutFragments.erase(mOutFragments.begin());
+ }
+
+ status_t err = OK;
+
+ if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ }
+
+ if (err != OK) {
+ notifyError(true /* send */, err, "Send failed.");
+ mSawSendFailure = true;
+ }
+
+#if 0
+ int numBytesQueued;
+ int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
+ if (res == 0 && numBytesQueued > 50 * 1024) {
+ if (numBytesQueued > 409600) {
+ ALOGW("!!! numBytesQueued = %d", numBytesQueued);
+ }
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+ if (mLastStallReportUs < 0ll
+ || nowUs > mLastStallReportUs + 100000ll) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatNetworkStall);
+ msg->setSize("numBytesQueued", numBytesQueued);
+ msg->post();
+
+ mLastStallReportUs = nowUs;
+ }
+ }
+#endif
+
+ return err;
+}
+
+status_t AmANetworkSession::Session::sendRequest(
+ const void *data, ssize_t size, bool timeValid, int64_t timeUs) {
+ CHECK(mState == CONNECTED || mState == DATAGRAM);
+
+ if (size < 0) {
+ size = strlen((const char *)data);
+ }
+
+ if (size == 0) {
+ return OK;
+ }
+
+ sp<ABuffer> buffer;
+
+ if (mState == CONNECTED && mMode == MODE_DATAGRAM) {
+ CHECK_LE(size, 65535);
+
+ buffer = new ABuffer(size + 2);
+ buffer->data()[0] = size >> 8;
+ buffer->data()[1] = size & 0xff;
+ memcpy(buffer->data() + 2, data, size);
+ } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) {
+ static const bool kUseMask = false; // Chromium doesn't like it.
+
+ size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0);
+ if (size > 65535) {
+ numHeaderBytes += 8;
+ } else if (size > 125) {
+ numHeaderBytes += 2;
+ }
+
+ buffer = new ABuffer(numHeaderBytes + size);
+ buffer->data()[0] = 0x81; // FIN==1 | opcode=1 (text)
+ buffer->data()[1] = kUseMask ? 0x80 : 0x00;
+
+ if (size > 65535) {
+ buffer->data()[1] |= 127;
+ buffer->data()[2] = 0x00;
+ buffer->data()[3] = 0x00;
+ buffer->data()[4] = 0x00;
+ buffer->data()[5] = 0x00;
+ buffer->data()[6] = (size >> 24) & 0xff;
+ buffer->data()[7] = (size >> 16) & 0xff;
+ buffer->data()[8] = (size >> 8) & 0xff;
+ buffer->data()[9] = size & 0xff;
+ } else if (size > 125) {
+ buffer->data()[1] |= 126;
+ buffer->data()[2] = (size >> 8) & 0xff;
+ buffer->data()[3] = size & 0xff;
+ } else {
+ buffer->data()[1] |= size;
+ }
+
+ if (kUseMask) {
+ uint32_t mask = rand();
+
+ buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff;
+ buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff;
+ buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff;
+ buffer->data()[numHeaderBytes - 1] = mask & 0xff;
+
+ for (size_t i = 0; i < (size_t)size; ++i) {
+ buffer->data()[numHeaderBytes + i] =
+ ((const uint8_t *)data)[i]
+ ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+ }
+ } else {
+ memcpy(buffer->data() + numHeaderBytes, data, size);
+ }
+ } else {
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ }
+
+ Fragment frag;
+
+ frag.mFlags = 0;
+ if (timeValid) {
+ frag.mFlags = FRAGMENT_FLAG_TIME_VALID;
+ frag.mTimeUs = timeUs;
+ }
+
+ frag.mBuffer = buffer;
+
+ mOutFragments.push_back(frag);
+
+ return OK;
+}
+
+void AmANetworkSession::Session::notifyError(
+ bool send, status_t err, const char *detail) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatError);
+ msg->setInt32("send", send);
+ msg->setInt32("err", err);
+ msg->setString("detail", detail);
+ msg->post();
+}
+
+void AmANetworkSession::Session::notify(NotificationReason reason) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", reason);
+ msg->post();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AmANetworkSession::AmANetworkSession()
+ : mNextSessionID(1),
+ mIsRTPConnection(false){
+ mPipeFd[0] = mPipeFd[1] = -1;
+}
+
+AmANetworkSession::~AmANetworkSession() {
+ stop();
+}
+
+status_t AmANetworkSession::start() {
+ if (mThread != NULL) {
+ return INVALID_OPERATION;
+ }
+
+ int res = pipe(mPipeFd);
+ if (res != 0) {
+ mPipeFd[0] = mPipeFd[1] = -1;
+ return -errno;
+ }
+
+ mThread = new NetworkThread(this);
+
+ status_t err = mThread->run("AmANetworkSession", ANDROID_PRIORITY_AUDIO);
+
+ if (err != OK) {
+ mThread.clear();
+
+ close(mPipeFd[0]);
+ close(mPipeFd[1]);
+ mPipeFd[0] = mPipeFd[1] = -1;
+
+ return err;
+ }
+
+ return OK;
+}
+
+status_t AmANetworkSession::stop() {
+ if (mThread == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ mThread->requestExit();
+ interrupt();
+ mThread->requestExitAndWait();
+
+ mThread.clear();
+
+ close(mPipeFd[0]);
+ close(mPipeFd[1]);
+ mPipeFd[0] = mPipeFd[1] = -1;
+
+ return OK;
+}
+
+status_t AmANetworkSession::createRTSPClient(
+ const char *host, unsigned port, const sp<AMessage> &notify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateRTSPClient,
+ NULL /* addr */,
+ 0 /* port */,
+ host,
+ port,
+ notify,
+ sessionID);
+}
+
+status_t AmANetworkSession::createRTSPServer(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> &notify, int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateRTSPServer,
+ &addr,
+ port,
+ NULL /* remoteHost */,
+ 0 /* remotePort */,
+ notify,
+ sessionID);
+}
+
+status_t AmANetworkSession::createUDPSession(
+ unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID) {
+ return createUDPSession(localPort, NULL, 0, notify, sessionID);
+}
+
+status_t AmANetworkSession::createUDPSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateUDPSession,
+ NULL /* addr */,
+ localPort,
+ remoteHost,
+ remotePort,
+ notify,
+ sessionID);
+}
+
+status_t AmANetworkSession::createTCPDatagramSession(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> &notify, int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateTCPDatagramSessionPassive,
+ &addr,
+ port,
+ NULL /* remoteHost */,
+ 0 /* remotePort */,
+ notify,
+ sessionID);
+}
+
+status_t AmANetworkSession::createTCPDatagramSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateTCPDatagramSessionActive,
+ NULL /* addr */,
+ localPort,
+ remoteHost,
+ remotePort,
+ notify,
+ sessionID);
+}
+
+status_t AmANetworkSession::destroySession(int32_t sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ mSessions.removeItemsAt(index);
+
+ interrupt();
+
+ return OK;
+}
+
+// static
+status_t AmANetworkSession::MakeSocketNonBlocking(int s) {
+ int flags = fcntl(s, F_GETFL, 0);
+ if (flags < 0) {
+ flags = 0;
+ }
+
+ int res = fcntl(s, F_SETFL, flags | O_NONBLOCK);
+ if (res < 0) {
+ return -errno;
+ }
+
+ return OK;
+}
+
+void AmANetworkSession::setRTPConnectionState(bool state){
+ mIsRTPConnection = state;
+}
+status_t AmANetworkSession::createClientOrServer(
+ Mode mode,
+ const struct in_addr *localAddr,
+ unsigned port,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ *sessionID = 0;
+ status_t err = OK;
+ int s, res;
+ sp<Session> session;
+
+ s = socket(
+ AF_INET,
+ (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
+ 0);
+
+ if (s < 0) {
+ err = -errno;
+ goto bail;
+ }
+
+ if (mode == kModeCreateRTSPServer
+ || mode == kModeCreateTCPDatagramSessionPassive) {
+ const int yes = 1;
+ res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ }
+
+ if (mode == kModeCreateUDPSession) {
+ int size = 256 * 1024;
+
+ res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ } else if (mode == kModeCreateTCPDatagramSessionActive) {
+ int flag = 1;
+ res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ int tos = 224; // VOICE
+ res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ }
+
+ err = MakeSocketNonBlocking(s);
+
+ if (err != OK) {
+ goto bail2;
+ }
+
+ struct sockaddr_in addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+ addr.sin_family = AF_INET;
+
+ if (mode == kModeCreateRTSPClient
+ || mode == kModeCreateTCPDatagramSessionActive) {
+ struct hostent *ent= gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ goto bail2;
+ }
+
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ addr.sin_port = htons(remotePort);
+ } else if (localAddr != NULL) {
+ addr.sin_addr = *localAddr;
+ addr.sin_port = htons(port);
+ } else {
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+ }
+
+ if (mode == kModeCreateRTSPClient
+ || mode == kModeCreateTCPDatagramSessionActive) {
+ in_addr_t x = ntohl(addr.sin_addr.s_addr);
+ ALOGI("connecting socket %d to %d.%d.%d.%d:%d",
+ s,
+ (x >> 24),
+ (x >> 16) & 0xff,
+ (x >> 8) & 0xff,
+ x & 0xff,
+ ntohs(addr.sin_port));
+
+ res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
+
+ CHECK_LT(res, 0);
+ if (errno == EINPROGRESS) {
+ res = 0;
+ }
+ } else {
+ res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
+
+ if (res == 0) {
+ if (mode == kModeCreateRTSPServer
+ || mode == kModeCreateTCPDatagramSessionPassive) {
+ res = listen(s, 4);
+ } else {
+ CHECK_EQ(mode, kModeCreateUDPSession);
+
+ if (remoteHost != NULL) {
+ struct sockaddr_in remoteAddr;
+ memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+ remoteAddr.sin_family = AF_INET;
+ remoteAddr.sin_port = htons(remotePort);
+
+ struct hostent *ent= gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ goto bail2;
+ }
+
+ remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+ res = connect(
+ s,
+ (const struct sockaddr *)&remoteAddr,
+ sizeof(remoteAddr));
+ }
+ }
+ }
+ }
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ Session::State state;
+ switch (mode) {
+ case kModeCreateRTSPClient:
+ state = Session::CONNECTING;
+ break;
+
+ case kModeCreateTCPDatagramSessionActive:
+ state = Session::CONNECTING;
+ break;
+
+ case kModeCreateTCPDatagramSessionPassive:
+ state = Session::LISTENING_TCP_DGRAMS;
+ break;
+
+ case kModeCreateRTSPServer:
+ state = Session::LISTENING_RTSP;
+ break;
+
+ default:
+ CHECK_EQ(mode, kModeCreateUDPSession);
+ state = Session::DATAGRAM;
+ break;
+ }
+
+ if (mIsRTPConnection) {
+ session = new Session(
+ mNextSessionID++,
+ state,
+ s,
+ true,
+ notify);
+ } else {
+ session = new Session(
+ mNextSessionID++,
+ state,
+ s,
+ false,
+ notify);
+ }
+ if (mode == kModeCreateTCPDatagramSessionActive) {
+ session->setMode(Session::MODE_DATAGRAM);
+ } else if (mode == kModeCreateRTSPClient) {
+ session->setMode(Session::MODE_RTSP);
+ }
+
+ mSessions.add(session->sessionID(), session);
+
+ interrupt();
+
+ *sessionID = session->sessionID();
+
+ goto bail;
+
+bail2:
+ close(s);
+ s = -1;
+
+bail:
+ return err;
+}
+
+status_t AmANetworkSession::connectUDPSession(
+ int32_t sessionID, const char *remoteHost, unsigned remotePort) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+ int s = session->socket();
+
+ struct sockaddr_in remoteAddr;
+ memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+ remoteAddr.sin_family = AF_INET;
+ remoteAddr.sin_port = htons(remotePort);
+
+ status_t err = OK;
+ struct hostent *ent = gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ } else {
+ remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+ int res = connect(
+ s,
+ (const struct sockaddr *)&remoteAddr,
+ sizeof(remoteAddr));
+
+ if (res < 0) {
+ err = -errno;
+ }
+ }
+
+ return err;
+}
+
+status_t AmANetworkSession::sendRequest(
+ int32_t sessionID, const void *data, ssize_t size,
+ bool timeValid, int64_t timeUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+
+ status_t err = session->sendRequest(data, size, timeValid, timeUs);
+
+ interrupt();
+
+ return err;
+}
+
+status_t AmANetworkSession::switchToWebSocketMode(int32_t sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+ return session->switchToWebSocketMode();
+}
+
+void AmANetworkSession::interrupt() {
+ static const char dummy = 0;
+
+ ssize_t n;
+ do {
+ n = write(mPipeFd[1], &dummy, 1);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) {
+ ALOGW("Error writing to pipe (%s)", strerror(errno));
+ }
+}
+
+void AmANetworkSession::threadLoop() {
+ fd_set rs, ws;
+ FD_ZERO(&rs);
+ FD_ZERO(&ws);
+
+ FD_SET(mPipeFd[0], &rs);
+ int maxFd = mPipeFd[0];
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ for (size_t i = 0; i < mSessions.size(); ++i) {
+ const sp<Session> &session = mSessions.valueAt(i);
+
+ int s = session->socket();
+
+ if (s < 0) {
+ continue;
+ }
+
+ if (session->wantsToRead()) {
+ FD_SET(s, &rs);
+ if (s > maxFd) {
+ maxFd = s;
+ }
+ }
+
+ if (session->wantsToWrite()) {
+ FD_SET(s, &ws);
+ if (s > maxFd) {
+ maxFd = s;
+ }
+ }
+ }
+ }
+
+ int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
+
+ if (res == 0) {
+ return;
+ }
+
+ if (res < 0) {
+ if (errno == EINTR) {
+ return;
+ }
+
+ ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
+ return;
+ }
+
+ if (FD_ISSET(mPipeFd[0], &rs)) {
+ char c;
+ ssize_t n;
+ do {
+ n = read(mPipeFd[0], &c, 1);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) {
+ ALOGW("Error reading from pipe (%s)", strerror(errno));
+ }
+
+ --res;
+ }
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ List<sp<Session> > sessionsToAdd;
+
+ for (size_t i = mSessions.size(); res > 0 && i > 0;) {
+ i--;
+ const sp<Session> &session = mSessions.valueAt(i);
+
+ int s = session->socket();
+
+ if (s < 0) {
+ continue;
+ }
+
+ if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
+ --res;
+ }
+
+ if (FD_ISSET(s, &rs)) {
+ if (session->isRTSPServer() || session->isTCPDatagramServer()) {
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ int clientSocket = accept(
+ s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+
+ if (clientSocket >= 0) {
+ status_t err = MakeSocketNonBlocking(clientSocket);
+
+ if (err != OK) {
+ ALOGE("Unable to make client socket non blocking, "
+ "failed w/ error %d (%s)",
+ err, strerror(-err));
+
+ close(clientSocket);
+ clientSocket = -1;
+ } else {
+ in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
+
+ ALOGI("incoming connection from %d.%d.%d.%d:%d "
+ "(socket %d)",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff,
+ ntohs(remoteAddr.sin_port),
+ clientSocket);
+
+ sp<Session> clientSession =
+ new Session(
+ mNextSessionID++,
+ Session::CONNECTED,
+ clientSocket,
+ false,
+ session->getNotificationMessage());
+
+ clientSession->setMode(
+ session->isRTSPServer()
+ ? Session::MODE_RTSP
+ : Session::MODE_DATAGRAM);
+
+ sessionsToAdd.push_back(clientSession);
+ }
+ } else {
+ ALOGE("accept returned error %d (%s)",
+ errno, strerror(errno));
+ }
+ } else {
+ status_t err = session->readMore();
+ if (err != OK) {
+ ALOGE("readMore on socket %d failed w/ error %d (%s)",
+ s, err, strerror(-err));
+ }
+ }
+ }
+
+ if (FD_ISSET(s, &ws)) {
+ status_t err = session->writeMore();
+ if (err != OK) {
+ ALOGE("writeMore on socket %d failed w/ error %d (%s)",
+ s, err, strerror(-err));
+ }
+ }
+ }
+
+ while (!sessionsToAdd.empty()) {
+ sp<Session> session = *sessionsToAdd.begin();
+ sessionsToAdd.erase(sessionsToAdd.begin());
+
+ mSessions.add(session->sessionID(), session);
+
+ ALOGI("added clientSession %d", session->sessionID());
+ }
+ }
+}
+
+} // namespace android
diff --git a/libstagefright/wifi-display/sink/AmANetworkSession.h b/libstagefright/wifi-display/sink/AmANetworkSession.h
new file mode 100644
index 0000000..dbf25fd
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/AmANetworkSession.h
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#ifndef AM_NETWORK_SESSION_H_
+
+#define AM_NETWORK_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/Thread.h>
+
+#include <netinet/in.h>
+
+namespace android {
+
+struct AMessage;
+
+// Helper class to manage a number of live sockets (datagram and stream-based)
+// on a single thread. Clients are notified about activity through AMessages.
+struct AmANetworkSession : public RefBase {
+ AmANetworkSession();
+
+ status_t start();
+ status_t stop();
+
+ status_t createRTSPClient(
+ const char *host, unsigned port, const sp<AMessage> &notify,
+ int32_t *sessionID);
+
+ status_t createRTSPServer(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> &notify, int32_t *sessionID);
+
+ status_t createUDPSession(
+ unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID);
+
+ status_t createUDPSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID);
+
+ status_t connectUDPSession(
+ int32_t sessionID, const char *remoteHost, unsigned remotePort);
+
+ // passive
+ status_t createTCPDatagramSession(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> &notify, int32_t *sessionID);
+
+ // active
+ status_t createTCPDatagramSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID);
+
+ status_t destroySession(int32_t sessionID);
+
+ status_t sendRequest(
+ int32_t sessionID, const void *data, ssize_t size = -1,
+ bool timeValid = false, int64_t timeUs = -1ll);
+
+ void setRTPConnectionState(bool state);
+
+ status_t switchToWebSocketMode(int32_t sessionID);
+
+ enum NotificationReason {
+ kWhatError,
+ kWhatConnected,
+ kWhatClientConnected,
+ kWhatData,
+ kWhatDatagram,
+ kWhatBinaryData,
+ kWhatWebSocketMessage,
+ kWhatNetworkStall,
+ kWhatRTPConnect,
+ };
+
+protected:
+ virtual ~AmANetworkSession();
+
+private:
+ struct NetworkThread;
+ struct Session;
+
+ Mutex mLock;
+ sp<Thread> mThread;
+
+ int32_t mNextSessionID;
+
+ int mPipeFd[2];
+
+ bool mIsRTPConnection;
+
+ KeyedVector<int32_t, sp<Session> > mSessions;
+
+ enum Mode {
+ kModeCreateUDPSession,
+ kModeCreateTCPDatagramSessionPassive,
+ kModeCreateTCPDatagramSessionActive,
+ kModeCreateRTSPServer,
+ kModeCreateRTSPClient,
+ };
+ status_t createClientOrServer(
+ Mode mode,
+ const struct in_addr *addr,
+ unsigned port,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> &notify,
+ int32_t *sessionID);
+
+ void threadLoop();
+ void interrupt();
+
+ static status_t MakeSocketNonBlocking(int s);
+
+ DISALLOW_EVIL_CONSTRUCTORS(AmANetworkSession);
+};
+
+} // namespace android
+
+#endif // AM_NETWORK_SESSION_H_
diff --git a/libstagefright/wifi-display/sink/LinearRegression.cpp b/libstagefright/wifi-display/sink/LinearRegression.cpp
new file mode 100644
index 0000000..a530c33
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/LinearRegression.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 "LinearRegression"
+#include <utils/Log.h>
+
+#include "LinearRegression.h"
+
+#include <math.h>
+#include <string.h>
+
+namespace android
+{
+
+ LinearRegression::LinearRegression(size_t historySize)
+ : mHistorySize(historySize),
+ mCount(0),
+ mHistory(new Point[mHistorySize]),
+ mSumX(0.0),
+ mSumY(0.0)
+ {
+ }
+
+ LinearRegression::~LinearRegression()
+ {
+ delete[] mHistory;
+ mHistory = NULL;
+ }
+
+ void LinearRegression::addPoint(float x, float y)
+ {
+ if (mCount == mHistorySize)
+ {
+ const Point &oldest = mHistory[0];
+
+ mSumX -= oldest.mX;
+ mSumY -= oldest.mY;
+
+ memmove(&mHistory[0], &mHistory[1], (mHistorySize - 1) * sizeof(Point));
+ --mCount;
+ }
+
+ Point *newest = &mHistory[mCount++];
+ newest->mX = x;
+ newest->mY = y;
+
+ mSumX += x;
+ mSumY += y;
+ }
+
+ bool LinearRegression::approxLine(float *n1, float *n2, float *b) const
+ {
+ static const float kEpsilon = 1.0E-4;
+
+ if (mCount < 2)
+ {
+ return false;
+ }
+
+ float sumX2 = 0.0f;
+ float sumY2 = 0.0f;
+ float sumXY = 0.0f;
+
+ float meanX = mSumX / (float)mCount;
+ float meanY = mSumY / (float)mCount;
+
+ for (size_t i = 0; i < mCount; ++i)
+ {
+ const Point &p = mHistory[i];
+
+ float x = p.mX - meanX;
+ float y = p.mY - meanY;
+
+ sumX2 += x * x;
+ sumY2 += y * y;
+ sumXY += x * y;
+ }
+
+ float T = sumX2 + sumY2;
+ float D = sumX2 * sumY2 - sumXY * sumXY;
+ float root = sqrt(T * T * 0.25 - D);
+
+ float L1 = T * 0.5 - root;
+
+ if (fabs(sumXY) > kEpsilon)
+ {
+ *n1 = 1.0;
+ *n2 = (2.0 * L1 - sumX2) / sumXY;
+
+ float mag = sqrt((*n1) * (*n1) + (*n2) * (*n2));
+
+ *n1 /= mag;
+ *n2 /= mag;
+ }
+ else
+ {
+ *n1 = 0.0;
+ *n2 = 1.0;
+ }
+
+ *b = (*n1) * meanX + (*n2) * meanY;
+
+ return true;
+ }
+
+} // namespace android
+
diff --git a/libstagefright/wifi-display/sink/LinearRegression.h b/libstagefright/wifi-display/sink/LinearRegression.h
new file mode 100644
index 0000000..dd42cb1
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/LinearRegression.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef LINEAR_REGRESSION_H_
+
+#define LINEAR_REGRESSION_H_
+
+#include <sys/types.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android
+{
+
+ // Helper class to fit a line to a set of points minimizing the sum of
+ // squared (orthogonal) distances from line to individual points.
+ struct LinearRegression
+ {
+ LinearRegression(size_t historySize);
+ ~LinearRegression();
+
+ void addPoint(float x, float y);
+
+ bool approxLine(float *n1, float *n2, float *b) const;
+
+ private:
+ struct Point
+ {
+ float mX, mY;
+ };
+
+ size_t mHistorySize;
+ size_t mCount;
+ Point *mHistory;
+
+ float mSumX, mSumY;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LinearRegression);
+ };
+
+} // namespace android
+
+#endif // LINEAR_REGRESSION_H_
diff --git a/libstagefright/wifi-display/sink/RTPSink.cpp b/libstagefright/wifi-display/sink/RTPSink.cpp
new file mode 100644
index 0000000..4da179e
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/RTPSink.cpp
@@ -0,0 +1,999 @@
+/*
+ * 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 "RTPSink"
+#include <utils/Log.h>
+
+#include "RTPSink.h"
+#include "Utils.h"
+
+#include "AmANetworkSession.h"
+#include "TunnelRenderer.h"
+#include <binder/IServiceManager.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+#include <media/stagefright/Utils.h>
+#else
+#include <media/stagefright/foundation/ByteUtils.h>
+#endif
+namespace android
+{
+
+ struct RTPSink::Source : public RefBase
+ {
+ Source(uint16_t seq, const sp<ABuffer> &buffer,
+ const sp<AMessage> queueBufferMsg);
+
+ bool updateSeq(uint16_t seq, const sp<ABuffer> &buffer);
+
+ void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
+
+ protected:
+ virtual ~Source();
+
+ private:
+ static const uint32_t kMinSequential = 2;
+ static const uint32_t kMaxDropout = 3000;
+ static const uint32_t kMaxMisorder = 100;
+ static const uint32_t kRTPSeqMod = 1u << 16;
+
+ sp<AMessage> mQueueBufferMsg;
+
+ uint16_t mMaxSeq;
+ uint32_t mCycles;
+ uint32_t mBaseSeq;
+ uint32_t mBadSeq;
+ uint32_t mProbation;
+ uint32_t mReceived;
+ uint32_t mExpectedPrior;
+ uint32_t mReceivedPrior;
+
+ void initSeq(uint16_t seq);
+ void queuePacket(const sp<ABuffer> &buffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Source);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ RTPSink::Source::Source(
+ uint16_t seq, const sp<ABuffer> &buffer,
+ const sp<AMessage> queueBufferMsg)
+ : mQueueBufferMsg(queueBufferMsg),
+ mProbation(kMinSequential)
+ {
+ initSeq(seq);
+ mMaxSeq = seq - 1;
+
+ buffer->setInt32Data(mCycles | seq);
+ queuePacket(buffer);
+ }
+
+ RTPSink::Source::~Source()
+ {
+ }
+
+ void RTPSink::Source::initSeq(uint16_t seq)
+ {
+ mMaxSeq = seq;
+ mCycles = 0;
+ mBaseSeq = seq;
+ mBadSeq = kRTPSeqMod + 1;
+ mReceived = 0;
+ mExpectedPrior = 0;
+ mReceivedPrior = 0;
+ }
+
+ bool RTPSink::Source::updateSeq(uint16_t seq, const sp<ABuffer> &buffer)
+ {
+ uint16_t udelta = seq - mMaxSeq;
+
+ if (mProbation)
+ {
+ // Startup phase
+
+ if (seq == mMaxSeq + 1)
+ {
+ buffer->setInt32Data(mCycles | seq);
+ queuePacket(buffer);
+
+ --mProbation;
+ mMaxSeq = seq;
+ if (mProbation == 0)
+ {
+ initSeq(seq);
+ ++mReceived;
+
+ return true;
+ }
+ }
+ else
+ {
+ // Packet out of sequence, restart startup phase
+
+ mProbation = kMinSequential - 1;
+ mMaxSeq = seq;
+
+#if 0
+ mPackets.clear();
+ mTotalBytesQueued = 0;
+ ALOGI("XXX cleared packets");
+#endif
+
+ buffer->setInt32Data(mCycles | seq);
+ queuePacket(buffer);
+ }
+
+ return false;
+ }
+
+ if (udelta < kMaxDropout)
+ {
+ // In order, with permissible gap.
+
+ if (seq < mMaxSeq)
+ {
+ // Sequence number wrapped - count another 64K cycle
+ mCycles += kRTPSeqMod;
+ }
+
+ mMaxSeq = seq;
+ }
+ else if (udelta <= kRTPSeqMod - kMaxMisorder)
+ {
+ // The sequence number made a very large jump
+
+ if (seq == mBadSeq)
+ {
+ // Two sequential packets -- assume that the other side
+ // restarted without telling us so just re-sync
+ // (i.e. pretend this was the first packet)
+
+ initSeq(seq);
+ }
+ else
+ {
+ mBadSeq = (seq + 1) & (kRTPSeqMod - 1);
+
+ return false;
+ }
+ }
+ else
+ {
+ // Duplicate or reordered packet.
+ }
+
+ ++mReceived;
+
+ buffer->setInt32Data(mCycles | seq);
+ queuePacket(buffer);
+
+ return true;
+ }
+
+ void RTPSink::Source::queuePacket(const sp<ABuffer> &buffer)
+ {
+ sp<AMessage> msg = mQueueBufferMsg->dup();
+ msg->setBuffer("buffer", buffer);
+ msg->post();
+ }
+
+ void RTPSink::Source::addReportBlock(
+ uint32_t ssrc, const sp<ABuffer> &buf)
+ {
+ uint32_t extMaxSeq = mMaxSeq | mCycles;
+ uint32_t expected = extMaxSeq - mBaseSeq + 1;
+
+ int64_t lost = (int64_t)expected - (int64_t)mReceived;
+ if (lost > 0x7fffff)
+ {
+ lost = 0x7fffff;
+ }
+ else if (lost < -0x800000)
+ {
+ lost = -0x800000;
+ }
+
+ uint32_t expectedInterval = expected - mExpectedPrior;
+ mExpectedPrior = expected;
+
+ uint32_t receivedInterval = mReceived - mReceivedPrior;
+ mReceivedPrior = mReceived;
+
+ int64_t lostInterval = expectedInterval - receivedInterval;
+
+ uint8_t fractionLost;
+ if (expectedInterval == 0 || lostInterval <= 0)
+ {
+ fractionLost = 0;
+ }
+ else
+ {
+ fractionLost = (lostInterval << 8) / expectedInterval;
+ }
+
+ uint8_t *ptr = buf->data() + buf->size();
+
+ ptr[0] = ssrc >> 24;
+ ptr[1] = (ssrc >> 16) & 0xff;
+ ptr[2] = (ssrc >> 8) & 0xff;
+ ptr[3] = ssrc & 0xff;
+
+ ptr[4] = fractionLost;
+
+ ptr[5] = (lost >> 16) & 0xff;
+ ptr[6] = (lost >> 8) & 0xff;
+ ptr[7] = lost & 0xff;
+
+ ptr[8] = extMaxSeq >> 24;
+ ptr[9] = (extMaxSeq >> 16) & 0xff;
+ ptr[10] = (extMaxSeq >> 8) & 0xff;
+ ptr[11] = extMaxSeq & 0xff;
+
+ // XXX TODO:
+
+ ptr[12] = 0x00; // interarrival jitter
+ ptr[13] = 0x00;
+ ptr[14] = 0x00;
+ ptr[15] = 0x00;
+
+ ptr[16] = 0x00; // last SR
+ ptr[17] = 0x00;
+ ptr[18] = 0x00;
+ ptr[19] = 0x00;
+
+ ptr[20] = 0x00; // delay since last SR
+ ptr[21] = 0x00;
+ ptr[22] = 0x00;
+ ptr[23] = 0x00;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ RTPSink::RTPSink(
+ const sp<AmANetworkSession> &netSession,
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> &stopNotify)
+ : mNetSession(netSession),
+ mBufferProducer(bufferProducer),
+ mRTPPort(0),
+ mRTPSessionID(0),
+ mRTCPSessionID(0),
+ mFirstArrivalTimeUs(-1ll),
+ mNumPacketsReceived(0ll),
+ mRegression(1000),
+ mMaxDelayMs(-1ll),
+ mStopNotify(stopNotify),
+ mIsHDCP(false)
+ {
+ mDumpEnable = getPropertyInt("sys.wfddump", 0);
+ }
+
+ RTPSink::~RTPSink()
+ {
+ if (mRTCPSessionID != 0)
+ {
+ mNetSession->destroySession(mRTCPSessionID);
+ }
+
+ if (mRTPSessionID != 0)
+ {
+ mNetSession->destroySession(mRTPSessionID);
+ }
+
+ if (mRenderer != NULL)
+ {
+ looper()->unregisterHandler(mRenderer->id());
+ mRenderer.clear();
+ }
+ }
+
+ status_t RTPSink::init(bool useTCPInterleaving)
+ {
+ if (useTCPInterleaving)
+ {
+ return OK;
+ }
+
+ int clientRtp;
+
+ sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, this);
+ sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, this);
+ for (clientRtp = 15550;; clientRtp += 2)
+ {
+ int32_t rtpSession;
+ mNetSession->setRTPConnectionState(true);
+ status_t err = mNetSession->createUDPSession(
+ clientRtp, rtpNotify, &rtpSession);
+
+ if (err != OK)
+ {
+ ALOGI("failed to create RTP socket on port %d", clientRtp);
+ continue;
+ }
+
+ mNetSession->setRTPConnectionState(false);
+
+ int32_t rtcpSession;
+ err = mNetSession->createUDPSession(
+ clientRtp + 1, rtcpNotify, &rtcpSession);
+
+ if (err == OK)
+ {
+ mRTPPort = clientRtp;
+ mRTPSessionID = rtpSession;
+ mRTCPSessionID = rtcpSession;
+ break;
+ }
+
+ ALOGI("failed to create RTCP socket on port %d", clientRtp + 1);
+ mNetSession->destroySession(rtpSession);
+ }
+
+ if (mRTPPort == 0)
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+ }
+
+ int32_t RTPSink::getRTPPort() const
+ {
+ return mRTPPort;
+ }
+
+ void RTPSink::onMessageReceived(const sp<AMessage> &msg)
+ {
+ switch (msg->what())
+ {
+ case kWhatRTPNotify:
+ case kWhatRTCPNotify:
+ {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ switch (reason)
+ {
+ case AmANetworkSession::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));
+
+ mNetSession->destroySession(sessionID);
+
+ if (sessionID == mRTPSessionID)
+ {
+ mRTPSessionID = 0;
+ }
+ else if (sessionID == mRTCPSessionID)
+ {
+ mRTCPSessionID = 0;
+ }
+ break;
+ }
+
+ case AmANetworkSession::kWhatDatagram:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ sp<ABuffer> data;
+ CHECK(msg->findBuffer("data", &data));
+
+ status_t err;
+ if (msg->what() == kWhatRTPNotify)
+ {
+ ALOGV("kWhatRTPNotify: parseRTP");
+ err = parseRTP(data);
+ }
+ else
+ {
+ ALOGV("kWhatRTCPNotify: parseRTCP");
+ err = parseRTCP(data);
+ }
+ break;
+ }
+
+ case AmANetworkSession::kWhatRTPConnect:
+ {
+ AString sourceHost;
+ CHECK(msg->findString("fromAddr", &sourceHost));
+ ALOGI("kWhatRTPConnect: %s", sourceHost.c_str());
+
+ int32_t rtpprot;
+ CHECK(msg->findInt32("fromPort", &rtpprot));
+ ALOGI("kWhatRTPConnect: %d", rtpprot);
+ if (msg->what() == kWhatRTPNotify)
+ {
+ connect(sourceHost.c_str(), rtpprot, rtpprot + 1);
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatSendRR:
+ {
+ onSendRR();
+ break;
+ }
+
+ case kWhatPacketLost:
+ {
+ onPacketLost(msg);
+ break;
+ }
+
+ case kWhatInject:
+ {
+ int32_t isRTP;
+ CHECK(msg->findInt32("isRTP", &isRTP));
+
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
+
+ status_t err;
+ if (isRTP)
+ {
+ err = parseRTP(buffer);
+ }
+ else
+ {
+ err = parseRTCP(buffer);
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ }
+
+ status_t RTPSink::injectPacket(bool isRTP, const sp<ABuffer> &buffer)
+ {
+ sp<AMessage> msg = new AMessage(kWhatInject, this);
+ msg->setInt32("isRTP", isRTP);
+ msg->setBuffer("buffer", buffer);
+ msg->post();
+
+ return OK;
+ }
+
+ void RTPSink::setIsHDCP(bool isHDCP)
+ {
+ mIsHDCP = isHDCP;
+ }
+
+ static char dump_buf[2048];
+
+ void dumpHex(unsigned char *buf, int len)
+ {
+ int i;
+ int base;
+ int offset = 0;
+ int nwritten;
+ for (offset = 0, base = 0; base < len; base += 16)
+ {
+ for (i = base; i < base + 16; i++)
+ {
+ if (i < len)
+ {
+ nwritten = sprintf(dump_buf + offset , "%02x ", (unsigned) buf[i]);
+ offset += nwritten;
+ }
+ else
+ {
+ nwritten = sprintf(dump_buf + offset , " ");
+ offset += nwritten;
+ }
+ }
+ nwritten = sprintf(dump_buf + offset , "\n");
+ offset += nwritten;
+ }
+
+ ALOGI("%s", dump_buf);
+ }
+
+
+
+ static FILE *fpo = NULL;
+
+ int dump_to_file(char *filepath, char *data, int len)
+ {
+ if (fpo == NULL)
+ {
+ fpo = fopen(filepath, "w+");
+ if (fpo == NULL)
+ {
+ ALOGI("failed to open output file %s", filepath);
+ return -1;
+ }
+ }
+
+ fwrite(data, 1, len, fpo);
+
+ return 0;
+ }
+
+
+ status_t RTPSink::parseRTP(const sp<ABuffer> &buffer)
+ {
+ size_t size = buffer->size();
+ if (size < 12)
+ {
+ // Too short to be a valid RTP header.
+ return ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = buffer->data();
+
+ if ((data[0] >> 6) != 2)
+ {
+ // Unsupported version.
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (data[0] & 0x20)
+ {
+ // Padding present.
+
+ size_t paddingLength = data[size - 1];
+
+ if (paddingLength + 12 > size)
+ {
+ // If we removed this much padding we'd end up with something
+ // that's too short to be a valid RTP header.
+ return ERROR_MALFORMED;
+ }
+
+ size -= paddingLength;
+ }
+
+ //dumpHex(buffer->data(), size);
+//#if 0
+ if (mDumpEnable == 1)
+ dump_to_file((char *)"/data/misc/rtp.data", (char *)(buffer->data()) + 12, size - 12);
+//#endif
+ int numCSRCs = data[0] & 0x0f;
+
+ size_t payloadOffset = 12 + 4 * numCSRCs;
+
+ if (size < payloadOffset)
+ {
+ // Not enough data to fit the basic header and all the CSRC entries.
+ return ERROR_MALFORMED;
+ }
+
+ if (data[0] & 0x10)
+ {
+ // Header eXtension present.
+
+ if (size < payloadOffset + 4)
+ {
+ // Not enough data to fit the basic header, all CSRC entries
+ // and the first 4 bytes of the extension header.
+
+ return ERROR_MALFORMED;
+ }
+
+ const uint8_t *extensionData = &data[payloadOffset];
+
+ size_t extensionLength =
+ 4 * (extensionData[2] << 8 | extensionData[3]);
+
+ if (size < payloadOffset + 4 + extensionLength)
+ {
+ return ERROR_MALFORMED;
+ }
+
+ payloadOffset += 4 + extensionLength;
+ }
+
+ uint32_t srcId = U32_AT(&data[8]);
+ uint32_t rtpTime = U32_AT(&data[4]);
+ uint16_t seqNo = U16_AT(&data[2]);
+
+ int64_t arrivalTimeUs;
+ CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs));
+
+ if (mFirstArrivalTimeUs < 0ll)
+ {
+ mFirstArrivalTimeUs = arrivalTimeUs;
+ }
+ arrivalTimeUs -= mFirstArrivalTimeUs;
+
+ int64_t arrivalTimeMedia = (arrivalTimeUs * 9ll) / 100ll;
+
+ ALOGV("Received RTP: seqNo: %d, SSRC 0x%08x, diff %lld",
+ seqNo, srcId, rtpTime - arrivalTimeMedia);
+
+ mRegression.addPoint((float)rtpTime, (float)arrivalTimeMedia);
+
+ ++mNumPacketsReceived;
+
+ float n1, n2, b;
+ if (mRegression.approxLine(&n1, &n2, &b))
+ {
+ ALOGV("Line %lld: %.2f %.2f %.2f, slope %.2f",
+ mNumPacketsReceived, n1, n2, b, -n1 / n2);
+
+ float expectedArrivalTimeMedia = (b - n1 * (float)rtpTime) / n2;
+ float latenessMs = (arrivalTimeMedia - expectedArrivalTimeMedia) / 90.0;
+
+ if (mMaxDelayMs < 0ll || latenessMs > mMaxDelayMs)
+ {
+ mMaxDelayMs = latenessMs;
+ ALOGV("packet was %.2f ms late", latenessMs);
+ }
+ }
+
+ sp<AMessage> meta = buffer->meta();
+ meta->setInt32("ssrc", srcId);
+ meta->setInt32("rtp-time", rtpTime);
+ meta->setInt32("PT", data[1] & 0x7f);
+ meta->setInt32("M", data[1] >> 7);
+
+ buffer->setRange(payloadOffset, size - payloadOffset);
+
+ ssize_t index = mSources.indexOfKey(srcId);
+ if (index < 0)
+ {
+ if (mRenderer == NULL)
+ {
+ sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, this);
+ notifyLost->setInt32("ssrc", srcId);
+
+ mRenderer = new TunnelRenderer(notifyLost, mBufferProducer, mStopNotify);
+ looper()->registerHandler(mRenderer);
+
+ mRenderer->setIsHDCP(mIsHDCP);
+ }
+
+ sp<AMessage> queueBufferMsg =
+ new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer);
+
+ sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);
+ mSources.add(srcId, source);
+ }
+ else
+ {
+ mSources.valueAt(index)->updateSeq(seqNo, buffer);
+ }
+
+ return OK;
+ }
+
+ status_t RTPSink::parseRTCP(const sp<ABuffer> &buffer)
+ {
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ while (size > 0)
+ {
+ if (size < 8)
+ {
+ // Too short to be a valid RTCP header
+ return ERROR_MALFORMED;
+ }
+
+ if ((data[0] >> 6) != 2)
+ {
+ // Unsupported version.
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (data[0] & 0x20)
+ {
+ // Padding present.
+
+ size_t paddingLength = data[size - 1];
+
+ if (paddingLength + 12 > size)
+ {
+ // If we removed this much padding we'd end up with something
+ // that's too short to be a valid RTP header.
+ return ERROR_MALFORMED;
+ }
+
+ size -= paddingLength;
+ }
+
+ size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+ if (size < headerLength)
+ {
+ // Only received a partial packet?
+ return ERROR_MALFORMED;
+ }
+
+ switch (data[1])
+ {
+ case 200:
+ {
+ parseSR(data, headerLength);
+ break;
+ }
+
+ case 201: // RR
+ case 202: // SDES
+ case 204: // APP
+ break;
+
+ case 205: // TSFB (transport layer specific feedback)
+ case 206: // PSFB (payload specific feedback)
+ // hexdump(data, headerLength);
+ break;
+
+ case 203:
+ {
+ parseBYE(data, headerLength);
+ break;
+ }
+
+ default:
+ {
+ ALOGW("Unknown RTCP packet type %u of size %d",
+ (unsigned)data[1], headerLength);
+ break;
+ }
+ }
+
+ data += headerLength;
+ size -= headerLength;
+ }
+
+ return OK;
+ }
+
+ status_t RTPSink::parseBYE(const uint8_t *data, size_t size)
+ {
+ size_t SC = data[0] & 0x3f;
+
+ if (SC == 0 || size < (4 + SC * 4))
+ {
+ // Packet too short for the minimal BYE header.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t id = U32_AT(&data[4]);
+
+ return OK;
+ }
+
+ status_t RTPSink::parseSR(const uint8_t *data, size_t size)
+ {
+ size_t RC = data[0] & 0x1f;
+
+ if (size < (7 + RC * 6) * 4)
+ {
+ // Packet too short for the minimal SR header.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t id = U32_AT(&data[4]);
+ uint64_t ntpTime = U64_AT(&data[8]);
+ uint32_t rtpTime = U32_AT(&data[16]);
+
+ ALOGI("SR: ssrc 0x%08x, ntpTime 0x%016llx, rtpTime 0x%08x",
+ id, ntpTime, rtpTime);
+
+ return OK;
+ }
+
+ status_t RTPSink::connect(
+ const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort)
+ {
+ ALOGI("connecting RTP/RTCP sockets to %s:{%d,%d}",
+ host, remoteRtpPort, remoteRtcpPort);
+
+ status_t err =
+ mNetSession->connectUDPSession(mRTPSessionID, host, remoteRtpPort);
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ err = mNetSession->connectUDPSession(mRTCPSessionID, host, remoteRtcpPort);
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+#if 0
+ sp<ABuffer> buf = new ABuffer(1500);
+ memset(buf->data(), 0, buf->size());
+
+ mNetSession->sendRequest(
+ mRTPSessionID, buf->data(), buf->size());
+
+ mNetSession->sendRequest(
+ mRTCPSessionID, buf->data(), buf->size());
+#endif
+
+ //scheduleSendRR();
+
+ return OK;
+ }
+
+ void RTPSink::scheduleSendRR()
+ {
+ (new AMessage(kWhatSendRR, this))->post(2000000ll);
+ }
+
+ void RTPSink::addSDES(const sp<ABuffer> &buffer)
+ {
+ uint8_t *data = buffer->data() + buffer->size();
+ data[0] = 0x80 | 1;
+ data[1] = 202; // SDES
+ data[4] = 0xde; // SSRC
+ data[5] = 0xad;
+ data[6] = 0xbe;
+ data[7] = 0xef;
+
+ size_t offset = 8;
+
+ data[offset++] = 1; // CNAME
+
+ AString cname = "stagefright@somewhere";
+ data[offset++] = cname.size();
+
+ memcpy(&data[offset], cname.c_str(), cname.size());
+ offset += cname.size();
+
+ data[offset++] = 6; // TOOL
+
+ AString tool = "stagefright/1.0";
+ data[offset++] = tool.size();
+
+ memcpy(&data[offset], tool.c_str(), tool.size());
+ offset += tool.size();
+
+ data[offset++] = 0;
+
+ if ((offset % 4) > 0)
+ {
+ size_t count = 4 - (offset % 4);
+ switch (count)
+ {
+ case 3:
+ data[offset++] = 0;
+ case 2:
+ data[offset++] = 0;
+ case 1:
+ data[offset++] = 0;
+ }
+ }
+
+ size_t numWords = (offset / 4) - 1;
+ data[2] = numWords >> 8;
+ data[3] = numWords & 0xff;
+
+ buffer->setRange(buffer->offset(), buffer->size() + offset);
+ }
+
+ void RTPSink::onSendRR()
+ {
+ sp<ABuffer> buf = new ABuffer(1500);
+ buf->setRange(0, 0);
+
+ uint8_t *ptr = buf->data();
+ ptr[0] = 0x80 | 0;
+ ptr[1] = 201; // RR
+ ptr[2] = 0;
+ ptr[3] = 1;
+ ptr[4] = 0xde; // SSRC
+ ptr[5] = 0xad;
+ ptr[6] = 0xbe;
+ ptr[7] = 0xef;
+
+ buf->setRange(0, 8);
+
+ size_t numReportBlocks = 0;
+ for (size_t i = 0; i < mSources.size(); ++i)
+ {
+ uint32_t ssrc = mSources.keyAt(i);
+ sp<Source> source = mSources.valueAt(i);
+
+ if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity())
+ {
+ // Cannot fit another report block.
+ break;
+ }
+
+ source->addReportBlock(ssrc, buf);
+ ++numReportBlocks;
+ }
+
+ ptr[0] |= numReportBlocks; // 5 bit
+
+ size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
+ ptr[2] = sizeInWordsMinus1 >> 8;
+ ptr[3] = sizeInWordsMinus1 & 0xff;
+
+ buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
+
+ addSDES(buf);
+
+ ALOGE("Send RTCP Receiver Report");
+ mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+
+ scheduleSendRR();
+ }
+
+ void RTPSink::onPacketLost(const sp<AMessage> &msg)
+ {
+ uint32_t srcId;
+ CHECK(msg->findInt32("ssrc", (int32_t *)&srcId));
+
+ int32_t seqNo;
+ CHECK(msg->findInt32("seqNo", &seqNo));
+
+ int32_t blp = 0;
+
+ sp<ABuffer> buf = new ABuffer(1500);
+ buf->setRange(0, 0);
+
+ uint8_t *ptr = buf->data();
+ ptr[0] = 0x80 | 1; // generic NACK
+ ptr[1] = 205; // RTPFB
+ ptr[2] = 0;
+ ptr[3] = 3;
+ ptr[4] = 0xde; // sender SSRC
+ ptr[5] = 0xad;
+ ptr[6] = 0xbe;
+ ptr[7] = 0xef;
+ ptr[8] = (srcId >> 24) & 0xff;
+ ptr[9] = (srcId >> 16) & 0xff;
+ ptr[10] = (srcId >> 8) & 0xff;
+ ptr[11] = (srcId & 0xff);
+ ptr[12] = (seqNo >> 8) & 0xff;
+ ptr[13] = (seqNo & 0xff);
+ ptr[14] = (blp >> 8) & 0xff;
+ ptr[15] = (blp & 0xff);
+
+ buf->setRange(0, 16);
+
+ mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
+ }
+
+} // namespace android
+
diff --git a/libstagefright/wifi-display/sink/RTPSink.h b/libstagefright/wifi-display/sink/RTPSink.h
new file mode 100644
index 0000000..a0f7af1
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/RTPSink.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef RTP_SINK_H_
+
+#define RTP_SINK_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "LinearRegression.h"
+
+#include <gui/Surface.h>
+
+
+namespace android
+{
+
+ struct ABuffer;
+ struct AmANetworkSession;
+ struct TunnelRenderer;
+
+ // Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer
+ // for incoming transport stream data and occasionally sends statistics over
+ // the RTCP channel.
+ struct RTPSink : public AHandler
+ {
+ RTPSink(const sp<AmANetworkSession> &netSession,
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> &stopNotify);
+
+ // If TCP interleaving is used, no UDP sockets are created, instead
+ // incoming RTP/RTCP packets (arriving on the RTSP control connection)
+ // are manually injected by WifiDisplaySink.
+ status_t init(bool useTCPInterleaving);
+
+ status_t connect(
+ const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort);
+
+ int32_t getRTPPort() const;
+
+ status_t injectPacket(bool isRTP, const sp<ABuffer> &buffer);
+
+ void setIsHDCP(bool isHDCP);
+
+ protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual ~RTPSink();
+
+ private:
+ enum
+ {
+ kWhatRTPNotify,
+ kWhatRTCPNotify,
+ kWhatSendRR,
+ kWhatPacketLost,
+ kWhatInject,
+ };
+
+ struct Source;
+ struct StreamSource;
+
+ sp<AmANetworkSession> mNetSession;
+ sp<IGraphicBufferProducer> mBufferProducer;
+ KeyedVector<uint32_t, sp<Source> > mSources;
+
+ int32_t mRTPPort;
+ int32_t mRTPSessionID;
+ int32_t mRTCPSessionID;
+
+ int64_t mFirstArrivalTimeUs;
+ int64_t mNumPacketsReceived;
+ LinearRegression mRegression;
+ int64_t mMaxDelayMs;
+ sp<AMessage> mStopNotify;
+
+ sp<TunnelRenderer> mRenderer;
+ int32_t mDumpEnable;
+
+ bool mIsHDCP;
+
+ status_t parseRTP(const sp<ABuffer> &buffer);
+ status_t parseRTCP(const sp<ABuffer> &buffer);
+ status_t parseBYE(const uint8_t *data, size_t size);
+ status_t parseSR(const uint8_t *data, size_t size);
+
+ void addSDES(const sp<ABuffer> &buffer);
+ void onSendRR();
+ void onPacketLost(const sp<AMessage> &msg);
+ void scheduleSendRR();
+
+ DISALLOW_EVIL_CONSTRUCTORS(RTPSink);
+ };
+
+} // namespace android
+
+#endif // RTP_SINK_H_
diff --git a/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/libstagefright/wifi-display/sink/TunnelRenderer.cpp
new file mode 100644
index 0000000..fadb881
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/TunnelRenderer.cpp
@@ -0,0 +1,618 @@
+/*
+ * 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 "TunnelRenderer"
+#include <utils/Log.h>
+
+#include "TunnelRenderer.h"
+
+#include "ATSParser.h"
+#include "Utils.h"
+#include <binder/IMemory.h>
+#include <binder/IServiceManager.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/ISurfaceComposer.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IStreamSource.h>
+#include <media/mediaplayer.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <ui/DisplayInfo.h>
+#include <cutils/properties.h>
+#include <media/ammediaplayerext.h>
+
+namespace android
+{
+ struct TunnelRenderer::PlayerClient : public BnMediaPlayerClient
+ {
+ PlayerClient()
+ :mPrepared(false)
+ {
+
+ }
+
+ virtual void notify(int msg, int ext1, int ext2, const Parcel *obj)
+ {
+ ALOGI("notify %d, %d, %d, %p", msg, ext1, ext2, obj);
+ switch (msg) {
+ case MEDIA_PREPARED:
+ mPrepared = true;
+ break;
+ default:
+ break;
+ }
+ }
+ bool isPrepared() {
+ return mPrepared;
+ }
+
+ protected:
+ virtual ~PlayerClient()
+ {
+ ALOGI("~PlayerClient");
+ }
+
+ private:
+ bool mPrepared;
+ DISALLOW_EVIL_CONSTRUCTORS(PlayerClient);
+ };
+
+ struct TunnelRenderer::StreamSource : public BnStreamSource
+ {
+ StreamSource(TunnelRenderer *owner);
+
+ virtual void setListener(const sp<IStreamListener> &listener);
+ virtual void setBuffers(const Vector<sp<IMemory> > &buffers);
+
+ virtual void onBufferAvailable(size_t index);
+
+ virtual uint32_t flags() const;
+
+ void doSomeWork();
+
+ protected:
+ virtual ~StreamSource();
+
+ private:
+ mutable Mutex mLock;
+
+ TunnelRenderer *mOwner;
+
+ sp<IStreamListener> mListener;
+
+ Vector<sp<IMemory> > mBuffers;
+ List<size_t> mIndicesAvailable;
+
+ size_t mNumDeqeued;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner)
+ : mOwner(owner),
+ mNumDeqeued(0)
+ {
+ }
+
+ TunnelRenderer::StreamSource::~StreamSource()
+ {
+ ALOGI("~StreamSource");
+ }
+
+ void TunnelRenderer::StreamSource::setListener(
+ const sp<IStreamListener> &listener)
+ {
+ mListener = listener;
+ }
+
+ void TunnelRenderer::StreamSource::setBuffers(
+ const Vector<sp<IMemory> > &buffers)
+ {
+ mBuffers = buffers;
+ }
+
+ void TunnelRenderer::StreamSource::onBufferAvailable(size_t index)
+ {
+ if (index >= 0x80000000)
+ {
+ doSomeWork();
+ return ;/*require buffer command,for amplayer only. ignore.now*/
+ }
+ CHECK_LT(index, mBuffers.size());
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ mIndicesAvailable.push_back(index);
+ }
+
+ doSomeWork();
+ }
+
+ uint32_t TunnelRenderer::StreamSource::flags() const
+ {
+ if (mOwner->getIsHDCP()) {
+ return kFlagAlignedVideoData;
+ } else {
+ return 0;
+ }
+ }
+
+ void TunnelRenderer::StreamSource::doSomeWork()
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ while (!mIndicesAvailable.empty())
+ {
+ sp<ABuffer> srcBuffer = mOwner->dequeueBuffer();
+ if (srcBuffer == NULL)
+ {
+ break;
+ }
+
+ ++mNumDeqeued;
+
+ if (mNumDeqeued == 1)
+ {
+ ALOGI("fixing real time now.");
+
+ sp<AMessage> extra = new AMessage;
+
+#if 0
+ extra->setInt32(
+ IStreamListener::kKeyDiscontinuityMask,
+ ATSParser::DISCONTINUITY_ABSOLUTE_TIME);
+#endif
+
+ extra->setInt64("timeUs", ALooper::GetNowUs());
+
+ mListener->issueCommand(
+ IStreamListener::DISCONTINUITY,
+ false /* synchronous */,
+ extra);
+ }
+
+ ALOGV("dequeue TS packet of size %d", srcBuffer->size());
+
+ size_t index = *mIndicesAvailable.begin();
+ mIndicesAvailable.erase(mIndicesAvailable.begin());
+
+ sp<IMemory> mem = mBuffers.itemAt(index);
+ CHECK_LE(srcBuffer->size(), mem->size());
+ CHECK_EQ((srcBuffer->size() % 188), 0u);
+
+ memcpy(mem->pointer(), srcBuffer->data(), srcBuffer->size());
+ mListener->queueBuffer(index, srcBuffer->size());
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ TunnelRenderer::TunnelRenderer(
+ const sp<AMessage> &notifyLost,
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> &stopNotify)
+ : mNotifyLost(notifyLost),
+ mBufferProducer(bufferProducer),
+ mTotalBytesQueued(0ll),
+ mMaxBytesQueued(0ll),
+ mMinBytesQueued(0ll),
+ mRetryTimes(0ll),
+ mBytesQueued(0),
+ mDebugEnable(false),
+ mLastDequeuedExtSeqNo(-1),
+ mFirstFailedAttemptUs(-1ll),
+ mPackageSuccess(0),
+ mPackageFailed(0),
+ mPackageRequest(0),
+ mRequestedRetry(false),
+ mRequestedRetransmission(false),
+ mStopNotify(stopNotify),
+ mIsHDCP(false)
+ {
+ mCurTime = ALooper::GetNowUs();
+
+ int d = getPropertyInt("sys.wfddump", 0) ;
+ d &= 0x02;
+ if (d == 2)
+ mDebugEnable = true;
+ else
+ mDebugEnable = false;
+ ALOGE("Miracast debug info enable? d=%d, mDebugEnable =%d\n", d , mDebugEnable );
+ if (mDebugEnable)
+ ALOGE("Miracast debug info enabled\n");
+ setProperty("sys.pkginfo", "suc:0,fail:0,req:0,total:0, max:0,min:0,retry:0,band:0");
+ }
+
+ TunnelRenderer::~TunnelRenderer()
+ {
+ destroyPlayer();
+ }
+
+ void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer)
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ mTotalBytesQueued += buffer->size();
+ mBytesQueued += buffer->size();
+ mMaxBytesQueued = mMaxBytesQueued > mTotalBytesQueued ? mMaxBytesQueued : mTotalBytesQueued;
+ if (mPackets.empty())
+ {
+ mPackets.push_back(buffer);
+ return;
+ }
+
+ int32_t newExtendedSeqNo = buffer->int32Data();
+
+ List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
+ List<sp<ABuffer> >::iterator it = --mPackets.end();
+ for (;;)
+ {
+ int32_t extendedSeqNo = (*it)->int32Data();
+
+ if (extendedSeqNo == newExtendedSeqNo)
+ {
+ // Duplicate packet.
+ return;
+ }
+
+ if (extendedSeqNo < newExtendedSeqNo)
+ {
+ // Insert new packet after the one at "it".
+ mPackets.insert(++it, buffer);
+ return;
+ }
+
+ if (it == firstIt)
+ {
+ // Insert new packet before the first existing one.
+ mPackets.insert(it, buffer);
+ return;
+ }
+
+ --it;
+ }
+ }
+
+ sp<ABuffer> TunnelRenderer::dequeueBuffer()
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<ABuffer> buffer;
+ int32_t extSeqNo;
+ char pkg_info[128];
+ int64_t curUs;
+
+ while (!mPackets.empty())
+ {
+ buffer = *mPackets.begin();
+ extSeqNo = buffer->int32Data();
+
+ if (mLastDequeuedExtSeqNo < 0 || extSeqNo > mLastDequeuedExtSeqNo)
+ {
+ break;
+ }
+
+ // This is a retransmission of a packet we've already returned.
+
+ mTotalBytesQueued -= buffer->size();
+ mMinBytesQueued = mMinBytesQueued < mTotalBytesQueued ? mMinBytesQueued : mTotalBytesQueued;
+ buffer.clear();
+ extSeqNo = -1;
+
+ mPackets.erase(mPackets.begin());
+ }
+
+ if (mPackets.empty())
+ {
+ if (mFirstFailedAttemptUs < 0ll)
+ {
+ mFirstFailedAttemptUs = ALooper::GetNowUs();
+ mRequestedRetry = false;
+ mRequestedRetransmission = false;
+ }
+ else
+ {
+ float noPacketTime = (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6;
+ ALOGV("no packets available for %.2f secs",noPacketTime);
+ if (noPacketTime>12.0) //beyond 12S
+ {
+#if 1
+
+ int ret = -1;
+ char value[PROPERTY_VALUE_MAX];
+ ret = getPropertyInt("sys.wfd.state", 0);
+ if (ret == 0)
+ {
+ ALOGI("no packets available beyond 12 secs,stop WifiDisplaySink now");
+ sp<AMessage> notify = mStopNotify->dup();
+ notify->post();
+ mFirstFailedAttemptUs = ALooper::GetNowUs();
+ }
+ else if (ret == 2)
+ {
+ ALOGI("pause->play, reset noPacketTime!");
+ mFirstFailedAttemptUs = ALooper::GetNowUs();
+ setProperty("sys.wfd.state", "0");
+ }
+#endif
+ }
+ }
+ return NULL;
+ }
+
+ if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1)
+ {
+ if (mRequestedRetransmission)
+ {
+ ALOGI("Recovered after requesting retransmission of %d",
+ extSeqNo);
+ }
+
+ if (!mRequestedRetry)
+ {
+ mPackageSuccess++;
+ }
+ else
+ {
+ mRequestedRetry = false;
+ mPackageRequest++;
+ }
+ if (mDebugEnable)
+ {
+ /*calculate bandwidth every 1s once*/
+ curUs = ALooper::GetNowUs();
+ if (curUs - mCurTime >= 1000000ll)
+ {
+ mBandwidth = mBytesQueued * 1000000ll / (curUs - mCurTime);
+ //ALOGI("309 curUs - mCurTime =%lld, bytes=%ld", curUs-mCurTime, mBytesQueued);
+ mBytesQueued = 0;
+ mCurTime = ALooper::GetNowUs();
+ sprintf(pkg_info, "suc:%d,fail:%d,req:%d, total:%lld, max:%lld,min:%lld,retry:%lld, band:%lld",
+ mPackageSuccess, mPackageFailed, mPackageRequest,
+ mTotalBytesQueued, mMaxBytesQueued, mMinBytesQueued,
+ mRetryTimes, mBandwidth);
+ setProperty("sys.pkginfo", pkg_info);
+ }
+ }
+ mLastDequeuedExtSeqNo = extSeqNo;
+ mFirstFailedAttemptUs = -1ll;
+ mRequestedRetransmission = false;
+
+ mPackets.erase(mPackets.begin());
+
+ mTotalBytesQueued -= buffer->size();
+ mMinBytesQueued = mMinBytesQueued < mTotalBytesQueued ? mMinBytesQueued : mTotalBytesQueued;
+ return buffer;
+ }
+
+ if (mFirstFailedAttemptUs < 0ll)
+ {
+ mFirstFailedAttemptUs = ALooper::GetNowUs();
+
+ ALOGI("failed to get the correct packet the first time.");
+ return NULL;
+ }
+
+ if (mFirstFailedAttemptUs + 10000ll > ALooper::GetNowUs())
+ {
+ // We're willing to wait a little while to get the right packet.
+
+ if (!mRequestedRetransmission)
+ {
+ ALOGI("requesting retransmission of seqNo %d",
+ (mLastDequeuedExtSeqNo + 1) & 0xffff);
+
+ sp<AMessage> notify = mNotifyLost->dup();
+ notify->setInt32("seqNo", (mLastDequeuedExtSeqNo + 1) & 0xffff);
+ notify->post();
+
+ mRequestedRetry = true;
+ mRequestedRetransmission = true;
+ mRetryTimes++;
+ }
+ else
+ {
+ ALOGI("still waiting for the correct packet to arrive.");
+ }
+
+ return NULL;
+ }
+
+ ALOGI("dropping packet. extSeqNo %d didn't arrive in time",
+ mLastDequeuedExtSeqNo + 1);
+
+ // Permanent failure, we never received the packet.
+ mPackageFailed++;
+
+ if (mDebugEnable)
+ {
+ /*calculate bandwidth every 2s once*/
+ curUs = ALooper::GetNowUs();
+ if (curUs - mCurTime >= 1000000ll)
+ {
+ mBandwidth = mBytesQueued * ((float)1000000ll / (curUs - mCurTime));
+ //ALOGI("367 curUs - mCurTime =%lld, bytes=%ld", curUs-mCurTime, mBytesQueued);
+ mBytesQueued = 0;
+ mCurTime = ALooper::GetNowUs();
+ sprintf(pkg_info, "suc:%d,fail:%d,req:%d, total:%lld, max:%lld,min:%lld,retry:%lld, band:%lld",
+ mPackageSuccess, mPackageFailed, mPackageRequest,
+ mTotalBytesQueued, mMaxBytesQueued, mMinBytesQueued,
+ mRetryTimes, mBandwidth);
+ setProperty("sys.pkginfo", pkg_info);
+ }
+ }
+ mLastDequeuedExtSeqNo = extSeqNo;
+ mFirstFailedAttemptUs = -1ll;
+ mRequestedRetransmission = false;
+ mRequestedRetry = false;
+
+ mTotalBytesQueued -= buffer->size();
+ mMinBytesQueued = mMinBytesQueued < mTotalBytesQueued ? mMinBytesQueued : mTotalBytesQueued;
+ mPackets.erase(mPackets.begin());
+
+ return buffer;
+ }
+
+ void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg)
+ {
+ switch (msg->what())
+ {
+ case kWhatQueueBuffer:
+ {
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
+
+ queueBuffer(buffer);
+
+ if (mStreamSource == NULL)
+ {
+ if (mTotalBytesQueued > 0ll)
+ {
+ initPlayer();
+ }
+ else
+ {
+ ALOGI("Have %lld bytes queued...", mTotalBytesQueued);
+ }
+ }
+ else
+ {
+ mStreamSource->doSomeWork();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ }
+
+ void TunnelRenderer::initPlayer()
+ {
+ if (mBufferProducer == NULL)
+ {
+ mComposerClient = new SurfaceComposerClient;
+ CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
+
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain), &info);
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ mSurfaceControl =
+ mComposerClient->createSurface(
+ String8("A Surface"),
+ displayWidth,
+ displayHeight,
+ PIXEL_FORMAT_RGB_565,
+ 0);
+
+ CHECK(mSurfaceControl != NULL);
+ CHECK(mSurfaceControl->isValid());
+
+ //SurfaceComposerClient::openGlobalTransaction();
+ //CHECK_EQ(mSurfaceControl->setLayer(INT_MAX), (status_t)OK);
+ //CHECK_EQ(mSurfaceControl->show(), (status_t)OK);
+ //SurfaceComposerClient::closeGlobalTransaction();
+ //SurfaceComposerClient::Transaction t;
+ //t.setLayer(mSurfaceControl, INT_MAX).apply();
+ //CHECK_EQ(mSurfaceControl->setLayer(INT_MAX), (status_t)OK);
+ //CHECK_EQ(mSurfaceControl->show(), (status_t)OK);
+ //SurfaceControl::closeTransaction();
+
+ mSurface = mSurfaceControl->getSurface();
+ CHECK(mSurface != NULL);
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ CHECK(service.get() != NULL);
+
+ mStreamSource = new StreamSource(this);
+
+ mPlayerClient = new PlayerClient;
+
+ mPlayer = service->create( mPlayerClient/*, 0*/);
+ CHECK(mPlayer != NULL);
+ CHECK_EQ(mPlayer->setDataSource(sp<IStreamSource>(mStreamSource)), (status_t)OK);
+
+ setProperty("media.libplayer.wfd", "1");
+ setProperty("media.libplayer.fastswitch", "2");
+ if (mIsHDCP)
+ {
+ ALOGI("HDCP Enabled!!!");
+ Parcel data;
+ data.writeInt32(1);
+ mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_HDCP_CUSTOM_DATA, data);
+ } else {
+ ALOGI("HDCP is disabled!!!");
+ Parcel data;
+ data.writeInt32(0);
+ mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_HDCP_CUSTOM_DATA, data);
+ }
+// mPlayer->setVideoSurfaceTexture(
+ // mBufferProducer != NULL ? mBufferProducer : mSurface->getSurfaceTexture());
+ mPlayer->setVideoSurfaceTexture(mSurface->getIGraphicBufferProducer());
+ Parcel request;
+ //mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_DIS_AUTO_BUFFER, request);
+ mPlayer->prepareAsync();
+ request.writeString16(String16("freerun_mode:60"));
+ // mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_FREERUN_MODE, request);
+ while (!mPlayerClient->isPrepared())
+ usleep(1000);
+ mPlayer->start();
+ }
+
+ void TunnelRenderer::destroyPlayer()
+ {
+ Parcel request;
+ //mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_ENA_AUTO_BUFFER, request);
+ request.writeString16(String16("freerun_mode:0"));
+ //mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_FREERUN_MODE, request);
+ mPlayerClient.clear();
+ mStreamSource.clear();
+ if (mIsHDCP)
+ {
+ Parcel data;
+ data.writeInt32(0);
+ // mPlayer->setParameter(KEY_PARAMETER_AML_PLAYER_USE_SOFT_DEMUX, data);
+ }
+ setProperty("media.libplayer.wfd", "0");
+ setProperty("media.libplayer.fastswitch", "");
+ mPlayer->stop();
+ mPlayer.clear();
+
+ if (mBufferProducer == NULL)
+ {
+ mSurface.clear();
+ mSurfaceControl.clear();
+
+ mComposerClient->dispose();
+ mComposerClient.clear();
+ }
+ }
+
+ void TunnelRenderer::setIsHDCP(bool isHDCP)
+ {
+ mIsHDCP = isHDCP;
+ }
+} // namespace android
+
diff --git a/libstagefright/wifi-display/sink/TunnelRenderer.h b/libstagefright/wifi-display/sink/TunnelRenderer.h
new file mode 100644
index 0000000..feb982a
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/TunnelRenderer.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef TUNNEL_RENDERER_H_
+
+#define TUNNEL_RENDERER_H_
+
+#include <gui/Surface.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+//#include <ISystemControlService.h>
+#include <string>
+//#include "SystemControlClient.h"
+
+using namespace std;
+
+namespace android
+{
+
+ struct ABuffer;
+ class SurfaceComposerClient;
+ class SurfaceControl;
+ class Surface;
+ class IMediaPlayer;
+ struct IStreamListener;
+
+ // This class reassembles incoming RTP packets into the correct order
+ // and sends the resulting transport stream to a mediaplayer instance
+ // for playback.
+ struct TunnelRenderer : public AHandler
+ {
+ TunnelRenderer(
+ const sp<AMessage> &notifyLost,
+ const sp<IGraphicBufferProducer> &bufferProducer,
+ const sp<AMessage> &stopNotify);
+
+ sp<ABuffer> dequeueBuffer();
+
+ enum
+ {
+ kWhatQueueBuffer,
+ };
+
+ void setIsHDCP(bool isHDCP);
+ bool getIsHDCP() { return mIsHDCP; }
+ protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual ~TunnelRenderer();
+
+ private:
+ struct PlayerClient;
+ struct StreamSource;
+
+ mutable Mutex mLock;
+
+ sp<AMessage> mNotifyLost;
+ sp<IGraphicBufferProducer> mBufferProducer;
+
+ List<sp<ABuffer> > mPackets;
+ int64_t mTotalBytesQueued;
+ int64_t mMaxBytesQueued;
+ int64_t mMinBytesQueued;
+ int64_t mRetryTimes;
+ int64_t mBandwidth;
+ int64_t mCurTime;
+ int32_t mBytesQueued;
+ bool mDebugEnable;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mSurfaceControl;
+ sp<Surface> mSurface;
+ sp<PlayerClient> mPlayerClient;
+ sp<IMediaPlayer> mPlayer;
+ sp<StreamSource> mStreamSource;
+
+ int32_t mLastDequeuedExtSeqNo;
+ int64_t mFirstFailedAttemptUs;
+ int32_t mPackageSuccess;
+ int32_t mPackageFailed;
+ int32_t mPackageRequest;
+ bool mRequestedRetry;
+ bool mRequestedRetransmission;
+ sp<AMessage> mStopNotify;
+ //sp<SystemControlClient > mSystemControlService;
+
+ bool mIsHDCP;
+
+ void initPlayer();
+ void destroyPlayer();
+
+ void queueBuffer(const sp<ABuffer> &buffer);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer);
+ };
+
+} // namespace android
+
+#endif // TUNNEL_RENDERER_H_
diff --git a/libstagefright/wifi-display/sink/Utils.cpp b/libstagefright/wifi-display/sink/Utils.cpp
new file mode 100644
index 0000000..432cdb9
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/Utils.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 Amlogic, Inc. All rights reserved.
+ *
+ * This source code is subject to the terms and conditions defined in the
+ * file 'LICENSE' which is part of this source code package.
+ *
+ * Description: c++ file
+ */
+ #include "Utils.h"
+ #include <cutils/log.h>
+
+ int32_t getPropertyInt(const char *key, int32_t def) {
+ int len;
+ char* end;
+ char buf[92] = {0};
+ int32_t result = def;
+
+ len = property_get(key, buf, "");
+ if (len > 0) {
+ result = strtol(buf, &end, 0);
+ if (end == buf) {
+ result = def;
+ }
+ }
+ return result;
+ }
+
+ void setProperty(const char *key, const char *value) {
+ int err;
+ err = property_set(key, value);
+ if (err < 0) {
+ ALOGI("failed to set system property %s\n", key);
+ }
+}
diff --git a/libstagefright/wifi-display/sink/Utils.h b/libstagefright/wifi-display/sink/Utils.h
new file mode 100644
index 0000000..aafbb04
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/Utils.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014 Amlogic, Inc. All rights reserved.
+ *
+ * This source code is subject to the terms and conditions defined in the
+ * file 'LICENSE' which is part of this source code package.
+ *
+ * Description: header file
+ */
+#ifndef _UTILS_H_
+#define _UTILS_H_
+#include <stdlib.h>
+#include <cutils/properties.h>
+#include <stdint.h>
+
+ int32_t getPropertyInt(const char *key, int32_t def);
+ void setProperty(const char *key, const char *value);
+#endif
diff --git a/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
new file mode 100644
index 0000000..031feb1
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
@@ -0,0 +1,1435 @@
+/*
+ * 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 "media/stagefright/foundation/ParsedMessage.h"
+#include "RTPSink.h"
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.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 <cutils/properties.h>
+#include <utils/NativeHandle.h>
+#include <utils/misc.h>
+
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+#include <media/IMediaPlayerService.h>
+#else
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCPService.h>
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCP.h>
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCPObserver.h>
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/types.h>
+#include <media/hardware/HDCPAPI.h>
+#include <android/hidl/manager/1.0/IServiceNotification.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#endif
+
+#define CEA_HIGH_RESOLUTION "0001DEFF"
+#define CEA_NORMAL_RESOLUTION "00008C7F"
+#define VESA_HIGH_RESOLUTION "157C7FFF"
+#define VESA_NORMAL_RESOLUTION "00007FFF"
+#define HH_HIGH_RESOLUTION "00000FFF"
+#define HH_NORMAL_RESOLUTION "00000FFF"
+
+namespace android
+{
+ WifiDisplaySink::WifiDisplaySink(
+ const sp<AmANetworkSession> &netSession,
+ const sp<IGraphicBufferProducer> &bufferProducer)
+ : mState(UNDEFINED),
+ mNetSession(netSession),
+ mBufferProducer(bufferProducer),
+ mSessionID(0),
+ mNextCSeq(1),
+ mConnectionRetry(0),
+ mUsingHDCP(false),
+ mNeedHDCP(false),
+ mResolution(Normal),
+ mHDCPRunning(false)
+ {
+ srand(time(0));
+ mHDCPPort = 9000 + rand() % 100;
+ setResolution(High);
+ property_set("sys.wfd.state","0"); //0-init play,1-normal->pause,2-pause->play
+ }
+
+ WifiDisplaySink::~WifiDisplaySink()
+ {
+ }
+
+ void WifiDisplaySink::setSinkHandler(const sp<AHandler> &handler)
+ {
+ mSinkHandler = handler;
+ }
+
+ void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort)
+ {
+ prepareHDCP();
+ mConnectionRetry = 0;
+ sp<AMessage> msg = new AMessage(kWhatStart, this);
+ msg->setString("sourceHost", sourceHost);
+ msg->setInt32("sourcePort", sourcePort);
+ ALOGI("post msg kWhatStart.");
+ msg->post();
+ }
+
+ void WifiDisplaySink::start(const char *uri)
+ {
+ prepareHDCP();
+ mConnectionRetry = 0;
+ sp<AMessage> msg = new AMessage(kWhatStart, this);
+ msg->setString("setupURI", uri);
+ msg->post();
+ }
+
+ void WifiDisplaySink::retryStart(int32_t uDelay)
+ {
+ sp<AMessage> msg = new AMessage(kWhatStart, this);
+ msg->setString("sourceHost", mRTSPHost.c_str());
+ msg->setInt32("sourcePort", mRTSPPort);
+ ALOGI("post msg kWhatStart.");
+ msg->post(uDelay);
+
+ }
+
+ void WifiDisplaySink::stop(void)
+ {
+ sp<AMessage> msg = new AMessage(kWhatStop, this);
+ msg->post();
+ }
+
+ void WifiDisplaySink::setPlay(void)
+ {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ sendPlay(mSessionID, !mSetupURI.empty()? mSetupURI.c_str() : url.c_str());
+ }
+
+ void WifiDisplaySink::setPause(void)
+ {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ sendPause(mSessionID, !mSetupURI.empty()? mSetupURI.c_str() : url.c_str());
+ }
+
+ void WifiDisplaySink::setTeardown(void)
+ {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ sendTeardown(mSessionID, !mSetupURI.empty()? mSetupURI.c_str() : url.c_str());
+ }
+ // 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::setResolution(int resolution)
+ {
+ if (resolution == High)
+ {
+ mResolution = High;
+ mCEA = CEA_HIGH_RESOLUTION;
+ mVESA = VESA_HIGH_RESOLUTION;
+ mHH = HH_HIGH_RESOLUTION;
+ }
+ else if(resolution == Normal)
+ {
+ mResolution = Normal;
+ mCEA = CEA_NORMAL_RESOLUTION;
+ mVESA = VESA_NORMAL_RESOLUTION;
+ mHH = HH_NORMAL_RESOLUTION;
+ }
+ else
+ {
+ ALOGE("unsupported resolution.");
+ }
+ }
+ int WifiDisplaySink::getResolution()
+ {
+ return mResolution;
+ }
+
+ void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg)
+ {
+ switch (msg->what())
+ {
+ case kWhatStart:
+ {
+ ALOGI("Received msg kWhatStart.");
+ mNotifyStop = new AMessage(kWhatNoPacket, this);
+ if (msg->findString("setupURI", &mSetupURI))
+ {
+ AString path, user, pass;
+ CHECK(ParseURL(
+ mSetupURI.c_str(),
+ &mRTSPHost, &mRTSPPort, &path, &user, &pass)
+ && user.empty() && pass.empty());
+ }
+ else
+ {
+ CHECK(msg->findString("sourceHost", &mRTSPHost));
+ CHECK(msg->findInt32("sourcePort", &mRTSPPort));
+ }
+
+ sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this);
+
+ ALOGI("Create RTSPClient.");
+ status_t err = mNetSession->createRTSPClient(
+ mRTSPHost.c_str(), mRTSPPort, notify, &mSessionID);
+ if (err) //network not ready
+ {
+ sp<AMessage> msg = new AMessage(kWhatSinkNotify, mSinkHandler);
+ ALOGI("post msg kWhatSinkNotify - RTSP_ERROR x1");
+ msg->setString("reason", "RTSP_ERROR x1");
+ msg->post();
+ break;
+ }
+ CHECK_EQ(err, (status_t)OK);
+
+ mState = CONNECTING;
+ break;
+ }
+
+ case kWhatRTSPNotify:
+ {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ ALOGI("Received msg kWhatRTSPNotify.");
+
+ switch (reason)
+ {
+ case AmANetworkSession::kWhatError:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+ ALOGI("reason: kWhatError.");
+
+ 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.
+#if 0
+ mNetSession->destroySession(mSessionID);
+ mSessionID = 0;
+
+ if (mRTPSink != NULL)
+ {
+ looper()->unregisterHandler(mRTPSink->id());
+ mRTPSink.clear();
+ }
+#endif
+ ALOGI("Quiting WifiDisplaySink.");
+ if (err == -111) //"Connection refused"
+ {
+ if (mConnectionRetry++ < MAX_CONN_RETRY)
+ {
+ mNetSession->destroySession(mSessionID);
+ mSessionID = 0;
+ ALOGI("Retry rtsp connection %d", mConnectionRetry);
+ retryStart(100000ll); //delay 100ms
+ }
+ else
+ {
+ sp<AMessage> msg = new AMessage(kWhatSinkNotify, mSinkHandler);
+ ALOGI("Post msg kWhatSinkNotify - RTSP_ERROR x2");
+ msg->setString("reason", "RTSP_ERROR x2");
+ msg->post();
+ }
+ }
+ else if(err == -104) //connection reset by peer
+ {
+ sp<AMessage> msg = new AMessage(kWhatSinkNotify, mSinkHandler);
+ ALOGI("post msg kWhatSinkNotify - connection reset by peer");
+ msg->setString("reason", "RTSP_RESET");
+ msg->post();
+ }
+ //looper()->stop();
+ }
+ break;
+ }
+
+ case AmANetworkSession::kWhatConnected:
+ {
+ ALOGI("reason: kWhatConnected.");
+ ALOGI("We're now connected.");
+ mState = CONNECTED;
+
+ if (!mSetupURI.empty())
+ {
+ ALOGI("sendDescribe.[uri]");
+
+ status_t err =
+ sendDescribe(mSessionID, mSetupURI.c_str());
+
+ CHECK_EQ(err, (status_t)OK);
+ }
+
+ break;
+ }
+
+ case AmANetworkSession::kWhatData:
+ {
+ ALOGI("reason: kWhatData.");
+ onReceiveClientData(msg);
+ break;
+ }
+
+ case AmANetworkSession::kWhatBinaryData:
+ {
+ CHECK(sUseTCPInterleaving);
+
+ ALOGI("reason: kWhatBinaryData.");
+
+ int32_t channel;
+ CHECK(msg->findInt32("channel", &channel));
+
+ sp<ABuffer> data;
+ CHECK(msg->findBuffer("data", &data));
+
+ mRTPSink->injectPacket(channel == 0 /* isRTP */, data);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatHDCPNotify:
+ {
+ int32_t msgCode, ext1, ext2;
+ CHECK(msg->findInt32("msg", &msgCode));
+ CHECK(msg->findInt32("ext1", &ext1));
+ CHECK(msg->findInt32("ext2", &ext2));
+
+ ALOGI("Saw HDCP notification code %d, ext1 %d, ext2 %d",
+ msgCode, ext1, ext2);
+
+ switch (msgCode)
+ {
+#if 1
+ case HDCPModule::HDCP_INITIALIZATION_COMPLETE:
+ mHDCPRunning = true;
+ mRTPSink->setIsHDCP(true);
+ break;
+ case HDCPModule::HDCP_SHUTDOWN_COMPLETE:
+ case HDCPModule::HDCP_SHUTDOWN_FAILED:
+ mHDCP->setObserver(NULL);
+ mHDCPObserver.clear();
+ mHDCP.clear();
+ mHDCPRunning = false;
+ mHDCPLock.unlock();
+ break;
+ case HDCPModule::HDCP_SESSION_ESTABLISHED:
+ break;
+#endif
+#if 0
+ {
+ // Ugly hack to make sure that the call to
+ // HDCPObserver::notify is completely handled before
+ // we clear the HDCP instance and unload the shared
+ // library :(
+ (new AMessage(kWhatFinishStop2, this))->post(300000ll);
+ break;
+ }
+#endif
+ default:
+ {
+ ALOGE("HDCP failure, shutting down.");
+ // TODO: Is this error handling correct?
+ // If any error occured, we should send M8 to terminate RTSP.
+ sp<AMessage> msg = new AMessage(kWhatStop, this);
+ msg->post();
+ break;
+ }
+ }
+ break;
+ }
+
+ case kWhatStop:
+ {
+ ALOGI("Received msg stop.");
+ if (mSessionID != 0)
+ {
+ mNetSession->destroySession(mSessionID);
+ mSessionID = 0;
+ }
+ if (mRTPSink != NULL)
+ {
+ looper()->unregisterHandler(mRTPSink->id());
+ mRTPSink.clear();
+ }
+#if 1
+ if (mHDCP != NULL /*&& mHDCPRunning == true*/) {
+ mHDCPLock.lock();
+ mHDCPRunning = true;
+ ALOGI("kWhatStop: Initiating HDCP shutdown.");
+ mHDCP->shutdownAsync();
+ }
+#endif
+ //looper()->stop();
+
+ break;
+ }
+ case kWhatNoPacket:
+ {
+ sp<AMessage> msg = new AMessage(kWhatSinkNotify, mSinkHandler);
+ ALOGI("post msg kWhatSinkNotify - RTP no packets");
+ msg->setString("reason", "RTP_NO_PACKET");
+ msg->post();
+
+ 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");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+ status_t err =
+ mNetSession->sendRequest(sessionID, request.c_str(), request.size());
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceiveM2Response", mNextCSeq);
+
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+
+ status_t WifiDisplaySink::onReceivePauseResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg) {
+ int32_t statusCode;
+
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ if (!msg->getStatusCode(&statusCode)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (statusCode != 200) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ // sp<AMessage> msg1 = new AMessage(kWhatSinkNotify, mHandlerId);
+ //ALOGI("post msg kWhatSinkNotify - received msg pause Response");
+ // msg1->setString("reason", "RTSP_PAUSE");
+ // msg1->post();
+
+ // mState = UNDEFINED;
+ mState = PAUSED;
+
+ property_set("sys.wfd.state","1");
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::onReceiveM2Response(
+ int32_t sessionID, const sp<ParsedMessage> &msg)
+ {
+ int32_t statusCode;
+
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ if (!msg->getStatusCode(&statusCode))
+ {
+ return ERROR_MALFORMED;
+ }
+
+ if (statusCode != 200)
+ {
+ return ERROR_UNSUPPORTED;
+ }
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::onReceiveDescribeResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg)
+ {
+ int32_t statusCode;
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ if (!msg->getStatusCode(&statusCode))
+ {
+ return ERROR_MALFORMED;
+ }
+
+ if (statusCode != 200)
+ {
+ return ERROR_UNSUPPORTED;
+ }
+
+ return sendSetup(sessionID, mSetupURI.c_str());
+ }
+
+ status_t WifiDisplaySink::onReceiveSetupResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg)
+ {
+ int32_t statusCode;
+
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ 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;
+ }
+
+
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+
+ ALOGI("%s: send PLAY Request", __FUNCTION__);
+
+ return sendPlay(
+ sessionID,
+ !mSetupURI.empty()
+ ? mSetupURI.c_str() : url.c_str());
+ }
+
+ status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg)
+ {
+ if (sUseTCPInterleaving)
+ {
+ return OK;
+ }
+
+ AString transport;
+ if (!msg->findString("transport", &transport))
+ {
+ ALOGE("Missing 'transport' field in SETUP response.");
+ return ERROR_MALFORMED;
+ }
+
+ AString sourceHost;
+ if (!ParsedMessage::GetAttribute(
+ transport.c_str(), "source", &sourceHost))
+ {
+ sourceHost = mRTSPHost;
+ }
+
+ AString serverPortStr;
+ if (!ParsedMessage::GetAttribute(
+ transport.c_str(), "server_port", &serverPortStr))
+ {
+ ALOGE("Missing 'server_port' in Transport field.");
+ //serverPortStr.clear();
+ //serverPortStr.append("16660-16661");
+ //return ERROR_MALFORMED;
+ }
+
+ int rtpPort = 0, rtcpPort = 0;
+ int ret;
+ ret = sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort);
+ if (ret != 2
+ || rtpPort <= 0 || rtpPort > 65535
+ || rtcpPort <= 0 || rtcpPort > 65535
+ || rtcpPort != rtpPort + 1)
+ {
+ ALOGE("!!! FIXME: Invalid server_port description '%s'.",
+ serverPortStr.c_str());
+
+ ALOGE("ret=%d, rtpPort=%d, rtcpPort=%d",
+ ret, rtpPort, rtcpPort);
+
+ //return ERROR_MALFORMED;
+ }
+
+ if (rtpPort & 1)
+ {
+ ALOGW("Server picked an odd numbered RTP port.");
+ }
+
+ //return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
+ return OK;
+ }
+
+ status_t WifiDisplaySink::onReceivePlayResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg)
+ {
+ int32_t statusCode;
+
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ if (!msg->getStatusCode(&statusCode))
+ {
+ return ERROR_MALFORMED;
+ }
+
+ if (statusCode != 200)
+ {
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (mState == PAUSED)
+ property_set("sys.wfd.state","2");
+ else
+ property_set("sys.wfd.state","0");
+
+ mState = PLAYING;
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::onReceiveTeardownResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg)
+ {
+ int32_t statusCode;
+
+ ALOGI("%s:%d", __FUNCTION__, sessionID);
+
+ if (!msg->getStatusCode(&statusCode))
+ {
+ return ERROR_MALFORMED;
+ }
+
+ if (statusCode != 200)
+ {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<AMessage> msg1 = new AMessage(kWhatSinkNotify, mSinkHandler);
+ ALOGI("post msg kWhatSinkNotify - received msg teardown Response");
+ msg1->setString("reason", "RTSP_TEARDOWN");
+ msg1->post();
+
+ mState = UNDEFINED;
+
+ 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());
+
+ AString method;
+ AString uri;
+ data->getRequestField(0, &method);
+
+ int32_t cseq;
+ if (!data->findInt32("cseq", &cseq))
+ {
+ ALOGI("\nReceived invalid packet:\n%s",
+ data->debugString().c_str());
+ sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
+ return;
+ }
+
+ if (method.startsWith("RTSP/"))
+ {
+ ALOGI("\nReceived Response:\n%s",
+ data->debugString().c_str());
+ // 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;
+ AString content(data->debugString().c_str());
+ ssize_t pos = -1;
+
+ ALOGI("\nReceived Request:\n%s",
+ data->debugString().c_str());
+
+ 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")
+ {
+ pos = content.find("wfd_content_protection", 0);
+ mNeedHDCP = ((pos != -1) ? true : false);
+ ALOGI("\nmNeedHDCP:%d\n",mNeedHDCP);
+ /*add by yalong.liu*/
+ pos = content.find("wfd_audio_codecs", 0);
+ mNeedAudioCodecs = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_video_formats", 0);
+ mNeedVideoFormats = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_3d_video_formats", 0);
+ mNeed3dVideoFormats = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_display_edid", 0);
+ mNeedDisplayEdid = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_coupled_sink", 0);
+ mNeedCoupledSink = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_client_rtp_ports", 0);
+ mNeedclientRtpPorts = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_standby_resume_capability", 0);
+ mNeedStandbyResumeCapability = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_uibc_capability", 0);
+ mNeedUibcCapability = ((pos != -1) ? true : false);
+
+ pos = content.find("wfd_connector_type", 0);
+ mNeedConnectorType = ((pos != -1) ? true :false);
+
+ pos = content.find("wfd_I2C", 0);
+ mNeedI2C = ((pos != -1) ? true :false);
+
+ 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)
+ {
+ ALOGV("session %d received '%s'",
+ sessionID, data->debugString().c_str());
+ //save_sessionid_to_file("/data/data/com.amlogic.miracast/sessionId", sessionID);
+ 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");
+
+ ALOGI("\nSend Response:\n%s", response.c_str());
+ status_t err = mNetSession->sendRequest(sessionID, response.c_str());
+ CHECK_EQ(err, (status_t)OK);
+
+ err = sendM2(sessionID);
+ CHECK_EQ(err, (status_t)OK);
+ }
+
+ int WifiDisplaySink::save_sessionid_to_file(char* filepath, int32_t sessionID)
+ {
+ FILE *fpo = NULL;
+
+ fpo = fopen(filepath, "w+");
+ if (fpo == NULL ) {
+ ALOGI("failed to open output file %s", filepath);
+ return -1;
+ }
+
+ //fwrite(&sessionID, sizeof(int32_t), 1, fpo);
+ fprintf(fpo, "%d", sessionID);
+
+ fclose(fpo);
+
+ return 0;
+ }
+ void WifiDisplaySink::prepareHDCP()
+ {
+#if 1
+ char prop[PROPERTY_VALUE_MAX];
+ int ret = property_get("persist.miracast.hdcp2", prop, "false");
+
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ status_t err;
+#else
+ Status err;
+#endif
+ ALOGI("prepareHDCP==>");
+ while (1)
+ {
+ if (!mHDCPRunning)
+ {
+ ALOGI("prepareHDCP1==>");
+ mHDCPLock.lock();
+ break;
+ }
+ else
+ {
+ ALOGI("prepareHDCP2==>");
+ sleep(2);
+ }
+ }
+ ALOGI("prepareHDCP lock");
+ if (!strcmp(prop, "true"))
+ {
+ if (mHDCP == 0)
+ {
+ {
+ mHDCPPort++;
+ err = makeHDCP();
+ mUsingHDCP = true;
+ mHDCPRunning = false;
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ if (err != OK)
+ {
+ ALOGE("Unable to instantiate HDCP component. "
+ "Not using HDCP after all.");
+ mUsingHDCP = false;
+ }
+#else
+ if (err != Status::OK)
+ {
+ ALOGE("Unable to instantiate HDCP component. "
+ "Not using HDCP after all.");
+ mUsingHDCP = false;
+ }
+#endif
+ }
+ }
+ else
+ {
+ mUsingHDCP = false;
+ }
+ }
+ mHDCPLock.unlock();
+#endif
+ }
+
+ status_t WifiDisplaySink::sendPause(int32_t sessionID, const char *uri)
+ {
+ AString request = AStringPrintf("PAUSE %s RTSP/1.0\r\n", uri);
+ AppendCommonResponse(&request, mNextCSeq);
+ request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
+ request.append("\r\n");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+
+ status_t err = mNetSession->sendRequest(
+ sessionID, request.c_str(), request.size());
+
+ if (err != OK) {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceivePauseResponse", mNextCSeq);
+
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceivePauseResponse);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+ void WifiDisplaySink::onGetParameterRequest(
+ int32_t sessionID,
+ int32_t cseq,
+ const sp<ParsedMessage> &data)
+ {
+ ALOGV("session %d received '%s'",
+ sessionID, data->debugString().c_str());
+ status_t err;
+
+ if (mRTPSink == NULL)
+ {
+ mRTPSink = new RTPSink(mNetSession, mBufferProducer, mNotifyStop);
+ looper()->registerHandler(mRTPSink);
+
+ err = mRTPSink->init(sUseTCPInterleaving);
+
+ if (err != OK)
+ {
+ looper()->unregisterHandler(mRTPSink->id());
+ mRTPSink.clear();
+ return;
+ }
+ }
+
+ //ALOGI("!!! FIXME: HARD CODE with video_formats, audio_codecs and client_rtp_ports");
+
+ char body[1024];
+
+ /*modified by yalong.liu*/
+ memset(body, 0, 1024);
+ if (mNeedVideoFormats)
+ snprintf(body, sizeof(body),
+ "wfd_video_formats: 28 00 02 02 %s %s %s 00 0000 0000 11 none none, 01 02 %s %s %s 00 0000 0000 11 none none\r\n",
+ mCEA.c_str(),
+ mVESA.c_str(),
+ mHH.c_str(),
+ mCEA.c_str(),
+ mVESA.c_str(),
+ mHH.c_str());
+
+ if (mNeedAudioCodecs)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body), "wfd_audio_codecs: LPCM 00000003 00\r\n");
+
+ if (mNeed3dVideoFormats)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body), "wfd_3d_video_formats: none\r\n");
+
+ if (mUsingHDCP && mNeedHDCP)
+ {
+ snprintf(body + strlen(body), sizeof(body) - strlen(body),
+ "wfd_content_protection: HDCP2.1 port=%d\r\n",
+ mHDCPPort);
+ }
+ else if (mNeedHDCP)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body), "wfd_content_protection: none\r\n");
+
+ if (mNeedDisplayEdid)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body), "wfd_display_edid: none\r\n");
+
+ if (mNeedCoupledSink)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body), "wfd_coupled_sink: none\r\n");
+
+ if (mNeedclientRtpPorts)
+ snprintf(body + strlen(body), sizeof(body) - strlen(body),
+ "wfd_client_rtp_ports: RTP/AVP/UDP;unicast %d 0 mode=play\r\n",
+ mRTPSink->getRTPPort());
+
+ if (mNeedUibcCapability)
+ snprintf(body+ strlen(body), sizeof(body) - strlen(body),
+ "wfd_uibc_capability: none\r\n");
+
+ if (mNeedStandbyResumeCapability)
+ snprintf(body+ strlen(body), sizeof(body) - strlen(body), "wfd_standby_resume_capability: none\r\n");
+
+ if (mNeedConnectorType)
+ snprintf(body+ strlen(body), sizeof(body) - strlen(body),
+ "wfd_connector_type: 00 00\r\n");
+
+ if (mNeedI2C)
+ snprintf(body+ strlen(body), sizeof(body) - strlen(body),
+ "wfd_I2C: none\r\n");
+
+ AString response = "RTSP/1.0 200 OK\r\n";
+ AppendCommonResponse(&response, cseq);
+ response.append("Content-Type: text/parameters\r\n");
+ response.append(AStringPrintf("Content-Length: %d\r\n", strlen(body)));
+ response.append("\r\n");
+ response.append(body);
+
+ ALOGI("\nSend Response:\n%s", response.c_str());
+ err = mNetSession->sendRequest(sessionID, response.c_str());
+ CHECK_EQ(err, (status_t)OK);
+ }
+
+ status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri)
+ {
+ AString request = AStringPrintf("DESCRIBE %s RTSP/1.0\r\n", uri);
+ AppendCommonResponse(&request, mNextCSeq);
+
+ request.append("Accept: application/sdp\r\n");
+ request.append("\r\n");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+
+ status_t err = mNetSession->sendRequest(
+ sessionID, request.c_str(), request.size());
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceiveDescribeResponse", mNextCSeq);
+
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceiveDescribeResponse);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri)
+ {
+ status_t err;
+
+ if (mRTPSink == NULL)
+ {
+ mRTPSink = new RTPSink(mNetSession, mBufferProducer, mNotifyStop);
+ looper()->registerHandler(mRTPSink);
+
+ err = mRTPSink->init(sUseTCPInterleaving);
+
+ if (err != OK)
+ {
+ looper()->unregisterHandler(mRTPSink->id());
+ mRTPSink.clear();
+ return err;
+ }
+ }
+
+ AString request = AStringPrintf("SETUP %s RTSP/1.0\r\n", uri);
+
+ AppendCommonResponse(&request, mNextCSeq);
+
+ if (sUseTCPInterleaving)
+ {
+ request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
+ }
+ else
+ {
+ int32_t rtpPort = mRTPSink->getRTPPort();
+
+ request.append(
+ AStringPrintf(
+ "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
+ rtpPort, rtpPort + 1));
+ }
+
+ request.append("\r\n");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+
+ err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceiveSetupResponse", mNextCSeq);
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri)
+ {
+ AString request = AStringPrintf("PLAY %s RTSP/1.0\r\n", uri);
+
+ AppendCommonResponse(&request, mNextCSeq);
+
+ request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
+ request.append("\r\n");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+
+ status_t err =
+ mNetSession->sendRequest(sessionID, request.c_str(), request.size());
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceivePlayResponse", mNextCSeq);
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+ status_t WifiDisplaySink::sendTeardown(int32_t sessionID, const char *uri)
+ {
+ AString request = AStringPrintf("TEARDOWN %s RTSP/1.0\r\n", uri);
+ AppendCommonResponse(&request, mNextCSeq);
+ request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
+ request.append("\r\n");
+
+ ALOGI("\nSend Request:\n%s", request.c_str());
+
+ status_t err = mNetSession->sendRequest(
+ sessionID, request.c_str(), request.size());
+
+ if (err != OK)
+ {
+ return err;
+ }
+
+ ALOGI("registerResponseHandler:id = %d, onReceiveTeardownResponse", mNextCSeq);
+
+ registerResponseHandler(
+ sessionID, mNextCSeq, &WifiDisplaySink::onReceiveTeardownResponse);
+
+ ++mNextCSeq;
+
+ return OK;
+ }
+
+ void WifiDisplaySink::onSetParameterRequest(
+ int32_t sessionID,
+ int32_t cseq,
+ const sp<ParsedMessage> &data)
+ {
+ const char *content = data->getContent();
+
+ AString response = "RTSP/1.0 200 OK\r\n";
+ AppendCommonResponse(&response, cseq);
+ response.append("\r\n");
+
+ ALOGI("\nSend Response:\n%s", response.c_str());
+ status_t err = mNetSession->sendRequest(sessionID, response.c_str());
+ CHECK_EQ(err, (status_t)OK);
+
+ if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ status_t err = sendSetup( sessionID, url.c_str());
+
+ CHECK_EQ(err, (status_t)OK);
+ }
+ else if (strstr(content, "wfd_trigger_method: TEARDOWN\r\n") != NULL)
+ {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ status_t err = sendTeardown(sessionID, url.c_str());
+ CHECK_EQ(err, (status_t)OK);
+ } else if (strstr(content, "wfd_trigger_method: PAUSE\r\n") != NULL) {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ status_t err = sendPause(sessionID, url.c_str());
+ CHECK_EQ(err, (status_t)OK);
+ } else if (strstr(content, "wfd_trigger_method: PLAY\r\n") != NULL) {
+ AString url = AStringPrintf("rtsp://%s/wfd1.0/streamid=0", mRTSPHost.c_str());
+ status_t err = sendPlay(sessionID, url.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");
+
+ ALOGI("\nSend Response:\n%s", response.c_str());
+ 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("User-Agent: stagefright/1.1 (Linux;Android 9.0)\r\n");
+
+ if (cseq >= 0)
+ {
+ response->append(AStringPrintf("CSeq: %d\r\n", cseq));
+ }
+ }
+#if 1
+ WifiDisplaySink::HDCPObserver::HDCPObserver(
+ const sp<AMessage> &notify)
+ : mNotify(notify)
+ {
+ }
+
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ void WifiDisplaySink::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();
+ return;
+ }
+
+ status_t WifiDisplaySink::makeHDCP()
+ {
+ ALOGE("[%s %d]\n", __FUNCTION__, __LINE__);
+ if (mHDCP != NULL) {
+ return OK;
+ }
+ 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(false /* createEncryptionModule */);
+ if (mHDCP == NULL) {
+ ALOGE("makeHDCP failed");
+ return ERROR_UNSUPPORTED;
+ }
+ ALOGE("[HDCP2.X Rx] makeHDCP, mHDCP is not NULL");
+ sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this);
+ mHDCPObserver = new HDCPObserver(notify);
+
+ status_t err = mHDCP->setObserver(mHDCPObserver);
+
+ if (err != (status_t)OK) {
+ ALOGE("Failed to set HDCP observer.");
+
+ mHDCPObserver.clear();
+ mHDCP.clear();
+
+ return err;
+ }
+
+ err = mHDCP->initAsync(mRTSPHost.c_str(), mHDCPPort);
+
+ if (err != (status_t)OK)
+ {
+ return err;
+ }
+
+ return OK;
+ }
+#else
+ Return<void> WifiDisplaySink::HDCPObserver::notify(
+ int msg, int ext1, int ext2)
+ {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("msg", msg);
+ notify->setInt32("ext1", ext1);
+ notify->setInt32("ext2", ext2);
+ notify->post();
+ return Void();
+ }
+
+ Status WifiDisplaySink::makeHDCP()
+ {
+ ALOGE("[%s %d]\n", __FUNCTION__, __LINE__);
+ sp<IHDCPService> hdcp_service = IHDCPService::getService();
+ if (!hdcp_service) {
+ ALOGE("failed to get hdcp2 service\n");
+ return Status::NO_INIT;
+ }
+
+ mHDCP = IHDCP::castFrom(hdcp_service->makeHDCP(false));
+
+ if (mHDCP == NULL)
+ {
+ ALOGE("failed to cast from hdcp2 service\n");
+ return Status::NO_INIT;
+ }
+
+ sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this);
+ mHDCPObserver = new HDCPObserver(notify);
+
+ Status err = mHDCP->setObserver(mHDCPObserver);
+
+ if (err != Status::OK)
+ {
+ ALOGE("Failed to set HDCP observer.");
+
+ mHDCPObserver.clear();
+ mHDCP.clear();
+
+ return err;
+ }
+
+ err = mHDCP->initAsync(mRTSPHost.c_str(), mHDCPPort);
+
+ if (err != Status::OK)
+ {
+ return err;
+ }
+
+ return Status::OK;
+ }
+#endif
+#endif
+} // namespace android
diff --git a/libstagefright/wifi-display/sink/WifiDisplaySink.h b/libstagefright/wifi-display/sink/WifiDisplaySink.h
new file mode 100644
index 0000000..ab25a0c
--- a/dev/null
+++ b/libstagefright/wifi-display/sink/WifiDisplaySink.h
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFI_DISPLAY_SINK_H_
+
+#define WIFI_DISPLAY_SINK_H_
+
+#include "AmANetworkSession.h"
+
+#include <gui/Surface.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+#include <media/IHDCP.h>
+#else
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCPService.h>
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCP.h>
+#include <vendor/amlogic/hardware/miracast_hdcp2/1.0/IHDCPObserver.h>
+using namespace vendor::amlogic::hardware::miracast_hdcp2::V1_0;
+#endif
+namespace android
+{
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ struct HDCPObserver;
+#else
+ using vendor::amlogic::hardware::miracast_hdcp2::V1_0::IHDCP;
+ using vendor::amlogic::hardware::miracast_hdcp2::V1_0::Status;
+ using ::android::hardware::Void;
+ using ::android::hardware::Return;
+ struct HDCPObserver;
+#endif
+ //struct IHDCP;
+ struct ParsedMessage;
+ struct RTPSink;
+
+ // Represents the RTSP client acting as a wifi display sink.
+ // Connects to a wifi display source and renders the incoming
+ // transport stream using a MediaPlayer instance.
+ struct WifiDisplaySink : public AHandler
+ {
+ WifiDisplaySink(
+ const sp<AmANetworkSession> &netSession,
+ const sp<IGraphicBufferProducer> &bufferProducer = NULL);
+ enum
+ {
+ High,
+ Normal
+ };
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ struct 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);
+ };
+#else
+ struct HDCPObserver : public IHDCPObserver
+ {
+ HDCPObserver(const sp<AMessage> &notify);
+ virtual Return<void> notify(
+ int msg, int ext1, int ext2) override;
+
+ private:
+ sp<AMessage> mNotify;
+ DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
+ };
+#endif
+ void start(const char *sourceHost, int32_t sourcePort);
+ void start(const char *uri);
+ void retryStart(int32_t uDelay);
+ void stop(void);
+ void setPlay(void);
+ void setPause(void);
+ void setTeardown(void);
+ void setSinkHandler(const sp<AHandler> &handler);
+ void setResolution(int resolution);
+ int getResolution();
+ protected:
+ virtual ~WifiDisplaySink();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+ private:
+ //struct HDCPObserver;
+
+ enum State
+ {
+ UNDEFINED,
+ CONNECTING,
+ CONNECTED,
+ PAUSED,
+ PLAYING,
+ };
+
+ enum {
+ kWhatNoPacketMsg,
+ kWahtLostPacketMsg,
+ };
+
+ enum
+ {
+ kWhatStart,
+ kWhatRTSPNotify,
+ kWhatStop,
+ kWhatHDCPNotify,
+ kWhatNoPacket,
+ };
+
+ enum
+ {
+ kWhatSinkNotify,
+ };
+
+ struct ResponseID
+ {
+ int32_t mSessionID;
+ int32_t mCSeq;
+
+ bool operator<(const ResponseID &other) const
+ {
+ return mSessionID < other.mSessionID
+ || (mSessionID == other.mSessionID
+ && mCSeq < other.mCSeq);
+ }
+ };
+
+ typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ static const bool sUseTCPInterleaving = false;
+
+ State mState;
+ sp<AmANetworkSession> mNetSession;
+ sp<IGraphicBufferProducer> mBufferProducer;
+ AString mSetupURI;
+ AString mRTSPHost;
+ int32_t mSessionID;
+
+ int32_t mNextCSeq;
+
+ KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
+
+ sp<RTPSink> mRTPSink;
+ AString mPlaybackSessionID;
+ int32_t mPlaybackSessionTimeoutSecs;
+ sp<AHandler> mSinkHandler;
+ sp<AMessage> mNotifyStop;
+ int32_t mRTSPPort;
+ int32_t mConnectionRetry;
+ #define MAX_CONN_RETRY 500
+
+ /*add by yalong.liu*/
+ bool mNeedAudioCodecs;
+ bool mNeedVideoFormats;
+ bool mNeed3dVideoFormats;
+ bool mNeedDisplayEdid;
+ bool mNeedCoupledSink;
+ bool mNeedclientRtpPorts;
+ bool mNeedStandbyResumeCapability;
+ bool mNeedUibcCapability;
+ bool mNeedConnectorType;
+ bool mNeedI2C;
+
+ // HDCP specific section >>>>
+ bool mUsingHDCP;
+ bool mNeedHDCP;
+ int32_t mHDCPPort;
+ int mResolution;
+ AString mCEA;
+ AString mVESA;
+ AString mHH;
+ sp<IHDCP> mHDCP;
+ sp<HDCPObserver> mHDCPObserver;
+#if ANDROID_PLATFORM_SDK_VERSION <= 27
+ status_t makeHDCP();
+#else
+ Status makeHDCP();
+#endif
+ Mutex mHDCPLock;
+ bool mHDCPRunning;
+ void prepareHDCP();
+ // <<<< HDCP specific section
+
+ status_t sendM2(int32_t sessionID);
+ status_t sendDescribe(int32_t sessionID, const char *uri);
+ status_t sendSetup(int32_t sessionID, const char *uri);
+ status_t sendPlay(int32_t sessionID, const char *uri);
+ status_t sendPause(int32_t sessionID, const char *uri);
+ status_t sendTeardown(int32_t sessionID, const char *uri);
+
+ status_t onReceiveM2Response(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ status_t onReceiveDescribeResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ status_t onReceiveSetupResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ status_t configureTransport(const sp<ParsedMessage> &msg);
+
+ status_t onReceivePlayResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ status_t onReceivePauseResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ status_t onReceiveTeardownResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ void registerResponseHandler(
+ int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
+
+ void onReceiveClientData(const sp<AMessage> &msg);
+
+ void onOptionsRequest(
+ int32_t sessionID,
+ int32_t cseq,
+ const sp<ParsedMessage> &data);
+
+ void onGetParameterRequest(
+ int32_t sessionID,
+ int32_t cseq,
+ const sp<ParsedMessage> &data);
+
+ void onSetParameterRequest(
+ int32_t sessionID,
+ int32_t cseq,
+ const sp<ParsedMessage> &data);
+
+ void sendErrorResponse(
+ int32_t sessionID,
+ const char *errorDetail,
+ int32_t cseq);
+
+ static void AppendCommonResponse(AString *response, int32_t cseq);
+
+ bool ParseURL(
+ const char *url, AString *host, int32_t *port, AString *path,
+ AString *user, AString *pass);
+
+ int save_sessionid_to_file(char* filepath, int32_t sessionID);
+
+ status_t sendIDRFrameRequest(int32_t sessionID);
+ status_t onReceiveIDRFrameRequestResponse(
+ int32_t sessionID, const sp<ParsedMessage> &msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
+ };
+
+} // namespace android
+
+#endif // WIFI_DISPLAY_SINK_H_
diff --git a/mediaextconfig/include/media/ammediaplayerext.h b/mediaextconfig/include/media/ammediaplayerext.h
index 09524b9..6607ab0 100644
--- a/mediaextconfig/include/media/ammediaplayerext.h
+++ b/mediaextconfig/include/media/ammediaplayerext.h
@@ -88,6 +88,7 @@ namespace android {
KEY_PARAMETER_AML_PLAYER_ENA_AUTO_BUFFER = 8005, // play ASAP...
KEY_PARAMETER_AML_PLAYER_USE_SOFT_DEMUX = 8006, // play use soft demux
KEY_PARAMETER_AML_PLAYER_PR_CUSTOM_DATA = 9001, // string, playready, set only
+ KEY_PARAMETER_AML_PLAYER_HDCP_CUSTOM_DATA = 9002, // int, mircast hdcp enable or not
};
enum video_out_type {
VIDEO_OUT_SOFT_RENDER = 0,