author | shipeng.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) |
commit | 219a12a98ff06b177a9532e4e906604b3861fcc0 (patch) | |
tree | 9b8ca07c0227444e2e034460e1319ff98ae9d400 | |
parent | 625b4c85596d21bcb0f6625467dba2826ad06007 (diff) | |
download | av-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>
-rw-r--r-- | libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | libstagefright/wifi-display/Android.mk | 53 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/AmANetworkSession.cpp | 1435 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/AmANetworkSession.h | 140 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/LinearRegression.cpp | 121 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/LinearRegression.h | 55 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/RTPSink.cpp | 999 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/RTPSink.h | 109 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/TunnelRenderer.cpp | 618 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/TunnelRenderer.h | 111 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/Utils.cpp | 34 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/Utils.h | 17 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/WifiDisplaySink.cpp | 1435 | ||||
-rw-r--r-- | libstagefright/wifi-display/sink/WifiDisplaySink.h | 270 | ||||
-rw-r--r-- | mediaextconfig/include/media/ammediaplayerext.h | 1 |
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> ¬ify); + + 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> ¬ify) + : 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> ¬ify, + 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> ¬ify, int32_t *sessionID) { + return createClientOrServer( + kModeCreateRTSPServer, + &addr, + port, + NULL /* remoteHost */, + 0 /* remotePort */, + notify, + sessionID); +} + +status_t AmANetworkSession::createUDPSession( + unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID) { + return createUDPSession(localPort, NULL, 0, notify, sessionID); +} + +status_t AmANetworkSession::createUDPSession( + unsigned localPort, + const char *remoteHost, + unsigned remotePort, + const sp<AMessage> ¬ify, + 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> ¬ify, 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> ¬ify, + 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> ¬ify, + 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> ¬ify, + int32_t *sessionID); + + status_t createRTSPServer( + const struct in_addr &addr, unsigned port, + const sp<AMessage> ¬ify, int32_t *sessionID); + + status_t createUDPSession( + unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID); + + status_t createUDPSession( + unsigned localPort, + const char *remoteHost, + unsigned remotePort, + const sp<AMessage> ¬ify, + 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> ¬ify, int32_t *sessionID); + + // active + status_t createTCPDatagramSession( + unsigned localPort, + const char *remoteHost, + unsigned remotePort, + const sp<AMessage> ¬ify, + 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> ¬ify, + 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> ¬ifyLost, + 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> ¬ifyLost, + 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> ¬ify) + : 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> ¬ify); + 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> ¬ify); + 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, |