diff options
Diffstat (limited to 'media/libstagefright/wifi-display')
20 files changed, 647 insertions, 2161 deletions
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp deleted file mode 100644 index 938d601..0000000 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ /dev/null @@ -1,1255 +0,0 @@ -/* - * 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 "NetworkSession" -#include <utils/Log.h> - -#include "ANetworkSession.h" -#include "ParsedMessage.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/Utils.h> - -namespace android { - -static const size_t kMaxUDPSize = 1500; -static const int32_t kMaxUDPRetries = 200; - -struct ANetworkSession::NetworkThread : public Thread { - NetworkThread(ANetworkSession *session); - -protected: - virtual ~NetworkThread(); - -private: - ANetworkSession *mSession; - - virtual bool threadLoop(); - - DISALLOW_EVIL_CONSTRUCTORS(NetworkThread); -}; - -struct ANetworkSession::Session : public RefBase { - enum State { - CONNECTING, - CONNECTED, - LISTENING_RTSP, - LISTENING_TCP_DGRAMS, - DATAGRAM, - }; - - Session(int32_t sessionID, - State state, - int s, - 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 setIsRTSPConnection(bool yesno); - -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 mIsRTSPConnection; - 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); -}; -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session) - : mSession(session) { -} - -ANetworkSession::NetworkThread::~NetworkThread() { -} - -bool ANetworkSession::NetworkThread::threadLoop() { - mSession->threadLoop(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::Session::Session( - int32_t sessionID, - State state, - int s, - const sp<AMessage> ¬ify) - : mSessionID(sessionID), - mState(state), - mIsRTSPConnection(false), - 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 = StringPrintf( - "%d.%d.%d.%d", - (addr >> 24), - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - addr & 0xff); - - addr = ntohl(remoteAddr.sin_addr.s_addr); - AString remoteAddrString = StringPrintf( - "%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(); - } -} - -ANetworkSession::Session::~Session() { - ALOGV("Session %d gone", mSessionID); - - close(mSocket); - mSocket = -1; -} - -int32_t ANetworkSession::Session::sessionID() const { - return mSessionID; -} - -int ANetworkSession::Session::socket() const { - return mSocket; -} - -void ANetworkSession::Session::setIsRTSPConnection(bool yesno) { - mIsRTSPConnection = yesno; -} - -sp<AMessage> ANetworkSession::Session::getNotificationMessage() const { - return mNotify; -} - -bool ANetworkSession::Session::isRTSPServer() const { - return mState == LISTENING_RTSP; -} - -bool ANetworkSession::Session::isTCPDatagramServer() const { - return mState == LISTENING_TCP_DGRAMS; -} - -bool ANetworkSession::Session::wantsToRead() { - return !mSawReceiveFailure && mState != CONNECTING; -} - -bool ANetworkSession::Session::wantsToWrite() { - return !mSawSendFailure - && (mState == CONNECTING - || (mState == CONNECTED && !mOutFragments.empty()) - || (mState == DATAGRAM && !mOutFragments.empty())); -} - -status_t ANetworkSession::Session::readMore() { - if (mState == 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 (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", - StringPrintf( - "%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 (!mIsRTSPConnection) { - // 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 { - 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; - } - } - } - - if (err != OK) { - notifyError(false /* send */, err, "Recv failed."); - mSawReceiveFailure = true; - } - - return err; -} - -void ANetworkSession::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 ANetworkSession::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("%d 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; - 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 ANetworkSession::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 && !mIsRTSPConnection) { - 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 { - 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 ANetworkSession::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 ANetworkSession::Session::notify(NotificationReason reason) { - sp<AMessage> msg = mNotify->dup(); - msg->setInt32("sessionID", mSessionID); - msg->setInt32("reason", reason); - msg->post(); -} - -//////////////////////////////////////////////////////////////////////////////// - -ANetworkSession::ANetworkSession() - : mNextSessionID(1) { - mPipeFd[0] = mPipeFd[1] = -1; -} - -ANetworkSession::~ANetworkSession() { - stop(); -} - -status_t ANetworkSession::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("ANetworkSession", 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 ANetworkSession::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 ANetworkSession::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 ANetworkSession::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 ANetworkSession::createUDPSession( - unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID) { - return createUDPSession(localPort, NULL, 0, notify, sessionID); -} - -status_t ANetworkSession::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 ANetworkSession::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 ANetworkSession::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 ANetworkSession::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 ANetworkSession::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; -} - -status_t ANetworkSession::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; - } - - session = new Session( - mNextSessionID++, - state, - s, - notify); - - if (mode == kModeCreateTCPDatagramSessionActive) { - session->setIsRTSPConnection(false); - } else if (mode == kModeCreateRTSPClient) { - session->setIsRTSPConnection(true); - } - - mSessions.add(session->sessionID(), session); - - interrupt(); - - *sessionID = session->sessionID(); - - goto bail; - -bail2: - close(s); - s = -1; - -bail: - return err; -} - -status_t ANetworkSession::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 ANetworkSession::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; -} - -void ANetworkSession::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 ANetworkSession::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;) { - 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, - session->getNotificationMessage()); - - clientSession->setIsRTSPConnection( - session->isRTSPServer()); - - 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/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h deleted file mode 100644 index 7c62b29..0000000 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 A_NETWORK_SESSION_H_ - -#define A_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 ANetworkSession : public RefBase { - ANetworkSession(); - - 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); - - enum NotificationReason { - kWhatError, - kWhatConnected, - kWhatClientConnected, - kWhatData, - kWhatDatagram, - kWhatBinaryData, - kWhatNetworkStall, - }; - -protected: - virtual ~ANetworkSession(); - -private: - struct NetworkThread; - struct Session; - - Mutex mLock; - sp<Thread> mThread; - - int32_t mNextSessionID; - - int mPipeFd[2]; - - 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(ANetworkSession); -}; - -} // namespace android - -#endif // A_NETWORK_SESSION_H_ diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 061ae89..f70454a 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -3,10 +3,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - ANetworkSession.cpp \ MediaSender.cpp \ Parameters.cpp \ - ParsedMessage.cpp \ rtp/RTPSender.cpp \ source/Converter.cpp \ source/MediaPuller.cpp \ @@ -37,26 +35,3 @@ LOCAL_MODULE:= libstagefright_wfd LOCAL_MODULE_TAGS:= optional include $(BUILD_SHARED_LIBRARY) - -################################################################################ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - wfd.cpp \ - -LOCAL_SHARED_LIBRARIES:= \ - libbinder \ - libgui \ - libmedia \ - libstagefright \ - libstagefright_foundation \ - libstagefright_wfd \ - libutils \ - liblog \ - -LOCAL_MODULE:= wfd - -LOCAL_MODULE_TAGS := debug - -include $(BUILD_EXECUTABLE) diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp index 8a3566f..b1cdec0 100644 --- a/media/libstagefright/wifi-display/MediaSender.cpp +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -20,16 +20,18 @@ #include "MediaSender.h" -#include "ANetworkSession.h" #include "rtp/RTPSender.h" #include "source/TSPacketizer.h" #include "include/avc_utils.h" #include <media/IHDCP.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ANetworkSession.h> +#include <ui/GraphicBuffer.h> namespace android { @@ -341,6 +343,22 @@ void MediaSender::onSenderNotify(const sp<AMessage> &msg) { break; } + case kWhatInformSender: + { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + break; + } + default: TRESPASS(); } @@ -392,11 +410,36 @@ status_t MediaSender::packetizeAccessUnit( info.mPacketizerTrackIndex, accessUnit); } - status_t err = mHDCP->encrypt( - accessUnit->data(), accessUnit->size(), - trackIndex /* streamCTR */, - &inputCTR, - accessUnit->data()); + status_t err; + native_handle_t* handle; + if (accessUnit->meta()->findPointer("handle", (void**)&handle) + && handle != NULL) { + int32_t rangeLength, rangeOffset; + sp<AMessage> notify; + CHECK(accessUnit->meta()->findInt32("rangeOffset", &rangeOffset)); + CHECK(accessUnit->meta()->findInt32("rangeLength", &rangeLength)); + CHECK(accessUnit->meta()->findMessage("notify", ¬ify) + && notify != NULL); + CHECK_GE(accessUnit->size(), rangeLength); + + sp<GraphicBuffer> grbuf(new GraphicBuffer( + rangeOffset + rangeLength, 1, HAL_PIXEL_FORMAT_Y8, + GRALLOC_USAGE_HW_VIDEO_ENCODER, rangeOffset + rangeLength, + handle, false)); + + err = mHDCP->encryptNative( + grbuf, rangeOffset, rangeLength, + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + notify->post(); + } else { + err = mHDCP->encrypt( + accessUnit->data(), accessUnit->size(), + trackIndex /* streamCTR */, + &inputCTR, + accessUnit->data()); + } if (err != OK) { ALOGE("Failed to HDCP-encrypt media data (err %d)", diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h index 64722c5..04538ea 100644 --- a/media/libstagefright/wifi-display/MediaSender.h +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -43,6 +43,7 @@ struct MediaSender : public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; MediaSender( diff --git a/media/libstagefright/wifi-display/ParsedMessage.cpp b/media/libstagefright/wifi-display/ParsedMessage.cpp deleted file mode 100644 index c0e60c3..0000000 --- a/media/libstagefright/wifi-display/ParsedMessage.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ParsedMessage.h" - -#include <ctype.h> -#include <media/stagefright/foundation/ABuffer.h> -#include <media/stagefright/foundation/ADebug.h> - -namespace android { - -// static -sp<ParsedMessage> ParsedMessage::Parse( - const char *data, size_t size, bool noMoreData, size_t *length) { - sp<ParsedMessage> msg = new ParsedMessage; - ssize_t res = msg->parse(data, size, noMoreData); - - if (res < 0) { - *length = 0; - return NULL; - } - - *length = res; - return msg; -} - -ParsedMessage::ParsedMessage() { -} - -ParsedMessage::~ParsedMessage() { -} - -bool ParsedMessage::findString(const char *name, AString *value) const { - AString key = name; - key.tolower(); - - ssize_t index = mDict.indexOfKey(key); - - if (index < 0) { - value->clear(); - - return false; - } - - *value = mDict.valueAt(index); - return true; -} - -bool ParsedMessage::findInt32(const char *name, int32_t *value) const { - AString stringValue; - - if (!findString(name, &stringValue)) { - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -const char *ParsedMessage::getContent() const { - return mContent.c_str(); -} - -ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) { - if (size == 0) { - return -1; - } - - ssize_t lastDictIndex = -1; - - size_t offset = 0; - while (offset < size) { - size_t lineEndOffset = offset; - while (lineEndOffset + 1 < size - && (data[lineEndOffset] != '\r' - || data[lineEndOffset + 1] != '\n')) { - ++lineEndOffset; - } - - if (lineEndOffset + 1 >= size) { - return -1; - } - - AString line(&data[offset], lineEndOffset - offset); - - if (offset == 0) { - // Special handling for the request/status line. - - mDict.add(AString("_"), line); - offset = lineEndOffset + 2; - - continue; - } - - if (lineEndOffset == offset) { - offset += 2; - break; - } - - if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') { - // Support for folded header values. - - if (lastDictIndex >= 0) { - // Otherwise it's malformed since the first header line - // cannot continue anything... - - AString &value = mDict.editValueAt(lastDictIndex); - value.append(line); - } - - offset = lineEndOffset + 2; - continue; - } - - ssize_t colonPos = line.find(":"); - if (colonPos >= 0) { - AString key(line, 0, colonPos); - key.trim(); - key.tolower(); - - line.erase(0, colonPos + 1); - - lastDictIndex = mDict.add(key, line); - } - - offset = lineEndOffset + 2; - } - - for (size_t i = 0; i < mDict.size(); ++i) { - mDict.editValueAt(i).trim(); - } - - // Found the end of headers. - - int32_t contentLength; - if (!findInt32("content-length", &contentLength) || contentLength < 0) { - contentLength = 0; - } - - size_t totalLength = offset + contentLength; - - if (size < totalLength) { - return -1; - } - - mContent.setTo(&data[offset], contentLength); - - return totalLength; -} - -void ParsedMessage::getRequestField(size_t index, AString *field) const { - AString line; - CHECK(findString("_", &line)); - - size_t prevOffset = 0; - size_t offset = 0; - for (size_t i = 0; i <= index; ++i) { - ssize_t spacePos = line.find(" ", offset); - - if (spacePos < 0) { - spacePos = line.size(); - } - - prevOffset = offset; - offset = spacePos + 1; - } - - field->setTo(line, prevOffset, offset - prevOffset - 1); -} - -bool ParsedMessage::getStatusCode(int32_t *statusCode) const { - AString statusCodeString; - getRequestField(1, &statusCodeString); - - char *end; - *statusCode = strtol(statusCodeString.c_str(), &end, 10); - - if (*end != '\0' || end == statusCodeString.c_str() - || (*statusCode) < 100 || (*statusCode) > 999) { - *statusCode = 0; - return false; - } - - return true; -} - -AString ParsedMessage::debugString() const { - AString line; - CHECK(findString("_", &line)); - - line.append("\n"); - - for (size_t i = 0; i < mDict.size(); ++i) { - const AString &key = mDict.keyAt(i); - const AString &value = mDict.valueAt(i); - - if (key == AString("_")) { - continue; - } - - line.append(key); - line.append(": "); - line.append(value); - line.append("\n"); - } - - line.append("\n"); - line.append(mContent); - - return line; -} - -// static -bool ParsedMessage::GetAttribute( - const char *s, const char *key, AString *value) { - value->clear(); - - size_t keyLen = strlen(key); - - for (;;) { - while (isspace(*s)) { - ++s; - } - - const char *colonPos = strchr(s, ';'); - - size_t len = - (colonPos == NULL) ? strlen(s) : colonPos - s; - - if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { - value->setTo(&s[keyLen + 1], len - keyLen - 1); - return true; - } - - if (colonPos == NULL) { - return false; - } - - s = colonPos + 1; - } -} - -// static -bool ParsedMessage::GetInt32Attribute( - const char *s, const char *key, int32_t *value) { - AString stringValue; - if (!GetAttribute(s, key, &stringValue)) { - *value = 0; - return false; - } - - char *end; - *value = strtol(stringValue.c_str(), &end, 10); - - if (end == stringValue.c_str() || *end != '\0') { - *value = 0; - return false; - } - - return true; -} - -} // namespace android - diff --git a/media/libstagefright/wifi-display/ParsedMessage.h b/media/libstagefright/wifi-display/ParsedMessage.h deleted file mode 100644 index e9a1859..0000000 --- a/media/libstagefright/wifi-display/ParsedMessage.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AString.h> -#include <utils/KeyedVector.h> -#include <utils/RefBase.h> - -namespace android { - -// Encapsulates an "HTTP/RTSP style" response, i.e. a status line, -// key/value pairs making up the headers and an optional body/content. -struct ParsedMessage : public RefBase { - static sp<ParsedMessage> Parse( - const char *data, size_t size, bool noMoreData, size_t *length); - - bool findString(const char *name, AString *value) const; - bool findInt32(const char *name, int32_t *value) const; - - const char *getContent() const; - - void getRequestField(size_t index, AString *field) const; - bool getStatusCode(int32_t *statusCode) const; - - AString debugString() const; - - static bool GetAttribute(const char *s, const char *key, AString *value); - - static bool GetInt32Attribute( - const char *s, const char *key, int32_t *value); - - -protected: - virtual ~ParsedMessage(); - -private: - KeyedVector<AString, AString> mDict; - AString mContent; - - ParsedMessage(); - - ssize_t parse(const char *data, size_t size, bool noMoreData); - - DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage); -}; - -} // namespace android diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp index 458b163..04e02c1 100644 --- a/media/libstagefright/wifi-display/VideoFormats.cpp +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -24,7 +24,8 @@ namespace android { -VideoFormats::config_t VideoFormats::mConfigs[][32] = { +// static +const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = { { // CEA Resolutions { 640, 480, 60, false, 0, 0}, @@ -133,6 +134,8 @@ VideoFormats::config_t VideoFormats::mConfigs[][32] = { }; VideoFormats::VideoFormats() { + memcpy(mConfigs, mResolutionTable, sizeof(mConfigs)); + for (size_t i = 0; i < kNumResolutionTypes; ++i) { mResolutionEnabled[i] = 0; } @@ -175,6 +178,29 @@ void VideoFormats::enableAll() { } } +void VideoFormats::enableResolutionUpto( + ResolutionType type, size_t index, + ProfileType profile, LevelType level) { + size_t width, height, fps, score; + bool interlaced; + if (!GetConfiguration(type, index, &width, &height, + &fps, &interlaced)) { + ALOGE("Maximum resolution not found!"); + return; + } + score = width * height * fps * (!interlaced + 1); + for (size_t i = 0; i < kNumResolutionTypes; ++i) { + for (size_t j = 0; j < 32; j++) { + if (GetConfiguration((ResolutionType)i, j, + &width, &height, &fps, &interlaced) + && score >= width * height * fps * (!interlaced + 1)) { + setResolutionEnabled((ResolutionType)i, j); + setProfileLevel((ResolutionType)i, j, profile, level); + } + } + } +} + void VideoFormats::setResolutionEnabled( ResolutionType type, size_t index, bool enabled) { CHECK_LT(type, kNumResolutionTypes); @@ -182,11 +208,56 @@ void VideoFormats::setResolutionEnabled( if (enabled) { mResolutionEnabled[type] |= (1ul << index); + mConfigs[type][index].profile = (1ul << PROFILE_CBP); + mConfigs[type][index].level = (1ul << LEVEL_31); } else { mResolutionEnabled[type] &= ~(1ul << index); + mConfigs[type][index].profile = 0; + mConfigs[type][index].level = 0; } } +void VideoFormats::setProfileLevel( + ResolutionType type, size_t index, + ProfileType profile, LevelType level) { + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + mConfigs[type][index].profile = (1ul << profile); + mConfigs[type][index].level = (1ul << level); +} + +void VideoFormats::getProfileLevel( + ResolutionType type, size_t index, + ProfileType *profile, LevelType *level) const{ + CHECK_LT(type, kNumResolutionTypes); + CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL)); + + int i, bestProfile = -1, bestLevel = -1; + + for (i = 0; i < kNumProfileTypes; ++i) { + if (mConfigs[type][index].profile & (1ul << i)) { + bestProfile = i; + } + } + + for (i = 0; i < kNumLevelTypes; ++i) { + if (mConfigs[type][index].level & (1ul << i)) { + bestLevel = i; + } + } + + if (bestProfile == -1 || bestLevel == -1) { + ALOGE("Profile or level not set for resolution type %d, index %d", + type, index); + bestProfile = PROFILE_CBP; + bestLevel = LEVEL_31; + } + + *profile = (ProfileType) bestProfile; + *level = (LevelType) bestLevel; +} + bool VideoFormats::isResolutionEnabled( ResolutionType type, size_t index) const { CHECK_LT(type, kNumResolutionTypes); @@ -207,7 +278,7 @@ bool VideoFormats::GetConfiguration( return false; } - const config_t *config = &mConfigs[type][index]; + const config_t *config = &mResolutionTable[type][index]; if (config->width == 0) { return false; @@ -251,9 +322,12 @@ bool VideoFormats::parseH264Codec(const char *spec) { if (res[i] & (1ul << j)){ mResolutionEnabled[i] |= (1ul << j); if (profile > mConfigs[i][j].profile) { + // prefer higher profile (even if level is lower) mConfigs[i][j].profile = profile; - if (level > mConfigs[i][j].level) - mConfigs[i][j].level = level; + mConfigs[i][j].level = level; + } else if (profile == mConfigs[i][j].profile && + level > mConfigs[i][j].level) { + mConfigs[i][j].level = level; } } } @@ -262,9 +336,51 @@ bool VideoFormats::parseH264Codec(const char *spec) { return true; } +// static +bool VideoFormats::GetProfileLevel( + ProfileType profile, LevelType level, unsigned *profileIdc, + unsigned *levelIdc, unsigned *constraintSet) { + CHECK_LT(profile, kNumProfileTypes); + CHECK_LT(level, kNumLevelTypes); + + static const unsigned kProfileIDC[kNumProfileTypes] = { + 66, // PROFILE_CBP + 100, // PROFILE_CHP + }; + + static const unsigned kLevelIDC[kNumLevelTypes] = { + 31, // LEVEL_31 + 32, // LEVEL_32 + 40, // LEVEL_40 + 41, // LEVEL_41 + 42, // LEVEL_42 + }; + + static const unsigned kConstraintSet[kNumProfileTypes] = { + 0xc0, // PROFILE_CBP + 0x0c, // PROFILE_CHP + }; + + if (profileIdc) { + *profileIdc = kProfileIDC[profile]; + } + + if (levelIdc) { + *levelIdc = kLevelIDC[level]; + } + + if (constraintSet) { + *constraintSet = kConstraintSet[profile]; + } + + return true; +} + bool VideoFormats::parseFormatSpec(const char *spec) { CHECK_EQ(kNumResolutionTypes, 3); + disableAll(); + unsigned native, dummy; unsigned res[3]; size_t size = strlen(spec); @@ -320,8 +436,10 @@ AString VideoFormats::getFormatSpec(bool forM4Message) const { // max-vres (none or 2 byte) return StringPrintf( - "%02x 00 02 02 %08x %08x %08x 00 0000 0000 00 none none", + "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none", forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType), + mConfigs[mNativeType][mNativeIndex].profile, + mConfigs[mNativeType][mNativeIndex].level, mResolutionEnabled[0], mResolutionEnabled[1], mResolutionEnabled[2]); @@ -332,7 +450,9 @@ bool VideoFormats::PickBestFormat( const VideoFormats &sinkSupported, const VideoFormats &sourceSupported, ResolutionType *chosenType, - size_t *chosenIndex) { + size_t *chosenIndex, + ProfileType *chosenProfile, + LevelType *chosenLevel) { #if 0 // Support for the native format is a great idea, the spec includes // these features, but nobody supports it and the tests don't validate it. @@ -412,6 +532,18 @@ bool VideoFormats::PickBestFormat( *chosenType = (ResolutionType)bestType; *chosenIndex = bestIndex; + // Pick the best profile/level supported by both sink and source. + ProfileType srcProfile, sinkProfile; + LevelType srcLevel, sinkLevel; + sourceSupported.getProfileLevel( + (ResolutionType)bestType, bestIndex, + &srcProfile, &srcLevel); + sinkSupported.getProfileLevel( + (ResolutionType)bestType, bestIndex, + &sinkProfile, &sinkLevel); + *chosenProfile = srcProfile < sinkProfile ? srcProfile : sinkProfile; + *chosenLevel = srcLevel < sinkLevel ? srcLevel : sinkLevel; + return true; } diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h index 01de246..fd38fd1 100644 --- a/media/libstagefright/wifi-display/VideoFormats.h +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -69,17 +69,33 @@ struct VideoFormats { void disableAll(); void enableAll(); + void enableResolutionUpto( + ResolutionType type, size_t index, + ProfileType profile, LevelType level); void setResolutionEnabled( ResolutionType type, size_t index, bool enabled = true); bool isResolutionEnabled(ResolutionType type, size_t index) const; + void setProfileLevel( + ResolutionType type, size_t index, + ProfileType profile, LevelType level); + + void getProfileLevel( + ResolutionType type, size_t index, + ProfileType *profile, LevelType *level) const; + static bool GetConfiguration( ResolutionType type, size_t index, size_t *width, size_t *height, size_t *framesPerSecond, bool *interlaced); + static bool GetProfileLevel( + ProfileType profile, LevelType level, + unsigned *profileIdc, unsigned *levelIdc, + unsigned *constraintSet); + bool parseFormatSpec(const char *spec); AString getFormatSpec(bool forM4Message = false) const; @@ -87,7 +103,9 @@ struct VideoFormats { const VideoFormats &sinkSupported, const VideoFormats &sourceSupported, ResolutionType *chosenType, - size_t *chosenIndex); + size_t *chosenIndex, + ProfileType *chosenProfile, + LevelType *chosenLevel); private: bool parseH264Codec(const char *spec); @@ -95,7 +113,8 @@ private: size_t mNativeIndex; uint32_t mResolutionEnabled[kNumResolutionTypes]; - static config_t mConfigs[kNumResolutionTypes][32]; + static const config_t mResolutionTable[kNumResolutionTypes][32]; + config_t mConfigs[kNumResolutionTypes][32]; DISALLOW_EVIL_CONSTRUCTORS(VideoFormats); }; diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp index 095fd97..1887b8b 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -20,11 +20,10 @@ #include "RTPSender.h" -#include "ANetworkSession.h" - #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ANetworkSession.h> #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/Utils.h> @@ -767,6 +766,17 @@ status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) { } status_t RTPSender::parseAPP(const uint8_t *data, size_t size) { + if (!memcmp("late", &data[8], 4)) { + int64_t avgLatencyUs = (int64_t)U64_AT(&data[12]); + int64_t maxLatencyUs = (int64_t)U64_AT(&data[20]); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatInformSender); + notify->setInt64("avgLatencyUs", avgLatencyUs); + notify->setInt64("maxLatencyUs", maxLatencyUs); + notify->post(); + } + return OK; } diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h index 7dc138a..fefcab7 100644 --- a/media/libstagefright/wifi-display/rtp/RTPSender.h +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -37,6 +37,7 @@ struct RTPSender : public RTPBase, public AHandler { kWhatInitDone, kWhatError, kWhatNetworkStall, + kWhatInformSender, }; RTPSender( const sp<ANetworkSession> &netSession, diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 5344623..753b3ec 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -21,6 +21,7 @@ #include "Converter.h" #include "MediaPuller.h" +#include "include/avc_utils.h" #include <cutils/properties.h> #include <gui/Surface.h> @@ -33,6 +34,8 @@ #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> +#include <arpa/inet.h> + #include <OMX_Video.h> namespace android { @@ -40,14 +43,15 @@ namespace android { Converter::Converter( const sp<AMessage> ¬ify, const sp<ALooper> &codecLooper, - const sp<AMessage> &format, - bool usePCMAudio) - : mInitCheck(NO_INIT), - mNotify(notify), + const sp<AMessage> &outputFormat, + uint32_t flags) + : mNotify(notify), mCodecLooper(codecLooper), - mInputFormat(format), + mOutputFormat(outputFormat), + mFlags(flags), mIsVideo(false), - mIsPCMAudio(usePCMAudio), + mIsH264(false), + mIsPCMAudio(false), mNeedToManuallyPrependSPSPPS(false), mDoMoreWorkPending(false) #if ENABLE_SILENCE_DETECTION @@ -56,20 +60,17 @@ Converter::Converter( #endif ,mPrevVideoBitrate(-1) ,mNumFramesToDrop(0) + ,mEncodingSuspended(false) { AString mime; - CHECK(mInputFormat->findString("mime", &mime)); + CHECK(mOutputFormat->findString("mime", &mime)); if (!strncasecmp("video/", mime.c_str(), 6)) { mIsVideo = true; - } - - CHECK(!usePCMAudio || !mIsVideo); - mInitCheck = initEncoder(); - - if (mInitCheck != OK) { - releaseEncoder(); + mIsH264 = !strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC); + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime.c_str())) { + mIsPCMAudio = true; } } @@ -119,8 +120,19 @@ void Converter::shutdownAsync() { (new AMessage(kWhatShutdown, id()))->post(); } -status_t Converter::initCheck() const { - return mInitCheck; +status_t Converter::init() { + status_t err = initEncoder(); + + if (err != OK) { + releaseEncoder(); + } + + return err; +} + +sp<IGraphicBufferProducer> Converter::getGraphicBufferProducer() { + CHECK(mFlags & FLAG_USE_SURFACE_INPUT); + return mGraphicBufferProducer; } size_t Converter::getInputBufferCount() const { @@ -152,23 +164,10 @@ int32_t Converter::GetInt32Property( } status_t Converter::initEncoder() { - AString inputMIME; - CHECK(mInputFormat->findString("mime", &inputMIME)); - AString outputMIME; - bool isAudio = false; - if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) { - if (mIsPCMAudio) { - outputMIME = MEDIA_MIMETYPE_AUDIO_RAW; - } else { - outputMIME = MEDIA_MIMETYPE_AUDIO_AAC; - } - isAudio = true; - } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) { - outputMIME = MEDIA_MIMETYPE_VIDEO_AVC; - } else { - TRESPASS(); - } + CHECK(mOutputFormat->findString("mime", &outputMIME)); + + bool isAudio = !strncasecmp(outputMIME.c_str(), "audio/", 6); if (!mIsPCMAudio) { mEncoder = MediaCodec::CreateByType( @@ -179,14 +178,10 @@ status_t Converter::initEncoder() { } } - mOutputFormat = mInputFormat->dup(); - if (mIsPCMAudio) { return OK; } - mOutputFormat->setString("mime", outputMIME.c_str()); - int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000); int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000); mPrevVideoBitrate = videoBitrate; @@ -262,6 +257,16 @@ status_t Converter::initEncoder() { return err; } + if (mFlags & FLAG_USE_SURFACE_INPUT) { + CHECK(mIsVideo); + + err = mEncoder->createInputSurface(&mGraphicBufferProducer); + + if (err != OK) { + return err; + } + } + err = mEncoder->start(); if (err != OK) { @@ -274,7 +279,17 @@ status_t Converter::initEncoder() { return err; } - return mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + err = mEncoder->getOutputBuffers(&mEncoderOutputBuffers); + + if (err != OK) { + return err; + } + + if (mFlags & FLAG_USE_SURFACE_INPUT) { + scheduleDoMoreWork(); + } + + return OK; } void Converter::notifyError(status_t err) { @@ -330,9 +345,12 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); - if (mIsVideo && mNumFramesToDrop) { - --mNumFramesToDrop; - ALOGI("dropping frame."); + if (mNumFramesToDrop > 0 || mEncodingSuspended) { + if (mNumFramesToDrop > 0) { + --mNumFramesToDrop; + ALOGI("dropping frame."); + } + ReleaseMediaBufferReference(accessUnit); break; } @@ -414,7 +432,7 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { } if (mIsVideo) { - ALOGI("requesting IDR frame"); + ALOGV("requesting IDR frame"); mEncoder->requestIDRFrame(); } break; @@ -427,8 +445,12 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { releaseEncoder(); AString mime; - CHECK(mInputFormat->findString("mime", &mime)); + CHECK(mOutputFormat->findString("mime", &mime)); ALOGI("encoder (%s) shut down.", mime.c_str()); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatShutdownCompleted); + notify->post(); break; } @@ -438,6 +460,32 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatReleaseOutputBuffer: + { + if (mEncoder != NULL) { + size_t bufferIndex; + CHECK(msg->findInt32("bufferIndex", (int32_t*)&bufferIndex)); + CHECK(bufferIndex < mEncoderOutputBuffers.size()); + mEncoder->releaseOutputBuffer(bufferIndex); + } + break; + } + + case kWhatSuspendEncoding: + { + int32_t suspend; + CHECK(msg->findInt32("suspend", &suspend)); + + mEncodingSuspended = suspend; + + if (mFlags & FLAG_USE_SURFACE_INPUT) { + sp<AMessage> params = new AMessage; + params->setInt32("drop-input-frames",suspend); + mEncoder->setParameters(params); + } + break; + } + default: TRESPASS(); } @@ -623,28 +671,46 @@ status_t Converter::feedEncoderInputBuffers() { return OK; } +sp<ABuffer> Converter::prependCSD(const sp<ABuffer> &accessUnit) const { + CHECK(mCSD0 != NULL); + + sp<ABuffer> dup = new ABuffer(accessUnit->size() + mCSD0->size()); + memcpy(dup->data(), mCSD0->data(), mCSD0->size()); + memcpy(dup->data() + mCSD0->size(), accessUnit->data(), accessUnit->size()); + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + dup->meta()->setInt64("timeUs", timeUs); + + return dup; +} + status_t Converter::doMoreWork() { status_t err; - for (;;) { - size_t bufferIndex; - err = mEncoder->dequeueInputBuffer(&bufferIndex); + if (!(mFlags & FLAG_USE_SURFACE_INPUT)) { + for (;;) { + size_t bufferIndex; + err = mEncoder->dequeueInputBuffer(&bufferIndex); - if (err != OK) { - break; + if (err != OK) { + break; + } + + mAvailEncoderInputIndices.push_back(bufferIndex); } - mAvailEncoderInputIndices.push_back(bufferIndex); + feedEncoderInputBuffers(); } - feedEncoderInputBuffers(); - for (;;) { size_t bufferIndex; size_t offset; size_t size; int64_t timeUs; uint32_t flags; + native_handle_t* handle = NULL; err = mEncoder->dequeueOutputBuffer( &bufferIndex, &offset, &size, &timeUs, &flags); @@ -667,19 +733,63 @@ status_t Converter::doMoreWork() { notify->setInt32("what", kWhatEOS); notify->post(); } else { - sp<ABuffer> buffer = new ABuffer(size); +#if 0 + if (mIsVideo) { + int32_t videoBitrate = GetInt32Property( + "media.wfd.video-bitrate", 5000000); + + setVideoBitrate(videoBitrate); + } +#endif + + sp<ABuffer> buffer; + sp<ABuffer> outbuf = mEncoderOutputBuffers.itemAt(bufferIndex); + + if (outbuf->meta()->findPointer("handle", (void**)&handle) && + handle != NULL) { + int32_t rangeLength, rangeOffset; + CHECK(outbuf->meta()->findInt32("rangeOffset", &rangeOffset)); + CHECK(outbuf->meta()->findInt32("rangeLength", &rangeLength)); + outbuf->meta()->setPointer("handle", NULL); + + // MediaSender will post the following message when HDCP + // is done, to release the output buffer back to encoder. + sp<AMessage> notify(new AMessage( + kWhatReleaseOutputBuffer, id())); + notify->setInt32("bufferIndex", bufferIndex); + + buffer = new ABuffer( + rangeLength > (int32_t)size ? rangeLength : size); + buffer->meta()->setPointer("handle", handle); + buffer->meta()->setInt32("rangeOffset", rangeOffset); + buffer->meta()->setInt32("rangeLength", rangeLength); + buffer->meta()->setMessage("notify", notify); + } else { + buffer = new ABuffer(size); + } + buffer->meta()->setInt64("timeUs", timeUs); ALOGV("[%s] time %lld us (%.2f secs)", mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6); - memcpy(buffer->data(), - mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset, - size); + memcpy(buffer->data(), outbuf->base() + offset, size); if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { - mOutputFormat->setBuffer("csd-0", buffer); + if (!handle) { + if (mIsH264) { + mCSD0 = buffer; + } + mOutputFormat->setBuffer("csd-0", buffer); + } } else { + if (mNeedToManuallyPrependSPSPPS + && mIsH264 + && (mFlags & FLAG_PREPEND_CSD_IF_NECESSARY) + && IsIDR(buffer)) { + buffer = prependCSD(buffer); + } + sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatAccessUnit); notify->setBuffer("accessUnit", buffer); @@ -687,7 +797,9 @@ status_t Converter::doMoreWork() { } } - mEncoder->releaseOutputBuffer(bufferIndex); + if (!handle) { + mEncoder->releaseOutputBuffer(bufferIndex); + } if (flags & MediaCodec::BUFFER_FLAG_EOS) { break; @@ -702,9 +814,18 @@ void Converter::requestIDRFrame() { } void Converter::dropAFrame() { + // Unsupported in surface input mode. + CHECK(!(mFlags & FLAG_USE_SURFACE_INPUT)); + (new AMessage(kWhatDropAFrame, id()))->post(); } +void Converter::suspendEncoding(bool suspend) { + sp<AMessage> msg = new AMessage(kWhatSuspendEncoding, id()); + msg->setInt32("suspend", suspend); + msg->post(); +} + int32_t Converter::getVideoBitrate() const { return mPrevVideoBitrate; } @@ -712,7 +833,7 @@ int32_t Converter::getVideoBitrate() const { void Converter::setVideoBitrate(int32_t bitRate) { if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) { sp<AMessage> params = new AMessage; - params->setInt32("videoBitrate", bitRate); + params->setInt32("video-bitrate", bitRate); mEncoder->setParameters(params); diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index ba297c4..5876e07 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -18,13 +18,12 @@ #define CONVERTER_H_ -#include "WifiDisplaySource.h" - #include <media/stagefright/foundation/AHandler.h> namespace android { struct ABuffer; +struct IGraphicBufferProducer; struct MediaCodec; #define ENABLE_SILENCE_DETECTION 0 @@ -33,13 +32,25 @@ struct MediaCodec; // media access unit of a different format. // Right now this'll convert raw video into H.264 and raw audio into AAC. struct Converter : public AHandler { - Converter( - const sp<AMessage> ¬ify, - const sp<ALooper> &codecLooper, - const sp<AMessage> &format, - bool usePCMAudio); + enum { + kWhatAccessUnit, + kWhatEOS, + kWhatError, + kWhatShutdownCompleted, + }; + + enum FlagBits { + FLAG_USE_SURFACE_INPUT = 1, + FLAG_PREPEND_CSD_IF_NECESSARY = 2, + }; + Converter(const sp<AMessage> ¬ify, + const sp<ALooper> &codecLooper, + const sp<AMessage> &outputFormat, + uint32_t flags = 0); - status_t initCheck() const; + status_t init(); + + sp<IGraphicBufferProducer> getGraphicBufferProducer(); size_t getInputBufferCount() const; @@ -52,21 +63,7 @@ struct Converter : public AHandler { void requestIDRFrame(); void dropAFrame(); - - enum { - kWhatAccessUnit, - kWhatEOS, - kWhatError, - }; - - enum { - kWhatDoMoreWork, - kWhatRequestIDRFrame, - kWhatShutdown, - kWhatMediaPullerNotify, - kWhatEncoderActivity, - kWhatDropAFrame, - }; + void suspendEncoding(bool suspend); void shutdownAsync(); @@ -75,23 +72,40 @@ struct Converter : public AHandler { static int32_t GetInt32Property(const char *propName, int32_t defaultValue); + enum { + // MUST not conflict with private enums below. + kWhatMediaPullerNotify = 'pulN', + }; + protected: virtual ~Converter(); virtual void onMessageReceived(const sp<AMessage> &msg); private: - status_t mInitCheck; + enum { + kWhatDoMoreWork, + kWhatRequestIDRFrame, + kWhatSuspendEncoding, + kWhatShutdown, + kWhatEncoderActivity, + kWhatDropAFrame, + kWhatReleaseOutputBuffer, + }; + sp<AMessage> mNotify; sp<ALooper> mCodecLooper; - sp<AMessage> mInputFormat; + sp<AMessage> mOutputFormat; + uint32_t mFlags; bool mIsVideo; + bool mIsH264; bool mIsPCMAudio; - sp<AMessage> mOutputFormat; bool mNeedToManuallyPrependSPSPPS; sp<MediaCodec> mEncoder; sp<AMessage> mEncoderActivityNotify; + sp<IGraphicBufferProducer> mGraphicBufferProducer; + Vector<sp<ABuffer> > mEncoderInputBuffers; Vector<sp<ABuffer> > mEncoderOutputBuffers; @@ -99,6 +113,8 @@ private: List<sp<ABuffer> > mInputBufferQueue; + sp<ABuffer> mCSD0; + bool mDoMoreWorkPending; #if ENABLE_SILENCE_DETECTION @@ -111,6 +127,7 @@ private: int32_t mPrevVideoBitrate; int32_t mNumFramesToDrop; + bool mEncodingSuspended; status_t initEncoder(); void releaseEncoder(); @@ -129,6 +146,8 @@ private: static bool IsSilence(const sp<ABuffer> &accessUnit); + sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const; + DISALLOW_EVIL_CONSTRUCTORS(Converter); }; diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp index 189bea3..7e8891d 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.cpp +++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp @@ -93,6 +93,9 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { err = mSource->start(params.get()); } else { err = mSource->start(); + if (err != OK) { + ALOGE("source failed to start w/ err %d", err); + } } if (err == OK) { diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 3d7b865..286ea13 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -378,7 +378,9 @@ status_t WifiDisplaySource::PlaybackSession::init( bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id()); mMediaSender = new MediaSender(mNetSession, notify); looper()->registerHandler(mMediaSender); @@ -390,7 +392,9 @@ status_t WifiDisplaySource::PlaybackSession::init( usePCMAudio, enableVideo, videoResolutionType, - videoResolutionIndex); + videoResolutionIndex, + videoProfileType, + videoLevelType); if (err == OK) { err = mMediaSender->initAsync( @@ -517,7 +521,7 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( if (mTracks.isEmpty()) { ALOGI("Reached EOS"); } - } else { + } else if (what != Converter::kWhatShutdownCompleted) { CHECK_EQ(what, Converter::kWhatError); status_t err; @@ -559,6 +563,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( converter->dropAFrame(); } } + } else if (what == MediaSender::kWhatInformSender) { + onSinkFeedback(msg); } else { TRESPASS(); } @@ -654,6 +660,89 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } +void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp<AMessage> &msg) { + int64_t avgLatencyUs; + CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs)); + + int64_t maxLatencyUs; + CHECK(msg->findInt64("maxLatencyUs", &maxLatencyUs)); + + ALOGI("sink reports avg. latency of %lld ms (max %lld ms)", + avgLatencyUs / 1000ll, + maxLatencyUs / 1000ll); + + if (mVideoTrackIndex >= 0) { + const sp<Track> &videoTrack = mTracks.valueFor(mVideoTrackIndex); + sp<Converter> converter = videoTrack->converter(); + + if (converter != NULL) { + int32_t videoBitrate = + Converter::GetInt32Property("media.wfd.video-bitrate", -1); + + char val[PROPERTY_VALUE_MAX]; + if (videoBitrate < 0 + && property_get("media.wfd.video-bitrate", val, NULL) + && !strcasecmp("adaptive", val)) { + videoBitrate = converter->getVideoBitrate(); + + if (avgLatencyUs > 300000ll) { + videoBitrate *= 0.6; + } else if (avgLatencyUs < 100000ll) { + videoBitrate *= 1.1; + } + } + + if (videoBitrate > 0) { + if (videoBitrate < 500000) { + videoBitrate = 500000; + } else if (videoBitrate > 10000000) { + videoBitrate = 10000000; + } + + if (videoBitrate != converter->getVideoBitrate()) { + ALOGI("setting video bitrate to %d bps", videoBitrate); + + converter->setVideoBitrate(videoBitrate); + } + } + } + + sp<RepeaterSource> repeaterSource = videoTrack->repeaterSource(); + if (repeaterSource != NULL) { + double rateHz = + Converter::GetInt32Property( + "media.wfd.video-framerate", -1); + + char val[PROPERTY_VALUE_MAX]; + if (rateHz < 0.0 + && property_get("media.wfd.video-framerate", val, NULL) + && !strcasecmp("adaptive", val)) { + rateHz = repeaterSource->getFrameRate(); + + if (avgLatencyUs > 300000ll) { + rateHz *= 0.9; + } else if (avgLatencyUs < 200000ll) { + rateHz *= 1.1; + } + } + + if (rateHz > 0) { + if (rateHz < 5.0) { + rateHz = 5.0; + } else if (rateHz > 30.0) { + rateHz = 30.0; + } + + if (rateHz != repeaterSource->getFrameRate()) { + ALOGI("setting frame rate to %.2f Hz", rateHz); + + repeaterSource->setFrameRate(rateHz); + } + } + } + } +} + status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( bool enableAudio, bool enableVideo) { DataSource::RegisterDefaultSniffers(); @@ -785,7 +874,9 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { CHECK(enableAudio || enableVideo); if (!mMediaPath.empty()) { @@ -794,7 +885,8 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( if (enableVideo) { status_t err = addVideoSource( - videoResolutionType, videoResolutionIndex); + videoResolutionType, videoResolutionIndex, videoProfileType, + videoLevelType); if (err != OK) { return err; @@ -810,9 +902,13 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer( status_t WifiDisplaySource::PlaybackSession::addSource( bool isVideo, const sp<MediaSource> &source, bool isRepeaterSource, - bool usePCMAudio, size_t *numInputBuffers) { + bool usePCMAudio, unsigned profileIdc, unsigned levelIdc, + unsigned constraintSet, size_t *numInputBuffers) { CHECK(!usePCMAudio || !isVideo); CHECK(!isRepeaterSource || isVideo); + CHECK(!profileIdc || isVideo); + CHECK(!levelIdc || isVideo); + CHECK(!constraintSet || isVideo); sp<ALooper> pullLooper = new ALooper; pullLooper->setName("pull_looper"); @@ -841,26 +937,37 @@ status_t WifiDisplaySource::PlaybackSession::addSource( CHECK_EQ(err, (status_t)OK); if (isVideo) { + format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC); format->setInt32("store-metadata-in-buffers", true); - + format->setInt32("store-metadata-in-buffers-output", (mHDCP != NULL) + && (mHDCP->getCaps() & HDCPModule::HDCP_CAPS_ENCRYPT_NATIVE)); format->setInt32( "color-format", OMX_COLOR_FormatAndroidOpaque); + format->setInt32("profile-idc", profileIdc); + format->setInt32("level-idc", levelIdc); + format->setInt32("constraint-set", constraintSet); + } else { + format->setString( + "mime", + usePCMAudio + ? MEDIA_MIMETYPE_AUDIO_RAW : MEDIA_MIMETYPE_AUDIO_AAC); } notify = new AMessage(kWhatConverterNotify, id()); notify->setSize("trackIndex", trackIndex); - sp<Converter> converter = - new Converter(notify, codecLooper, format, usePCMAudio); + sp<Converter> converter = new Converter(notify, codecLooper, format); + + looper()->registerHandler(converter); - err = converter->initCheck(); + err = converter->init(); if (err != OK) { ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err); + + looper()->unregisterHandler(converter->id()); return err; } - looper()->registerHandler(converter); - notify = new AMessage(Converter::kWhatMediaPullerNotify, converter->id()); notify->setSize("trackIndex", trackIndex); @@ -905,7 +1012,9 @@ status_t WifiDisplaySource::PlaybackSession::addSource( status_t WifiDisplaySource::PlaybackSession::addVideoSource( VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex) { + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType) { size_t width, height, framesPerSecond; bool interlaced; CHECK(VideoFormats::GetConfiguration( @@ -916,6 +1025,14 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource( &framesPerSecond, &interlaced)); + unsigned profileIdc, levelIdc, constraintSet; + CHECK(VideoFormats::GetProfileLevel( + videoProfileType, + videoLevelType, + &profileIdc, + &levelIdc, + &constraintSet)); + sp<SurfaceMediaSource> source = new SurfaceMediaSource(width, height); source->setUseAbsoluteTimestamps(); @@ -926,7 +1043,8 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource( size_t numInputBuffers; status_t err = addSource( true /* isVideo */, videoSource, true /* isRepeaterSource */, - false /* usePCMAudio */, &numInputBuffers); + false /* usePCMAudio */, profileIdc, levelIdc, constraintSet, + &numInputBuffers); if (err != OK) { return err; @@ -949,7 +1067,8 @@ status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) { if (audioSource->initCheck() == OK) { return addSource( false /* isVideo */, audioSource, false /* isRepeaterSource */, - usePCMAudio, NULL /* numInputBuffers */); + usePCMAudio, 0 /* profileIdc */, 0 /* levelIdc */, + 0 /* constraintSet */, NULL /* numInputBuffers */); } ALOGW("Unable to instantiate audio source"); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 39086a1..5c8ee94 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -53,7 +53,9 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); void destroyAsync(); @@ -130,18 +132,25 @@ private: bool usePCMAudio, bool enableVideo, VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); status_t addSource( bool isVideo, const sp<MediaSource> &source, bool isRepeaterSource, bool usePCMAudio, + unsigned profileIdc, + unsigned levelIdc, + unsigned contraintSet, size_t *numInputBuffers); status_t addVideoSource( VideoFormats::ResolutionType videoResolutionType, - size_t videoResolutionIndex); + size_t videoResolutionIndex, + VideoFormats::ProfileType videoProfileType, + VideoFormats::LevelType videoLevelType); status_t addAudioSource(bool usePCMAudio); diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index 2c4a373..c674700 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -261,12 +261,24 @@ void TSPacketizer::Track::finalize() { data[0] = 40; // descriptor_tag data[1] = 4; // descriptor_length - CHECK_GE(mCSD.size(), 1u); - const sp<ABuffer> &sps = mCSD.itemAt(0); - CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4)); - CHECK_GE(sps->size(), 7u); - // profile_idc, constraint_set*, level_idc - memcpy(&data[2], sps->data() + 4, 3); + if (mCSD.size() > 0) { + CHECK_GE(mCSD.size(), 1u); + const sp<ABuffer> &sps = mCSD.itemAt(0); + CHECK(!memcmp("\x00\x00\x00\x01", sps->data(), 4)); + CHECK_GE(sps->size(), 7u); + // profile_idc, constraint_set*, level_idc + memcpy(&data[2], sps->data() + 4, 3); + } else { + int32_t profileIdc, levelIdc, constraintSet; + CHECK(mFormat->findInt32("profile-idc", &profileIdc)); + CHECK(mFormat->findInt32("level-idc", &levelIdc)); + CHECK(mFormat->findInt32("constraint-set", &constraintSet)); + CHECK_GE(profileIdc, 0u); + CHECK_GE(levelIdc, 0u); + data[2] = profileIdc; // profile_idc + data[3] = constraintSet; // constraint_set* + data[4] = levelIdc; // level_idc + } // AVC_still_present=0, AVC_24_hour_picture_flag=0, reserved data[5] = 0x3f; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 22dd0b1..05e4018 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -21,7 +21,6 @@ #include "WifiDisplaySource.h" #include "PlaybackSession.h" #include "Parameters.h" -#include "ParsedMessage.h" #include "rtp/RTPSender.h" #include <binder/IServiceManager.h> @@ -32,6 +31,7 @@ #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/ParsedMessage.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/Utils.h> @@ -73,6 +73,12 @@ WifiDisplaySource::WifiDisplaySource( mSupportedSourceVideoFormats.setNativeResolution( VideoFormats::RESOLUTION_CEA, 5); // 1280x720 p30 + + // Enable all resolutions up to 1280x720p30 + mSupportedSourceVideoFormats.enableResolutionUpto( + VideoFormats::RESOLUTION_CEA, 5, + VideoFormats::PROFILE_CHP, // Constrained High Profile + VideoFormats::LEVEL_32); // Level 3.2 } WifiDisplaySource::~WifiDisplaySource() { @@ -164,10 +170,10 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { } else { err = -EINVAL; } - - mState = AWAITING_CLIENT_CONNECTION; } + mState = AWAITING_CLIENT_CONNECTION; + sp<AMessage> response = new AMessage; response->setInt32("err", err); response->postReply(replyID); @@ -401,7 +407,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { 0, // height, mUsingHDCP ? IRemoteDisplayClient::kDisplayFlagSecure - : 0); + : 0, + 0); } else { size_t width, height; @@ -420,7 +427,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { height, mUsingHDCP ? IRemoteDisplayClient::kDisplayFlagSecure - : 0); + : 0, + playbackSessionID); } } @@ -617,6 +625,9 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { chosenVideoFormat.disableAll(); chosenVideoFormat.setNativeResolution( mChosenVideoResolutionType, mChosenVideoResolutionIndex); + chosenVideoFormat.setProfileLevel( + mChosenVideoResolutionType, mChosenVideoResolutionIndex, + mChosenVideoProfile, mChosenVideoLevel); body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */)); body.append("\r\n"); @@ -729,6 +740,8 @@ status_t WifiDisplaySource::sendM16(int32_t sessionID) { ++mNextCSeq; + scheduleKeepAlive(sessionID); + return OK; } @@ -845,7 +858,9 @@ status_t WifiDisplaySource::onReceiveM3Response( mSupportedSinkVideoFormats, mSupportedSourceVideoFormats, &mChosenVideoResolutionType, - &mChosenVideoResolutionIndex)) { + &mChosenVideoResolutionIndex, + &mChosenVideoProfile, + &mChosenVideoLevel)) { ALOGE("Sink and source share no commonly supported video " "formats."); @@ -864,6 +879,9 @@ status_t WifiDisplaySource::onReceiveM3Response( ALOGI("Picked video resolution %u x %u %c%u", width, height, interlaced ? 'i' : 'p', framesPerSecond); + + ALOGI("Picked AVC profile %d, level %d", + mChosenVideoProfile, mChosenVideoLevel); } else { ALOGI("Sink doesn't support video at all."); } @@ -994,8 +1012,6 @@ status_t WifiDisplaySource::onReceiveM16Response( if (mClientInfo.mPlaybackSession != NULL) { mClientInfo.mPlaybackSession->updateLiveness(); - - scheduleKeepAlive(sessionID); } return OK; @@ -1257,7 +1273,9 @@ status_t WifiDisplaySource::onSetupRequest( mUsingPCMAudio, mSinkSupportsVideo, mChosenVideoResolutionType, - mChosenVideoResolutionIndex); + mChosenVideoResolutionIndex, + mChosenVideoProfile, + mChosenVideoLevel); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); @@ -1340,7 +1358,9 @@ status_t WifiDisplaySource::onPlayRequest( return ERROR_MALFORMED; } - if (mState != AWAITING_CLIENT_PLAY) { + if (mState != AWAITING_CLIENT_PLAY + && mState != PAUSED_TO_PLAYING + && mState != PAUSED) { ALOGW("Received PLAY request but we're in state %d", mState); sendErrorResponse( @@ -1367,7 +1387,7 @@ status_t WifiDisplaySource::onPlayRequest( return err; } - if (mState == PAUSED_TO_PLAYING) { + if (mState == PAUSED_TO_PLAYING || mPlaybackSessionEstablished) { mState = PLAYING; return OK; } @@ -1401,7 +1421,7 @@ status_t WifiDisplaySource::onPauseRequest( ALOGI("Received PAUSE request."); - if (mState != PLAYING_TO_PAUSED) { + if (mState != PLAYING_TO_PAUSED && mState != PLAYING) { return INVALID_OPERATION; } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 44d3e4d..750265f 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -18,10 +18,10 @@ #define WIFI_DISPLAY_SOURCE_H_ -#include "ANetworkSession.h" #include "VideoFormats.h" #include <media/stagefright/foundation/AHandler.h> +#include <media/stagefright/foundation/ANetworkSession.h> #include <netinet/in.h> @@ -131,6 +131,8 @@ private: VideoFormats::ResolutionType mChosenVideoResolutionType; size_t mChosenVideoResolutionIndex; + VideoFormats::ProfileType mChosenVideoProfile; + VideoFormats::LevelType mChosenVideoLevel; bool mSinkSupportsAudio; diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp deleted file mode 100644 index c947765..0000000 --- a/media/libstagefright/wifi-display/wfd.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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 "wfd" -#include <utils/Log.h> - -#include "source/WifiDisplaySource.h" - -#include <binder/ProcessState.h> -#include <binder/IServiceManager.h> -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> -#include <media/AudioSystem.h> -#include <media/IMediaPlayerService.h> -#include <media/IRemoteDisplay.h> -#include <media/IRemoteDisplayClient.h> -#include <media/stagefright/DataSource.h> -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/AMessage.h> -#include <ui/DisplayInfo.h> - -namespace android { - -static void usage(const char *me) { - fprintf(stderr, - "usage:\n" - " %s -l iface[:port]\tcreate a wifi display source\n" - " -f(ilename) \tstream media\n", - me); -} - -struct RemoteDisplayClient : public BnRemoteDisplayClient { - RemoteDisplayClient(); - - virtual void onDisplayConnected( - const sp<IGraphicBufferProducer> &bufferProducer, - uint32_t width, - uint32_t height, - uint32_t flags); - - virtual void onDisplayDisconnected(); - virtual void onDisplayError(int32_t error); - - void waitUntilDone(); - -protected: - virtual ~RemoteDisplayClient(); - -private: - Mutex mLock; - Condition mCondition; - - bool mDone; - - sp<SurfaceComposerClient> mComposerClient; - sp<IGraphicBufferProducer> mSurfaceTexture; - sp<IBinder> mDisplayBinder; - - DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); -}; - -RemoteDisplayClient::RemoteDisplayClient() - : mDone(false) { - mComposerClient = new SurfaceComposerClient; - CHECK_EQ(mComposerClient->initCheck(), (status_t)OK); -} - -RemoteDisplayClient::~RemoteDisplayClient() { -} - -void RemoteDisplayClient::onDisplayConnected( - const sp<IGraphicBufferProducer> &bufferProducer, - uint32_t width, - uint32_t height, - uint32_t flags) { - ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", - width, height, flags); - - if (bufferProducer != NULL) { - mSurfaceTexture = bufferProducer; - mDisplayBinder = mComposerClient->createDisplay( - String8("foo"), false /* secure */); - - SurfaceComposerClient::openGlobalTransaction(); - mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); - - Rect layerStackRect(1280, 720); // XXX fix this. - Rect displayRect(1280, 720); - - mComposerClient->setDisplayProjection( - mDisplayBinder, 0 /* 0 degree rotation */, - layerStackRect, - displayRect); - - SurfaceComposerClient::closeGlobalTransaction(); - } -} - -void RemoteDisplayClient::onDisplayDisconnected() { - ALOGI("onDisplayDisconnected"); - - Mutex::Autolock autoLock(mLock); - mDone = true; - mCondition.broadcast(); -} - -void RemoteDisplayClient::onDisplayError(int32_t error) { - ALOGI("onDisplayError error=%d", error); - - Mutex::Autolock autoLock(mLock); - mDone = true; - mCondition.broadcast(); -} - -void RemoteDisplayClient::waitUntilDone() { - Mutex::Autolock autoLock(mLock); - while (!mDone) { - mCondition.wait(mLock); - } -} - -static status_t enableAudioSubmix(bool enable) { - status_t err = AudioSystem::setDeviceConnectionState( - AUDIO_DEVICE_IN_REMOTE_SUBMIX, - enable - ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE - : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - NULL /* device_address */); - - if (err != OK) { - return err; - } - - err = AudioSystem::setDeviceConnectionState( - AUDIO_DEVICE_OUT_REMOTE_SUBMIX, - enable - ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE - : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - NULL /* device_address */); - - return err; -} - -static void createSource(const AString &addr, int32_t port) { - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> binder = sm->getService(String16("media.player")); - sp<IMediaPlayerService> service = - interface_cast<IMediaPlayerService>(binder); - - CHECK(service.get() != NULL); - - enableAudioSubmix(true /* enable */); - - String8 iface; - iface.append(addr.c_str()); - iface.append(StringPrintf(":%d", port).c_str()); - - sp<RemoteDisplayClient> client = new RemoteDisplayClient; - sp<IRemoteDisplay> display = service->listenForRemoteDisplay(client, iface); - - client->waitUntilDone(); - - display->dispose(); - display.clear(); - - enableAudioSubmix(false /* enable */); -} - -static void createFileSource( - const AString &addr, int32_t port, const char *path) { - sp<ANetworkSession> session = new ANetworkSession; - session->start(); - - sp<ALooper> looper = new ALooper; - looper->start(); - - sp<RemoteDisplayClient> client = new RemoteDisplayClient; - sp<WifiDisplaySource> source = new WifiDisplaySource(session, client, path); - looper->registerHandler(source); - - AString iface = StringPrintf("%s:%d", addr.c_str(), port); - CHECK_EQ((status_t)OK, source->start(iface.c_str())); - - client->waitUntilDone(); - - source->stop(); -} - -} // namespace android - -int main(int argc, char **argv) { - using namespace android; - - ProcessState::self()->startThreadPool(); - - DataSource::RegisterDefaultSniffers(); - - AString listenOnAddr; - int32_t listenOnPort = -1; - - AString path; - - int res; - while ((res = getopt(argc, argv, "hl:f:")) >= 0) { - switch (res) { - case 'f': - { - path = optarg; - break; - } - - case 'l': - { - const char *colonPos = strrchr(optarg, ':'); - - if (colonPos == NULL) { - listenOnAddr = optarg; - listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort; - } else { - listenOnAddr.setTo(optarg, colonPos - optarg); - - char *end; - listenOnPort = strtol(colonPos + 1, &end, 10); - - if (*end != '\0' || end == colonPos + 1 - || listenOnPort < 1 || listenOnPort > 65535) { - fprintf(stderr, "Illegal port specified.\n"); - exit(1); - } - } - break; - } - - case '?': - case 'h': - default: - usage(argv[0]); - exit(1); - } - } - - if (listenOnPort >= 0) { - if (path.empty()) { - createSource(listenOnAddr, listenOnPort); - } else { - createFileSource(listenOnAddr, listenOnPort, path.c_str()); - } - - exit(0); - } - - usage(argv[0]); - - return 0; -} |