summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/wifi-display
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/wifi-display')
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.cpp1119
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.h130
-rw-r--r--media/libstagefright/wifi-display/Android.mk55
-rw-r--r--media/libstagefright/wifi-display/MediaSender.cpp517
-rw-r--r--media/libstagefright/wifi-display/MediaSender.h132
-rw-r--r--media/libstagefright/wifi-display/Parameters.cpp4
-rw-r--r--media/libstagefright/wifi-display/ParsedMessage.cpp284
-rw-r--r--media/libstagefright/wifi-display/ParsedMessage.h60
-rw-r--r--media/libstagefright/wifi-display/VideoFormats.cpp551
-rw-r--r--media/libstagefright/wifi-display/VideoFormats.h125
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPBase.h51
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.cpp805
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.h121
-rw-r--r--media/libstagefright/wifi-display/sink/LinearRegression.cpp110
-rw-r--r--media/libstagefright/wifi-display/sink/LinearRegression.h52
-rw-r--r--media/libstagefright/wifi-display/sink/RTPSink.cpp806
-rw-r--r--media/libstagefright/wifi-display/sink/RTPSink.h98
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.cpp396
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.h84
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp644
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.h147
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp396
-rw-r--r--media/libstagefright/wifi-display/source/Converter.h78
-rw-r--r--media/libstagefright/wifi-display/source/MediaPuller.cpp37
-rw-r--r--media/libstagefright/wifi-display/source/MediaPuller.h6
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp827
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.h94
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.cpp24
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.h5
-rw-r--r--media/libstagefright/wifi-display/source/Sender.cpp979
-rw-r--r--media/libstagefright/wifi-display/source/Sender.h166
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.cpp305
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.h11
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp499
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h45
-rw-r--r--media/libstagefright/wifi-display/udptest.cpp355
-rw-r--r--media/libstagefright/wifi-display/wfd.cpp123
37 files changed, 3960 insertions, 6281 deletions
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
deleted file mode 100644
index 819cd62..0000000
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ /dev/null
@@ -1,1119 +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 <net/if.h>
-#include <netdb.h>
-#include <netinet/in.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;
-
-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> &notify);
-
- int32_t sessionID() const;
- int socket() const;
- sp<AMessage> getNotificationMessage() const;
-
- bool isRTSPServer() const;
- bool isTCPDatagramServer() const;
-
- bool wantsToRead();
- bool wantsToWrite();
-
- status_t readMore();
- status_t writeMore();
-
- status_t sendRequest(const void *data, ssize_t size);
-
- void setIsRTSPConnection(bool yesno);
-
-protected:
- virtual ~Session();
-
-private:
- int32_t mSessionID;
- State mState;
- bool mIsRTSPConnection;
- int mSocket;
- sp<AMessage> mNotify;
- bool mSawReceiveFailure, mSawSendFailure;
-
- // for TCP / stream data
- AString mOutBuffer;
-
- // for UDP / datagrams
- List<sp<ABuffer> > mOutDatagrams;
-
- AString mInBuffer;
-
- void notifyError(bool send, status_t err, const char *detail);
- void notify(NotificationReason reason);
-
- 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> &notify)
- : mSessionID(sessionID),
- mState(state),
- mIsRTSPConnection(false),
- mSocket(s),
- mNotify(notify),
- mSawReceiveFailure(false),
- mSawSendFailure(false) {
- 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 && !mOutBuffer.empty())
- || (mState == DATAGRAM && !mOutDatagrams.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) {
- notifyError(false /* send */, err, "Recvfrom failed.");
- mSawReceiveFailure = true;
- }
-
- 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);
-
- 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;
-}
-
-status_t ANetworkSession::Session::writeMore() {
- if (mState == DATAGRAM) {
- CHECK(!mOutDatagrams.empty());
-
- status_t err;
- do {
- const sp<ABuffer> &datagram = *mOutDatagrams.begin();
-
- int n;
- do {
- n = send(mSocket, datagram->data(), datagram->size(), 0);
- } while (n < 0 && errno == EINTR);
-
- err = OK;
-
- if (n > 0) {
- mOutDatagrams.erase(mOutDatagrams.begin());
- } else if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- }
- } while (err == OK && !mOutDatagrams.empty());
-
- if (err == -EAGAIN) {
- err = OK;
- }
-
- if (err != OK) {
- notifyError(true /* send */, err, "Send datagram failed.");
- mSawSendFailure = true;
- }
-
- 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(!mOutBuffer.empty());
-
- ssize_t n;
- do {
- n = send(mSocket, mOutBuffer.c_str(), mOutBuffer.size(), 0);
- } while (n < 0 && errno == EINTR);
-
- status_t err = OK;
-
- if (n > 0) {
-#if 0
- ALOGI("out:");
- hexdump(mOutBuffer.c_str(), n);
-#endif
-
- mOutBuffer.erase(0, n);
- } else if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- }
-
- if (err != OK) {
- notifyError(true /* send */, err, "Send failed.");
- mSawSendFailure = true;
- }
-
- return err;
-}
-
-status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) {
- CHECK(mState == CONNECTED || mState == DATAGRAM);
-
- if (mState == DATAGRAM) {
- CHECK_GE(size, 0);
-
- sp<ABuffer> datagram = new ABuffer(size);
- memcpy(datagram->data(), data, size);
-
- mOutDatagrams.push_back(datagram);
- return OK;
- }
-
- if (mState == CONNECTED && !mIsRTSPConnection) {
- CHECK_LE(size, 65535);
-
- uint8_t prefix[2];
- prefix[0] = size >> 8;
- prefix[1] = size & 0xff;
-
- mOutBuffer.append((const char *)prefix, sizeof(prefix));
- }
-
- mOutBuffer.append(
- (const char *)data,
- (size >= 0) ? size : strlen((const char *)data));
-
- 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> &notify,
- 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> &notify, int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateRTSPServer,
- &addr,
- port,
- NULL /* remoteHost */,
- 0 /* remotePort */,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
- unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID) {
- return createUDPSession(localPort, NULL, 0, notify, sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> &notify,
- int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateUDPSession,
- NULL /* addr */,
- localPort,
- remoteHost,
- remotePort,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> &notify, int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateTCPDatagramSessionPassive,
- &addr,
- port,
- NULL /* remoteHost */,
- 0 /* remotePort */,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> &notify,
- int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateTCPDatagramSessionActive,
- NULL /* addr */,
- localPort,
- remoteHost,
- remotePort,
- notify,
- sessionID);
-}
-
-status_t 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> &notify,
- int32_t *sessionID) {
- Mutex::Autolock autoLock(mLock);
-
- *sessionID = 0;
- status_t err = OK;
- int s, res;
- sp<Session> session;
-
- s = socket(
- AF_INET,
- (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
- 0);
-
- if (s < 0) {
- err = -errno;
- goto bail;
- }
-
- if (mode == kModeCreateRTSPServer
- || mode == kModeCreateTCPDatagramSessionPassive) {
- const int yes = 1;
- res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- }
-
- if (mode == kModeCreateUDPSession) {
- int size = 256 * 1024;
-
- res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
-
- res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- }
-
- 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) {
- 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);
-
- 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 =
- // using socket sd as sessionID
- 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 c1acdcc..0000000
--- a/media/libstagefright/wifi-display/ANetworkSession.h
+++ /dev/null
@@ -1,130 +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> &notify,
- int32_t *sessionID);
-
- status_t createRTSPServer(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> &notify, int32_t *sessionID);
-
- status_t createUDPSession(
- unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID);
-
- status_t createUDPSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> &notify,
- int32_t *sessionID);
-
- status_t connectUDPSession(
- int32_t sessionID, const char *remoteHost, unsigned remotePort);
-
- // passive
- status_t createTCPDatagramSession(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> &notify, int32_t *sessionID);
-
- // active
- status_t createTCPDatagramSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> &notify,
- int32_t *sessionID);
-
- status_t destroySession(int32_t sessionID);
-
- status_t sendRequest(
- int32_t sessionID, const void *data, ssize_t size = -1);
-
- enum NotificationReason {
- kWhatError,
- kWhatConnected,
- kWhatClientConnected,
- kWhatData,
- kWhatDatagram,
- kWhatBinaryData,
- };
-
-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> &notify,
- 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 611bfff..f70454a 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -3,20 +3,16 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- ANetworkSession.cpp \
+ MediaSender.cpp \
Parameters.cpp \
- ParsedMessage.cpp \
- sink/LinearRegression.cpp \
- sink/RTPSink.cpp \
- sink/TunnelRenderer.cpp \
- sink/WifiDisplaySink.cpp \
+ rtp/RTPSender.cpp \
source/Converter.cpp \
source/MediaPuller.cpp \
source/PlaybackSession.cpp \
source/RepeaterSource.cpp \
- source/Sender.cpp \
source/TSPacketizer.cpp \
source/WifiDisplaySource.cpp \
+ VideoFormats.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/media/libstagefright \
@@ -26,6 +22,7 @@ LOCAL_C_INCLUDES:= \
LOCAL_SHARED_LIBRARIES:= \
libbinder \
libcutils \
+ liblog \
libgui \
libmedia \
libstagefright \
@@ -38,47 +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 \
-
-LOCAL_MODULE:= wfd
-
-LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_EXECUTABLE)
-
-################################################################################
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- udptest.cpp \
-
-LOCAL_SHARED_LIBRARIES:= \
- libbinder \
- libgui \
- libmedia \
- libstagefright \
- libstagefright_foundation \
- libstagefright_wfd \
- libutils \
-
-LOCAL_MODULE:= udptest
-
-LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_EXECUTABLE)
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
new file mode 100644
index 0000000..b1cdec0
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2013, 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 "MediaSender"
+#include <utils/Log.h>
+
+#include "MediaSender.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 {
+
+MediaSender::MediaSender(
+ const sp<ANetworkSession> &netSession,
+ const sp<AMessage> &notify)
+ : mNetSession(netSession),
+ mNotify(notify),
+ mMode(MODE_UNDEFINED),
+ mGeneration(0),
+ mPrevTimeUs(-1ll),
+ mInitDoneCount(0),
+ mLogFile(NULL) {
+ // mLogFile = fopen("/data/misc/log.ts", "wb");
+}
+
+MediaSender::~MediaSender() {
+ if (mLogFile != NULL) {
+ fclose(mLogFile);
+ mLogFile = NULL;
+ }
+}
+
+status_t MediaSender::setHDCP(const sp<IHDCP> &hdcp) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ mHDCP = hdcp;
+
+ return OK;
+}
+
+ssize_t MediaSender::addTrack(const sp<AMessage> &format, uint32_t flags) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ TrackInfo info;
+ info.mFormat = format;
+ info.mFlags = flags;
+ info.mPacketizerTrackIndex = -1;
+
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
+
+ size_t index = mTrackInfos.size();
+ mTrackInfos.push_back(info);
+
+ return index;
+}
+
+status_t MediaSender::initAsync(
+ ssize_t trackIndex,
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ RTPSender::TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ RTPSender::TransportMode rtcpMode,
+ int32_t *localRTPPort) {
+ if (trackIndex < 0) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ uint32_t flags = 0;
+ if (mHDCP != NULL) {
+ // XXX Determine proper HDCP version.
+ flags |= TSPacketizer::EMIT_HDCP20_DESCRIPTOR;
+ }
+ mTSPacketizer = new TSPacketizer(flags);
+
+ status_t err = OK;
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ TrackInfo *info = &mTrackInfos.editItemAt(i);
+
+ ssize_t packetizerTrackIndex =
+ mTSPacketizer->addTrack(info->mFormat);
+
+ if (packetizerTrackIndex < 0) {
+ err = packetizerTrackIndex;
+ break;
+ }
+
+ info->mPacketizerTrackIndex = packetizerTrackIndex;
+ }
+
+ if (err == OK) {
+ sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ notify->setInt32("generation", mGeneration);
+ mTSSender = new RTPSender(mNetSession, notify);
+ looper()->registerHandler(mTSSender);
+
+ err = mTSSender->initAsync(
+ remoteHost,
+ remoteRTPPort,
+ rtpMode,
+ remoteRTCPPort,
+ rtcpMode,
+ localRTPPort);
+
+ if (err != OK) {
+ looper()->unregisterHandler(mTSSender->id());
+ mTSSender.clear();
+ }
+ }
+
+ if (err != OK) {
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ TrackInfo *info = &mTrackInfos.editItemAt(i);
+ info->mPacketizerTrackIndex = -1;
+ }
+
+ mTSPacketizer.clear();
+ return err;
+ }
+
+ mMode = MODE_TRANSPORT_STREAM;
+ mInitDoneCount = 1;
+
+ return OK;
+ }
+
+ if (mMode == MODE_TRANSPORT_STREAM) {
+ return INVALID_OPERATION;
+ }
+
+ if ((size_t)trackIndex >= mTrackInfos.size()) {
+ return -ERANGE;
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+ if (info->mSender != NULL) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ notify->setInt32("generation", mGeneration);
+ notify->setSize("trackIndex", trackIndex);
+
+ info->mSender = new RTPSender(mNetSession, notify);
+ looper()->registerHandler(info->mSender);
+
+ status_t err = info->mSender->initAsync(
+ remoteHost,
+ remoteRTPPort,
+ rtpMode,
+ remoteRTCPPort,
+ rtcpMode,
+ localRTPPort);
+
+ if (err != OK) {
+ looper()->unregisterHandler(info->mSender->id());
+ info->mSender.clear();
+
+ return err;
+ }
+
+ if (mMode == MODE_UNDEFINED) {
+ mInitDoneCount = mTrackInfos.size();
+ }
+
+ mMode = MODE_ELEMENTARY_STREAMS;
+
+ return OK;
+}
+
+status_t MediaSender::queueAccessUnit(
+ size_t trackIndex, const sp<ABuffer> &accessUnit) {
+ if (mMode == MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ if (trackIndex >= mTrackInfos.size()) {
+ return -ERANGE;
+ }
+
+ if (mMode == MODE_TRANSPORT_STREAM) {
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+ info->mAccessUnits.push_back(accessUnit);
+
+ mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex);
+
+ for (;;) {
+ ssize_t minTrackIndex = -1;
+ int64_t minTimeUs = -1ll;
+
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ const TrackInfo &info = mTrackInfos.itemAt(i);
+
+ if (info.mAccessUnits.empty()) {
+ minTrackIndex = -1;
+ minTimeUs = -1ll;
+ break;
+ }
+
+ int64_t timeUs;
+ const sp<ABuffer> &accessUnit = *info.mAccessUnits.begin();
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ if (minTrackIndex < 0 || timeUs < minTimeUs) {
+ minTrackIndex = i;
+ minTimeUs = timeUs;
+ }
+ }
+
+ if (minTrackIndex < 0) {
+ return OK;
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex);
+ sp<ABuffer> accessUnit = *info->mAccessUnits.begin();
+ info->mAccessUnits.erase(info->mAccessUnits.begin());
+
+ sp<ABuffer> tsPackets;
+ status_t err = packetizeAccessUnit(
+ minTrackIndex, accessUnit, &tsPackets);
+
+ if (err == OK) {
+ if (mLogFile != NULL) {
+ fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
+ }
+
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ tsPackets->meta()->setInt64("timeUs", timeUs);
+
+ err = mTSSender->queueBuffer(
+ tsPackets,
+ 33 /* packetType */,
+ RTPSender::PACKETIZATION_TRANSPORT_STREAM);
+ }
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+ return info->mSender->queueBuffer(
+ accessUnit,
+ info->mIsAudio ? 96 : 97 /* packetType */,
+ info->mIsAudio
+ ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264);
+}
+
+void MediaSender::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSenderNotify:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mGeneration) {
+ break;
+ }
+
+ onSenderNotify(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void MediaSender::onSenderNotify(const sp<AMessage> &msg) {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ switch (what) {
+ case RTPSender::kWhatInitDone:
+ {
+ --mInitDoneCount;
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ if (err != OK) {
+ notifyInitDone(err);
+ ++mGeneration;
+ break;
+ }
+
+ if (mInitDoneCount == 0) {
+ notifyInitDone(OK);
+ }
+ break;
+ }
+
+ case RTPSender::kWhatError:
+ {
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ notifyError(err);
+ break;
+ }
+
+ case kWhatNetworkStall:
+ {
+ size_t numBytesQueued;
+ CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
+
+ notifyNetworkStall(numBytesQueued);
+ 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();
+ }
+}
+
+void MediaSender::notifyInitDone(status_t err) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatInitDone);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void MediaSender::notifyError(status_t err) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void MediaSender::notifyNetworkStall(size_t numBytesQueued) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatNetworkStall);
+ notify->setSize("numBytesQueued", numBytesQueued);
+ notify->post();
+}
+
+status_t MediaSender::packetizeAccessUnit(
+ size_t trackIndex,
+ sp<ABuffer> accessUnit,
+ sp<ABuffer> *tsPackets) {
+ const TrackInfo &info = mTrackInfos.itemAt(trackIndex);
+
+ uint32_t flags = 0;
+
+ bool isHDCPEncrypted = false;
+ uint64_t inputCTR;
+ uint8_t HDCP_private_data[16];
+
+ bool manuallyPrependSPSPPS =
+ !info.mIsAudio
+ && (info.mFlags & FLAG_MANUALLY_PREPEND_SPS_PPS)
+ && IsIDR(accessUnit);
+
+ if (mHDCP != NULL && !info.mIsAudio) {
+ isHDCPEncrypted = true;
+
+ if (manuallyPrependSPSPPS) {
+ accessUnit = mTSPacketizer->prependCSD(
+ info.mPacketizerTrackIndex, accessUnit);
+ }
+
+ 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", &notify)
+ && 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)",
+ err);
+
+ return err;
+ }
+
+ HDCP_private_data[0] = 0x00;
+
+ HDCP_private_data[1] =
+ (((trackIndex >> 30) & 3) << 1) | 1;
+
+ HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
+
+ HDCP_private_data[3] =
+ (((trackIndex >> 15) & 0x7f) << 1) | 1;
+
+ HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
+
+ HDCP_private_data[5] =
+ ((trackIndex & 0x7f) << 1) | 1;
+
+ HDCP_private_data[6] = 0x00;
+
+ HDCP_private_data[7] =
+ (((inputCTR >> 60) & 0x0f) << 1) | 1;
+
+ HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
+
+ HDCP_private_data[9] =
+ (((inputCTR >> 45) & 0x7f) << 1) | 1;
+
+ HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
+
+ HDCP_private_data[11] =
+ (((inputCTR >> 30) & 0x7f) << 1) | 1;
+
+ HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
+
+ HDCP_private_data[13] =
+ (((inputCTR >> 15) & 0x7f) << 1) | 1;
+
+ HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
+
+ HDCP_private_data[15] =
+ ((inputCTR & 0x7f) << 1) | 1;
+
+ flags |= TSPacketizer::IS_ENCRYPTED;
+ } else if (manuallyPrependSPSPPS) {
+ flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
+ }
+
+ int64_t timeUs = ALooper::GetNowUs();
+ if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
+ flags |= TSPacketizer::EMIT_PCR;
+ flags |= TSPacketizer::EMIT_PAT_AND_PMT;
+
+ mPrevTimeUs = timeUs;
+ }
+
+ mTSPacketizer->packetize(
+ info.mPacketizerTrackIndex,
+ accessUnit,
+ tsPackets,
+ flags,
+ !isHDCPEncrypted ? NULL : HDCP_private_data,
+ !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
+ info.mIsAudio ? 2 : 0 /* numStuffingBytes */);
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/wifi-display/MediaSender.h b/media/libstagefright/wifi-display/MediaSender.h
new file mode 100644
index 0000000..04538ea
--- /dev/null
+++ b/media/libstagefright/wifi-display/MediaSender.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2013, 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 MEDIA_SENDER_H_
+
+#define MEDIA_SENDER_H_
+
+#include "rtp/RTPSender.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+struct AMessage;
+struct IHDCP;
+struct TSPacketizer;
+
+// This class facilitates sending of data from one or more media tracks
+// through one or more RTP channels, either providing a 1:1 mapping from
+// track to RTP channel or muxing all tracks into a single RTP channel and
+// using transport stream encapsulation.
+// Optionally the (video) data is encrypted using the provided hdcp object.
+struct MediaSender : public AHandler {
+ enum {
+ kWhatInitDone,
+ kWhatError,
+ kWhatNetworkStall,
+ kWhatInformSender,
+ };
+
+ MediaSender(
+ const sp<ANetworkSession> &netSession,
+ const sp<AMessage> &notify);
+
+ status_t setHDCP(const sp<IHDCP> &hdcp);
+
+ enum FlagBits {
+ FLAG_MANUALLY_PREPEND_SPS_PPS = 1,
+ };
+ ssize_t addTrack(const sp<AMessage> &format, uint32_t flags);
+
+ // If trackIndex == -1, initialize for transport stream muxing.
+ status_t initAsync(
+ ssize_t trackIndex,
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ RTPSender::TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ RTPSender::TransportMode rtcpMode,
+ int32_t *localRTPPort);
+
+ status_t queueAccessUnit(
+ size_t trackIndex, const sp<ABuffer> &accessUnit);
+
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual ~MediaSender();
+
+private:
+ enum {
+ kWhatSenderNotify,
+ };
+
+ enum Mode {
+ MODE_UNDEFINED,
+ MODE_TRANSPORT_STREAM,
+ MODE_ELEMENTARY_STREAMS,
+ };
+
+ struct TrackInfo {
+ sp<AMessage> mFormat;
+ uint32_t mFlags;
+ sp<RTPSender> mSender;
+ List<sp<ABuffer> > mAccessUnits;
+ ssize_t mPacketizerTrackIndex;
+ bool mIsAudio;
+ };
+
+ sp<ANetworkSession> mNetSession;
+ sp<AMessage> mNotify;
+
+ sp<IHDCP> mHDCP;
+
+ Mode mMode;
+ int32_t mGeneration;
+
+ Vector<TrackInfo> mTrackInfos;
+
+ sp<TSPacketizer> mTSPacketizer;
+ sp<RTPSender> mTSSender;
+ int64_t mPrevTimeUs;
+
+ size_t mInitDoneCount;
+
+ FILE *mLogFile;
+
+ void onSenderNotify(const sp<AMessage> &msg);
+
+ void notifyInitDone(status_t err);
+ void notifyError(status_t err);
+ void notifyNetworkStall(size_t numBytesQueued);
+
+ status_t packetizeAccessUnit(
+ size_t trackIndex,
+ sp<ABuffer> accessUnit,
+ sp<ABuffer> *tsPackets);
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaSender);
+};
+
+} // namespace android
+
+#endif // MEDIA_SENDER_H_
+
diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp
index f7118b3..d2a61ea 100644
--- a/media/libstagefright/wifi-display/Parameters.cpp
+++ b/media/libstagefright/wifi-display/Parameters.cpp
@@ -65,7 +65,9 @@ status_t Parameters::parse(const char *data, size_t size) {
mDict.add(name, value);
- i += 2;
+ while (i + 1 < size && data[i] == '\r' && data[i + 1] == '\n') {
+ i += 2;
+ }
}
return OK;
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
new file mode 100644
index 0000000..04e02c1
--- /dev/null
+++ b/media/libstagefright/wifi-display/VideoFormats.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2013, 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 "VideoFormats"
+#include <utils/Log.h>
+
+#include "VideoFormats.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+// static
+const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = {
+ {
+ // CEA Resolutions
+ { 640, 480, 60, false, 0, 0},
+ { 720, 480, 60, false, 0, 0},
+ { 720, 480, 60, true, 0, 0},
+ { 720, 576, 50, false, 0, 0},
+ { 720, 576, 50, true, 0, 0},
+ { 1280, 720, 30, false, 0, 0},
+ { 1280, 720, 60, false, 0, 0},
+ { 1920, 1080, 30, false, 0, 0},
+ { 1920, 1080, 60, false, 0, 0},
+ { 1920, 1080, 60, true, 0, 0},
+ { 1280, 720, 25, false, 0, 0},
+ { 1280, 720, 50, false, 0, 0},
+ { 1920, 1080, 25, false, 0, 0},
+ { 1920, 1080, 50, false, 0, 0},
+ { 1920, 1080, 50, true, 0, 0},
+ { 1280, 720, 24, false, 0, 0},
+ { 1920, 1080, 24, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ },
+ {
+ // VESA Resolutions
+ { 800, 600, 30, false, 0, 0},
+ { 800, 600, 60, false, 0, 0},
+ { 1024, 768, 30, false, 0, 0},
+ { 1024, 768, 60, false, 0, 0},
+ { 1152, 864, 30, false, 0, 0},
+ { 1152, 864, 60, false, 0, 0},
+ { 1280, 768, 30, false, 0, 0},
+ { 1280, 768, 60, false, 0, 0},
+ { 1280, 800, 30, false, 0, 0},
+ { 1280, 800, 60, false, 0, 0},
+ { 1360, 768, 30, false, 0, 0},
+ { 1360, 768, 60, false, 0, 0},
+ { 1366, 768, 30, false, 0, 0},
+ { 1366, 768, 60, false, 0, 0},
+ { 1280, 1024, 30, false, 0, 0},
+ { 1280, 1024, 60, false, 0, 0},
+ { 1400, 1050, 30, false, 0, 0},
+ { 1400, 1050, 60, false, 0, 0},
+ { 1440, 900, 30, false, 0, 0},
+ { 1440, 900, 60, false, 0, 0},
+ { 1600, 900, 30, false, 0, 0},
+ { 1600, 900, 60, false, 0, 0},
+ { 1600, 1200, 30, false, 0, 0},
+ { 1600, 1200, 60, false, 0, 0},
+ { 1680, 1024, 30, false, 0, 0},
+ { 1680, 1024, 60, false, 0, 0},
+ { 1680, 1050, 30, false, 0, 0},
+ { 1680, 1050, 60, false, 0, 0},
+ { 1920, 1200, 30, false, 0, 0},
+ { 1920, 1200, 60, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ },
+ {
+ // HH Resolutions
+ { 800, 480, 30, false, 0, 0},
+ { 800, 480, 60, false, 0, 0},
+ { 854, 480, 30, false, 0, 0},
+ { 854, 480, 60, false, 0, 0},
+ { 864, 480, 30, false, 0, 0},
+ { 864, 480, 60, false, 0, 0},
+ { 640, 360, 30, false, 0, 0},
+ { 640, 360, 60, false, 0, 0},
+ { 960, 540, 30, false, 0, 0},
+ { 960, 540, 60, false, 0, 0},
+ { 848, 480, 30, false, 0, 0},
+ { 848, 480, 60, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ { 0, 0, 0, false, 0, 0},
+ }
+};
+
+VideoFormats::VideoFormats() {
+ memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
+
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0;
+ }
+
+ setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
+}
+
+void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
+ CHECK_LT(type, kNumResolutionTypes);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ mNativeType = type;
+ mNativeIndex = index;
+
+ setResolutionEnabled(type, index);
+}
+
+void VideoFormats::getNativeResolution(
+ ResolutionType *type, size_t *index) const {
+ *type = mNativeType;
+ *index = mNativeIndex;
+}
+
+void VideoFormats::disableAll() {
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0;
+ for (size_t j = 0; j < 32; j++) {
+ mConfigs[i][j].profile = mConfigs[i][j].level = 0;
+ }
+ }
+}
+
+void VideoFormats::enableAll() {
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ mResolutionEnabled[i] = 0xffffffff;
+ for (size_t j = 0; j < 32; j++) {
+ mConfigs[i][j].profile = (1ul << PROFILE_CBP);
+ mConfigs[i][j].level = (1ul << LEVEL_31);
+ }
+ }
+}
+
+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);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ 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);
+ CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
+
+ return mResolutionEnabled[type] & (1ul << index);
+}
+
+// static
+bool VideoFormats::GetConfiguration(
+ ResolutionType type,
+ size_t index,
+ size_t *width, size_t *height, size_t *framesPerSecond,
+ bool *interlaced) {
+ CHECK_LT(type, kNumResolutionTypes);
+
+ if (index >= 32) {
+ return false;
+ }
+
+ const config_t *config = &mResolutionTable[type][index];
+
+ if (config->width == 0) {
+ return false;
+ }
+
+ if (width) {
+ *width = config->width;
+ }
+
+ if (height) {
+ *height = config->height;
+ }
+
+ if (framesPerSecond) {
+ *framesPerSecond = config->framesPerSecond;
+ }
+
+ if (interlaced) {
+ *interlaced = config->interlaced;
+ }
+
+ return true;
+}
+
+bool VideoFormats::parseH264Codec(const char *spec) {
+ unsigned profile, level, res[3];
+
+ if (sscanf(
+ spec,
+ "%02x %02x %08X %08X %08X",
+ &profile,
+ &level,
+ &res[0],
+ &res[1],
+ &res[2]) != 5) {
+ return false;
+ }
+
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ for (size_t j = 0; j < 32; ++j) {
+ 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;
+ mConfigs[i][j].level = level;
+ } else if (profile == mConfigs[i][j].profile &&
+ level > mConfigs[i][j].level) {
+ mConfigs[i][j].level = level;
+ }
+ }
+ }
+ }
+
+ 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);
+ size_t offset = 0;
+
+ if (sscanf(spec, "%02x %02x ", &native, &dummy) != 2) {
+ return false;
+ }
+
+ offset += 6; // skip native and preferred-display-mode-supported
+ CHECK_LE(offset + 58, size);
+ while (offset < size) {
+ parseH264Codec(spec + offset);
+ offset += 60; // skip H.264-codec + ", "
+ }
+
+ mNativeIndex = native >> 3;
+ mNativeType = (ResolutionType)(native & 7);
+
+ bool success;
+ if (mNativeType >= kNumResolutionTypes) {
+ success = false;
+ } else {
+ success = GetConfiguration(
+ mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
+ }
+
+ if (!success) {
+ ALOGW("sink advertised an illegal native resolution, fortunately "
+ "this value is ignored for the time being...");
+ }
+
+ return true;
+}
+
+AString VideoFormats::getFormatSpec(bool forM4Message) const {
+ CHECK_EQ(kNumResolutionTypes, 3);
+
+ // wfd_video_formats:
+ // 1 byte "native"
+ // 1 byte "preferred-display-mode-supported" 0 or 1
+ // one or more avc codec structures
+ // 1 byte profile
+ // 1 byte level
+ // 4 byte CEA mask
+ // 4 byte VESA mask
+ // 4 byte HH mask
+ // 1 byte latency
+ // 2 byte min-slice-slice
+ // 2 byte slice-enc-params
+ // 1 byte framerate-control-support
+ // max-hres (none or 2 byte)
+ // max-vres (none or 2 byte)
+
+ return StringPrintf(
+ "%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]);
+}
+
+// static
+bool VideoFormats::PickBestFormat(
+ const VideoFormats &sinkSupported,
+ const VideoFormats &sourceSupported,
+ ResolutionType *chosenType,
+ 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.
+
+ ResolutionType nativeType;
+ size_t nativeIndex;
+ sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
+ if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ ALOGI("Choosing sink's native resolution");
+ *chosenType = nativeType;
+ *chosenIndex = nativeIndex;
+ return true;
+ }
+ } else {
+ ALOGW("Sink advertised native resolution that it doesn't "
+ "actually support... ignoring");
+ }
+
+ sourceSupported.getNativeResolution(&nativeType, &nativeIndex);
+ if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
+ ALOGI("Choosing source's native resolution");
+ *chosenType = nativeType;
+ *chosenIndex = nativeIndex;
+ return true;
+ }
+ } else {
+ ALOGW("Source advertised native resolution that it doesn't "
+ "actually support... ignoring");
+ }
+#endif
+
+ bool first = true;
+ uint32_t bestScore = 0;
+ size_t bestType = 0;
+ size_t bestIndex = 0;
+ for (size_t i = 0; i < kNumResolutionTypes; ++i) {
+ for (size_t j = 0; j < 32; ++j) {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ if (!GetConfiguration(
+ (ResolutionType)i,
+ j,
+ &width, &height, &framesPerSecond, &interlaced)) {
+ break;
+ }
+
+ if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j)
+ || !sourceSupported.isResolutionEnabled(
+ (ResolutionType)i, j)) {
+ continue;
+ }
+
+ ALOGV("type %u, index %u, %u x %u %c%u supported",
+ i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
+
+ uint32_t score = width * height * framesPerSecond;
+ if (!interlaced) {
+ score *= 2;
+ }
+
+ if (first || score > bestScore) {
+ bestScore = score;
+ bestType = i;
+ bestIndex = j;
+
+ first = false;
+ }
+ }
+ }
+
+ if (first) {
+ return false;
+ }
+
+ *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;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/wifi-display/VideoFormats.h b/media/libstagefright/wifi-display/VideoFormats.h
new file mode 100644
index 0000000..fd38fd1
--- /dev/null
+++ b/media/libstagefright/wifi-display/VideoFormats.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2013, 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 VIDEO_FORMATS_H_
+
+#define VIDEO_FORMATS_H_
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct AString;
+
+// This class encapsulates that video resolution capabilities of a wfd source
+// or sink as outlined in the wfd specs. Currently three sets of resolutions
+// are specified, each of which supports up to 32 resolutions.
+// In addition to its capabilities each sink/source also publishes its
+// "native" resolution, presumably one that is preferred among all others
+// because it wouldn't require any scaling and directly corresponds to the
+// display capabilities/pixels.
+struct VideoFormats {
+ VideoFormats();
+
+ struct config_t {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ unsigned char profile, level;
+ };
+
+ enum ProfileType {
+ PROFILE_CBP = 0,
+ PROFILE_CHP,
+ kNumProfileTypes,
+ };
+
+ enum LevelType {
+ LEVEL_31 = 0,
+ LEVEL_32,
+ LEVEL_40,
+ LEVEL_41,
+ LEVEL_42,
+ kNumLevelTypes,
+ };
+
+ enum ResolutionType {
+ RESOLUTION_CEA,
+ RESOLUTION_VESA,
+ RESOLUTION_HH,
+ kNumResolutionTypes,
+ };
+
+ void setNativeResolution(ResolutionType type, size_t index);
+ void getNativeResolution(ResolutionType *type, size_t *index) const;
+
+ 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;
+
+ static bool PickBestFormat(
+ const VideoFormats &sinkSupported,
+ const VideoFormats &sourceSupported,
+ ResolutionType *chosenType,
+ size_t *chosenIndex,
+ ProfileType *chosenProfile,
+ LevelType *chosenLevel);
+
+private:
+ bool parseH264Codec(const char *spec);
+ ResolutionType mNativeType;
+ size_t mNativeIndex;
+
+ uint32_t mResolutionEnabled[kNumResolutionTypes];
+ static const config_t mResolutionTable[kNumResolutionTypes][32];
+ config_t mConfigs[kNumResolutionTypes][32];
+
+ DISALLOW_EVIL_CONSTRUCTORS(VideoFormats);
+};
+
+} // namespace android
+
+#endif // VIDEO_FORMATS_H_
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPBase.h b/media/libstagefright/wifi-display/rtp/RTPBase.h
new file mode 100644
index 0000000..6178f00
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPBase.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013, 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_BASE_H_
+
+#define RTP_BASE_H_
+
+namespace android {
+
+struct RTPBase {
+ enum PacketizationMode {
+ PACKETIZATION_TRANSPORT_STREAM,
+ PACKETIZATION_H264,
+ PACKETIZATION_AAC,
+ PACKETIZATION_NONE,
+ };
+
+ enum TransportMode {
+ TRANSPORT_UNDEFINED,
+ TRANSPORT_NONE,
+ TRANSPORT_UDP,
+ TRANSPORT_TCP,
+ TRANSPORT_TCP_INTERLEAVED,
+ };
+
+ enum {
+ // Really UDP _payload_ size
+ kMaxUDPPacketSize = 1472, // 1472 good, 1473 bad on Android@Home
+ };
+
+ static int32_t PickRandomRTPPort();
+};
+
+} // namespace android
+
+#endif // RTP_BASE_H_
+
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
new file mode 100644
index 0000000..1887b8b
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -0,0 +1,805 @@
+/*
+ * Copyright 2013, 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 "RTPSender"
+#include <utils/Log.h>
+
+#include "RTPSender.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>
+
+#include "include/avc_utils.h"
+
+namespace android {
+
+RTPSender::RTPSender(
+ const sp<ANetworkSession> &netSession,
+ const sp<AMessage> &notify)
+ : mNetSession(netSession),
+ mNotify(notify),
+ mRTPMode(TRANSPORT_UNDEFINED),
+ mRTCPMode(TRANSPORT_UNDEFINED),
+ mRTPSessionID(0),
+ mRTCPSessionID(0),
+ mRTPConnected(false),
+ mRTCPConnected(false),
+ mLastNTPTime(0),
+ mLastRTPTime(0),
+ mNumRTPSent(0),
+ mNumRTPOctetsSent(0),
+ mNumSRsSent(0),
+ mRTPSeqNo(0),
+ mHistorySize(0) {
+}
+
+RTPSender::~RTPSender() {
+ if (mRTCPSessionID != 0) {
+ mNetSession->destroySession(mRTCPSessionID);
+ mRTCPSessionID = 0;
+ }
+
+ if (mRTPSessionID != 0) {
+ mNetSession->destroySession(mRTPSessionID);
+ mRTPSessionID = 0;
+ }
+}
+
+// static
+int32_t RTPBase::PickRandomRTPPort() {
+ // Pick an even integer in range [1024, 65534)
+
+ static const size_t kRange = (65534 - 1024) / 2;
+
+ return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024;
+}
+
+status_t RTPSender::initAsync(
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ TransportMode rtcpMode,
+ int32_t *outLocalRTPPort) {
+ if (mRTPMode != TRANSPORT_UNDEFINED
+ || rtpMode == TRANSPORT_UNDEFINED
+ || rtpMode == TRANSPORT_NONE
+ || rtcpMode == TRANSPORT_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED);
+ CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED);
+
+ if ((rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0)
+ || (rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0)) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+ sp<AMessage> rtcpNotify;
+ if (remoteRTCPPort >= 0) {
+ rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+ }
+
+ CHECK_EQ(mRTPSessionID, 0);
+ CHECK_EQ(mRTCPSessionID, 0);
+
+ int32_t localRTPPort;
+
+ for (;;) {
+ localRTPPort = PickRandomRTPPort();
+
+ status_t err;
+ if (rtpMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ localRTPPort,
+ remoteHost,
+ remoteRTPPort,
+ rtpNotify,
+ &mRTPSessionID);
+ } else {
+ CHECK_EQ(rtpMode, TRANSPORT_TCP);
+ err = mNetSession->createTCPDatagramSession(
+ localRTPPort,
+ remoteHost,
+ remoteRTPPort,
+ rtpNotify,
+ &mRTPSessionID);
+ }
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (remoteRTCPPort < 0) {
+ break;
+ }
+
+ if (rtcpMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ localRTPPort + 1,
+ remoteHost,
+ remoteRTCPPort,
+ rtcpNotify,
+ &mRTCPSessionID);
+ } else {
+ CHECK_EQ(rtcpMode, TRANSPORT_TCP);
+ err = mNetSession->createTCPDatagramSession(
+ localRTPPort + 1,
+ remoteHost,
+ remoteRTCPPort,
+ rtcpNotify,
+ &mRTCPSessionID);
+ }
+
+ if (err == OK) {
+ break;
+ }
+
+ mNetSession->destroySession(mRTPSessionID);
+ mRTPSessionID = 0;
+ }
+
+ if (rtpMode == TRANSPORT_UDP) {
+ mRTPConnected = true;
+ }
+
+ if (rtcpMode == TRANSPORT_UDP) {
+ mRTCPConnected = true;
+ }
+
+ mRTPMode = rtpMode;
+ mRTCPMode = rtcpMode;
+ *outLocalRTPPort = localRTPPort;
+
+ if (mRTPMode == TRANSPORT_UDP
+ && (mRTCPMode == TRANSPORT_UDP || mRTCPMode == TRANSPORT_NONE)) {
+ notifyInitDone(OK);
+ }
+
+ return OK;
+}
+
+status_t RTPSender::queueBuffer(
+ const sp<ABuffer> &buffer, uint8_t packetType, PacketizationMode mode) {
+ status_t err;
+
+ switch (mode) {
+ case PACKETIZATION_NONE:
+ err = queueRawPacket(buffer, packetType);
+ break;
+
+ case PACKETIZATION_TRANSPORT_STREAM:
+ err = queueTSPackets(buffer, packetType);
+ break;
+
+ case PACKETIZATION_H264:
+ err = queueAVCBuffer(buffer, packetType);
+ break;
+
+ default:
+ TRESPASS();
+ }
+
+ return err;
+}
+
+status_t RTPSender::queueRawPacket(
+ const sp<ABuffer> &packet, uint8_t packetType) {
+ CHECK_LE(packet->size(), kMaxUDPPacketSize - 12);
+
+ int64_t timeUs;
+ CHECK(packet->meta()->findInt64("timeUs", &timeUs));
+
+ sp<ABuffer> udpPacket = new ABuffer(12 + packet->size());
+
+ udpPacket->setInt32Data(mRTPSeqNo);
+
+ uint8_t *rtp = udpPacket->data();
+ rtp[0] = 0x80;
+ rtp[1] = packetType;
+
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ uint32_t rtpTime = (timeUs * 9) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ memcpy(&rtp[12], packet->data(), packet->size());
+
+ return sendRTPPacket(
+ udpPacket,
+ true /* storeInHistory */,
+ true /* timeValid */,
+ ALooper::GetNowUs());
+}
+
+status_t RTPSender::queueTSPackets(
+ const sp<ABuffer> &tsPackets, uint8_t packetType) {
+ CHECK_EQ(0, tsPackets->size() % 188);
+
+ int64_t timeUs;
+ CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
+
+ const size_t numTSPackets = tsPackets->size() / 188;
+
+ size_t srcOffset = 0;
+ while (srcOffset < tsPackets->size()) {
+ sp<ABuffer> udpPacket =
+ new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+
+ udpPacket->setInt32Data(mRTPSeqNo);
+
+ uint8_t *rtp = udpPacket->data();
+ rtp[0] = 0x80;
+ rtp[1] = packetType;
+
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ int64_t nowUs = ALooper::GetNowUs();
+ uint32_t rtpTime = (nowUs * 9) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ size_t numTSPackets = (tsPackets->size() - srcOffset) / 188;
+ if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) {
+ numTSPackets = kMaxNumTSPacketsPerRTPPacket;
+ }
+
+ memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188);
+
+ udpPacket->setRange(0, 12 + numTSPackets * 188);
+
+ srcOffset += numTSPackets * 188;
+ bool isLastPacket = (srcOffset == tsPackets->size());
+
+ status_t err = sendRTPPacket(
+ udpPacket,
+ true /* storeInHistory */,
+ isLastPacket /* timeValid */,
+ timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t RTPSender::queueAVCBuffer(
+ const sp<ABuffer> &accessUnit, uint8_t packetType) {
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ uint32_t rtpTime = (timeUs * 9 / 100ll);
+
+ List<sp<ABuffer> > packets;
+
+ sp<ABuffer> out = new ABuffer(kMaxUDPPacketSize);
+ size_t outBytesUsed = 12; // Placeholder for RTP header.
+
+ const uint8_t *data = accessUnit->data();
+ size_t size = accessUnit->size();
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(
+ &data, &size, &nalStart, &nalSize,
+ true /* startCodeFollows */) == OK) {
+ size_t bytesNeeded = nalSize + 2;
+ if (outBytesUsed == 12) {
+ ++bytesNeeded;
+ }
+
+ if (outBytesUsed + bytesNeeded > out->capacity()) {
+ bool emitSingleNALPacket = false;
+
+ if (outBytesUsed == 12
+ && outBytesUsed + nalSize <= out->capacity()) {
+ // We haven't emitted anything into the current packet yet and
+ // this NAL unit fits into a single-NAL-unit-packet while
+ // it wouldn't have fit as part of a STAP-A packet.
+
+ memcpy(out->data() + outBytesUsed, nalStart, nalSize);
+ outBytesUsed += nalSize;
+
+ emitSingleNALPacket = true;
+ }
+
+ if (outBytesUsed > 12) {
+ out->setRange(0, outBytesUsed);
+ packets.push_back(out);
+ out = new ABuffer(kMaxUDPPacketSize);
+ outBytesUsed = 12; // Placeholder for RTP header
+ }
+
+ if (emitSingleNALPacket) {
+ continue;
+ }
+ }
+
+ if (outBytesUsed + bytesNeeded <= out->capacity()) {
+ uint8_t *dst = out->data() + outBytesUsed;
+
+ if (outBytesUsed == 12) {
+ *dst++ = 24; // STAP-A header
+ }
+
+ *dst++ = (nalSize >> 8) & 0xff;
+ *dst++ = nalSize & 0xff;
+ memcpy(dst, nalStart, nalSize);
+
+ outBytesUsed += bytesNeeded;
+ continue;
+ }
+
+ // This single NAL unit does not fit into a single RTP packet,
+ // we need to emit an FU-A.
+
+ CHECK_EQ(outBytesUsed, 12u);
+
+ uint8_t nalType = nalStart[0] & 0x1f;
+ uint8_t nri = (nalStart[0] >> 5) & 3;
+
+ size_t srcOffset = 1;
+ while (srcOffset < nalSize) {
+ size_t copy = out->capacity() - outBytesUsed - 2;
+ if (copy > nalSize - srcOffset) {
+ copy = nalSize - srcOffset;
+ }
+
+ uint8_t *dst = out->data() + outBytesUsed;
+ dst[0] = (nri << 5) | 28;
+
+ dst[1] = nalType;
+
+ if (srcOffset == 1) {
+ dst[1] |= 0x80;
+ }
+
+ if (srcOffset + copy == nalSize) {
+ dst[1] |= 0x40;
+ }
+
+ memcpy(&dst[2], nalStart + srcOffset, copy);
+ srcOffset += copy;
+
+ out->setRange(0, outBytesUsed + copy + 2);
+
+ packets.push_back(out);
+ out = new ABuffer(kMaxUDPPacketSize);
+ outBytesUsed = 12; // Placeholder for RTP header
+ }
+ }
+
+ if (outBytesUsed > 12) {
+ out->setRange(0, outBytesUsed);
+ packets.push_back(out);
+ }
+
+ while (!packets.empty()) {
+ sp<ABuffer> out = *packets.begin();
+ packets.erase(packets.begin());
+
+ out->setInt32Data(mRTPSeqNo);
+
+ bool last = packets.empty();
+
+ uint8_t *dst = out->data();
+
+ dst[0] = 0x80;
+
+ dst[1] = packetType;
+ if (last) {
+ dst[1] |= 1 << 7; // M-bit
+ }
+
+ dst[2] = (mRTPSeqNo >> 8) & 0xff;
+ dst[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ dst[4] = rtpTime >> 24;
+ dst[5] = (rtpTime >> 16) & 0xff;
+ dst[6] = (rtpTime >> 8) & 0xff;
+ dst[7] = rtpTime & 0xff;
+ dst[8] = kSourceID >> 24;
+ dst[9] = (kSourceID >> 16) & 0xff;
+ dst[10] = (kSourceID >> 8) & 0xff;
+ dst[11] = kSourceID & 0xff;
+
+ status_t err = sendRTPPacket(out, true /* storeInHistory */);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t RTPSender::sendRTPPacket(
+ const sp<ABuffer> &buffer, bool storeInHistory,
+ bool timeValid, int64_t timeUs) {
+ CHECK(mRTPConnected);
+
+ status_t err = mNetSession->sendRequest(
+ mRTPSessionID, buffer->data(), buffer->size(),
+ timeValid, timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mLastNTPTime = GetNowNTP();
+ mLastRTPTime = U32_AT(buffer->data() + 4);
+
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+
+ if (storeInHistory) {
+ if (mHistorySize == kMaxHistorySize) {
+ mHistory.erase(mHistory.begin());
+ } else {
+ ++mHistorySize;
+ }
+ mHistory.push_back(buffer);
+ }
+
+ return OK;
+}
+
+// static
+uint64_t RTPSender::GetNowNTP() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL /* timezone */);
+
+ uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+ nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+ uint64_t hi = nowUs / 1000000ll;
+ uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+ return (hi << 32) | lo;
+}
+
+void RTPSender::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRTPNotify:
+ case kWhatRTCPNotify:
+ onNetNotify(msg->what() == kWhatRTPNotify, msg);
+ break;
+
+ default:
+ TRESPASS();
+ }
+}
+
+void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ switch (reason) {
+ case ANetworkSession::kWhatError:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ int32_t errorOccuredDuringSend;
+ CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+ AString detail;
+ CHECK(msg->findString("detail", &detail));
+
+ ALOGE("An error occurred during %s in session %d "
+ "(%d, '%s' (%s)).",
+ errorOccuredDuringSend ? "send" : "receive",
+ sessionID,
+ err,
+ detail.c_str(),
+ strerror(-err));
+
+ mNetSession->destroySession(sessionID);
+
+ if (sessionID == mRTPSessionID) {
+ mRTPSessionID = 0;
+ } else if (sessionID == mRTCPSessionID) {
+ mRTCPSessionID = 0;
+ }
+
+ if (!mRTPConnected
+ || (mRTPMode != TRANSPORT_NONE && !mRTCPConnected)) {
+ // We haven't completed initialization, attach the error
+ // to the notification instead.
+ notifyInitDone(err);
+ break;
+ }
+
+ notifyError(err);
+ break;
+ }
+
+ case ANetworkSession::kWhatDatagram:
+ {
+ sp<ABuffer> data;
+ CHECK(msg->findBuffer("data", &data));
+
+ if (isRTP) {
+ ALOGW("Huh? Received data on RTP connection...");
+ } else {
+ onRTCPData(data);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatConnected:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ if (isRTP) {
+ CHECK_EQ(mRTPMode, TRANSPORT_TCP);
+ CHECK_EQ(sessionID, mRTPSessionID);
+ mRTPConnected = true;
+ } else {
+ CHECK_EQ(mRTCPMode, TRANSPORT_TCP);
+ CHECK_EQ(sessionID, mRTCPSessionID);
+ mRTCPConnected = true;
+ }
+
+ if (mRTPConnected
+ && (mRTCPMode == TRANSPORT_NONE || mRTCPConnected)) {
+ notifyInitDone(OK);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatNetworkStall:
+ {
+ size_t numBytesQueued;
+ CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
+
+ notifyNetworkStall(numBytesQueued);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+status_t RTPSender::onRTCPData(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:
+ case 201: // RR
+ parseReceiverReport(data, headerLength);
+ break;
+
+ case 202: // SDES
+ case 203:
+ break;
+
+ case 204: // APP
+ parseAPP(data, headerLength);
+ break;
+
+ case 205: // TSFB (transport layer specific feedback)
+ parseTSFB(data, headerLength);
+ break;
+
+ case 206: // PSFB (payload specific feedback)
+ // hexdump(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 RTPSender::parseReceiverReport(const uint8_t *data, size_t size) {
+ // hexdump(data, size);
+
+ float fractionLost = data[12] / 256.0f;
+
+ ALOGI("lost %.2f %% of packets during report interval.",
+ 100.0f * fractionLost);
+
+ return OK;
+}
+
+status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) {
+ if ((data[0] & 0x1f) != 1) {
+ return ERROR_UNSUPPORTED; // We only support NACK for now.
+ }
+
+ uint32_t srcId = U32_AT(&data[8]);
+ if (srcId != kSourceID) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = 12; i < size; i += 4) {
+ uint16_t seqNo = U16_AT(&data[i]);
+ uint16_t blp = U16_AT(&data[i + 2]);
+
+ List<sp<ABuffer> >::iterator it = mHistory.begin();
+ bool foundSeqNo = false;
+ while (it != mHistory.end()) {
+ const sp<ABuffer> &buffer = *it;
+
+ uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
+
+ bool retransmit = false;
+ if (bufferSeqNo == seqNo) {
+ retransmit = true;
+ } else if (blp != 0) {
+ for (size_t i = 0; i < 16; ++i) {
+ if ((blp & (1 << i))
+ && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
+ blp &= ~(1 << i);
+ retransmit = true;
+ }
+ }
+ }
+
+ if (retransmit) {
+ ALOGV("retransmitting seqNo %d", bufferSeqNo);
+
+ CHECK_EQ((status_t)OK,
+ sendRTPPacket(buffer, false /* storeInHistory */));
+
+ if (bufferSeqNo == seqNo) {
+ foundSeqNo = true;
+ }
+
+ if (foundSeqNo && blp == 0) {
+ break;
+ }
+ }
+
+ ++it;
+ }
+
+ if (!foundSeqNo || blp != 0) {
+ ALOGI("Some sequence numbers were no longer available for "
+ "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
+ seqNo, foundSeqNo, blp);
+
+ if (!mHistory.empty()) {
+ int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
+ int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
+
+ ALOGI("have seq numbers from %d - %d", earliest, latest);
+ }
+ }
+ }
+
+ return OK;
+}
+
+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;
+}
+
+void RTPSender::notifyInitDone(status_t err) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatInitDone);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void RTPSender::notifyError(status_t err) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+void RTPSender::notifyNetworkStall(size_t numBytesQueued) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatNetworkStall);
+ notify->setSize("numBytesQueued", numBytesQueued);
+ notify->post();
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.h b/media/libstagefright/wifi-display/rtp/RTPSender.h
new file mode 100644
index 0000000..fefcab7
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2013, 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_SENDER_H_
+
+#define RTP_SENDER_H_
+
+#include "RTPBase.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct ANetworkSession;
+
+// An object of this class facilitates sending of media data over an RTP
+// channel. The channel is established over a UDP or TCP connection depending
+// on which "TransportMode" was chosen. In addition different RTP packetization
+// schemes are supported such as "Transport Stream Packets over RTP",
+// or "AVC/H.264 encapsulation as specified in RFC 3984 (non-interleaved mode)"
+struct RTPSender : public RTPBase, public AHandler {
+ enum {
+ kWhatInitDone,
+ kWhatError,
+ kWhatNetworkStall,
+ kWhatInformSender,
+ };
+ RTPSender(
+ const sp<ANetworkSession> &netSession,
+ const sp<AMessage> &notify);
+
+ status_t initAsync(
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ TransportMode rtcpMode,
+ int32_t *outLocalRTPPort);
+
+ status_t queueBuffer(
+ const sp<ABuffer> &buffer,
+ uint8_t packetType,
+ PacketizationMode mode);
+
+protected:
+ virtual ~RTPSender();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatRTPNotify,
+ kWhatRTCPNotify,
+ };
+
+ enum {
+ kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188,
+ kMaxHistorySize = 1024,
+ kSourceID = 0xdeadbeef,
+ };
+
+ sp<ANetworkSession> mNetSession;
+ sp<AMessage> mNotify;
+ TransportMode mRTPMode;
+ TransportMode mRTCPMode;
+ int32_t mRTPSessionID;
+ int32_t mRTCPSessionID;
+ bool mRTPConnected;
+ bool mRTCPConnected;
+
+ uint64_t mLastNTPTime;
+ uint32_t mLastRTPTime;
+ uint32_t mNumRTPSent;
+ uint32_t mNumRTPOctetsSent;
+ uint32_t mNumSRsSent;
+
+ uint32_t mRTPSeqNo;
+
+ List<sp<ABuffer> > mHistory;
+ size_t mHistorySize;
+
+ static uint64_t GetNowNTP();
+
+ status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType);
+ status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
+ status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
+
+ status_t sendRTPPacket(
+ const sp<ABuffer> &packet, bool storeInHistory,
+ bool timeValid = false, int64_t timeUs = -1ll);
+
+ void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+
+ status_t onRTCPData(const sp<ABuffer> &data);
+ status_t parseReceiverReport(const uint8_t *data, size_t size);
+ status_t parseTSFB(const uint8_t *data, size_t size);
+ status_t parseAPP(const uint8_t *data, size_t size);
+
+ void notifyInitDone(status_t err);
+ void notifyError(status_t err);
+ void notifyNetworkStall(size_t numBytesQueued);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RTPSender);
+};
+
+} // namespace android
+
+#endif // RTP_SENDER_H_
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.cpp b/media/libstagefright/wifi-display/sink/LinearRegression.cpp
deleted file mode 100644
index 8cfce37..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.cpp
+++ /dev/null
@@ -1,110 +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 "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/media/libstagefright/wifi-display/sink/LinearRegression.h b/media/libstagefright/wifi-display/sink/LinearRegression.h
deleted file mode 100644
index ca6f5a1..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.h
+++ /dev/null
@@ -1,52 +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 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/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp
deleted file mode 100644
index 0918034..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.cpp
+++ /dev/null
@@ -1,806 +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 "RTPSink"
-#include <utils/Log.h>
-
-#include "RTPSink.h"
-
-#include "ANetworkSession.h"
-#include "TunnelRenderer.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>
-
-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<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex)
- : mNetSession(netSession),
- mSurfaceTex(surfaceTex),
- mRTPPort(0),
- mRTPSessionID(0),
- mRTCPSessionID(0),
- mFirstArrivalTimeUs(-1ll),
- mNumPacketsReceived(0ll),
- mRegression(1000),
- mMaxDelayMs(-1ll) {
-}
-
-RTPSink::~RTPSink() {
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- }
-
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- }
-}
-
-status_t RTPSink::init(bool useTCPInterleaving) {
- if (useTCPInterleaving) {
- return OK;
- }
-
- int clientRtp;
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
- for (clientRtp = 15550;; clientRtp += 2) {
- int32_t rtpSession;
- status_t err = mNetSession->createUDPSession(
- clientRtp, rtpNotify, &rtpSession);
-
- if (err != OK) {
- ALOGI("failed to create RTP socket on port %d", clientRtp);
- continue;
- }
-
- 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 ANetworkSession::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 ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- status_t err;
- if (msg->what() == kWhatRTPNotify) {
- err = parseRTP(data);
- } else {
- err = parseRTCP(data);
- }
- 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, id());
- msg->setInt32("isRTP", isRTP);
- msg->setBuffer("buffer", buffer);
- msg->post();
-
- return OK;
-}
-
-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;
- }
-
- 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("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;
- ALOGI("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, id());
- notifyLost->setInt32("ssrc", srcId);
-
- mRenderer = new TunnelRenderer(notifyLost, mSurfaceTex);
- looper()->registerHandler(mRenderer);
- }
-
- sp<AMessage> queueBufferMsg =
- new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer->id());
-
- 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]);
-
- ALOGV("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, id()))->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);
-
- 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/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h
deleted file mode 100644
index a1d127d..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.h
+++ /dev/null
@@ -1,98 +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 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 ANetworkSession;
-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<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex);
-
- // 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);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~RTPSink();
-
-private:
- enum {
- kWhatRTPNotify,
- kWhatRTCPNotify,
- kWhatSendRR,
- kWhatPacketLost,
- kWhatInject,
- };
-
- struct Source;
- struct StreamSource;
-
- sp<ANetworkSession> mNetSession;
- sp<ISurfaceTexture> mSurfaceTex;
- 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<TunnelRenderer> mRenderer;
-
- 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/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
deleted file mode 100644
index bc35aef..0000000
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
+++ /dev/null
@@ -1,396 +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 "TunnelRenderer"
-#include <utils/Log.h>
-
-#include "TunnelRenderer.h"
-
-#include "ATSParser.h"
-
-#include <binder/IMemory.h>
-#include <binder/IServiceManager.h>
-#include <gui/SurfaceComposerClient.h>
-#include <media/IMediaPlayerService.h>
-#include <media/IStreamSource.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <ui/DisplayInfo.h>
-
-namespace android {
-
-struct TunnelRenderer::PlayerClient : public BnMediaPlayerClient {
- PlayerClient() {}
-
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
- ALOGI("notify %d, %d, %d", msg, ext1, ext2);
- }
-
-protected:
- virtual ~PlayerClient() {}
-
-private:
- 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() {
-}
-
-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) {
- CHECK_LT(index, mBuffers.size());
-
- {
- Mutex::Autolock autoLock(mLock);
- mIndicesAvailable.push_back(index);
- }
-
- doSomeWork();
-}
-
-uint32_t TunnelRenderer::StreamSource::flags() const {
- return kFlagAlignedVideoData;
-}
-
-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;
-
- extra->setInt32(
- IStreamListener::kKeyDiscontinuityMask,
- ATSParser::DISCONTINUITY_ABSOLUTE_TIME);
-
- extra->setInt64("timeUs", ALooper::GetNowUs());
-
- mListener->issueCommand(
- IStreamListener::DISCONTINUITY,
- false /* synchronous */,
- extra);
- }
-
- ALOGV("dequeue TS packet of size %d", srcBuffer->size());
-
- size_t index = *mIndicesAvailable.begin();
- mIndicesAvailable.erase(mIndicesAvailable.begin());
-
- sp<IMemory> mem = mBuffers.itemAt(index);
- CHECK_LE(srcBuffer->size(), mem->size());
- CHECK_EQ((srcBuffer->size() % 188), 0u);
-
- memcpy(mem->pointer(), srcBuffer->data(), srcBuffer->size());
- mListener->queueBuffer(index, srcBuffer->size());
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TunnelRenderer::TunnelRenderer(
- const sp<AMessage> &notifyLost,
- const sp<ISurfaceTexture> &surfaceTex)
- : mNotifyLost(notifyLost),
- mSurfaceTex(surfaceTex),
- mTotalBytesQueued(0ll),
- mLastDequeuedExtSeqNo(-1),
- mFirstFailedAttemptUs(-1ll),
- mRequestedRetransmission(false) {
-}
-
-TunnelRenderer::~TunnelRenderer() {
- destroyPlayer();
-}
-
-void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
- Mutex::Autolock autoLock(mLock);
-
- mTotalBytesQueued += buffer->size();
-
- 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;
- 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();
- buffer.clear();
- extSeqNo = -1;
-
- mPackets.erase(mPackets.begin());
- }
-
- if (mPackets.empty()) {
- if (mFirstFailedAttemptUs < 0ll) {
- mFirstFailedAttemptUs = ALooper::GetNowUs();
- mRequestedRetransmission = false;
- } else {
- ALOGV("no packets available for %.2f secs",
- (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6);
- }
-
- return NULL;
- }
-
- if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1) {
- if (mRequestedRetransmission) {
- ALOGI("Recovered after requesting retransmission of %d",
- extSeqNo);
- }
-
- mLastDequeuedExtSeqNo = extSeqNo;
- mFirstFailedAttemptUs = -1ll;
- mRequestedRetransmission = false;
-
- mPackets.erase(mPackets.begin());
-
- mTotalBytesQueued -= buffer->size();
-
- return buffer;
- }
-
- if (mFirstFailedAttemptUs < 0ll) {
- mFirstFailedAttemptUs = ALooper::GetNowUs();
-
- ALOGI("failed to get the correct packet the first time.");
- return NULL;
- }
-
- if (mFirstFailedAttemptUs + 50000ll > 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();
-
- mRequestedRetransmission = true;
- } 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.
- mLastDequeuedExtSeqNo = extSeqNo;
- mFirstFailedAttemptUs = -1ll;
- mRequestedRetransmission = false;
-
- mTotalBytesQueued -= buffer->size();
-
- 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 (mSurfaceTex == NULL) {
- mComposerClient = new SurfaceComposerClient;
- CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
-
- DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(0, &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();
-
- 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(getpid(), mPlayerClient, 0);
- CHECK(mPlayer != NULL);
- CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK);
-
- mPlayer->setVideoSurfaceTexture(
- mSurfaceTex != NULL ? mSurfaceTex : mSurface->getSurfaceTexture());
-
- mPlayer->start();
-}
-
-void TunnelRenderer::destroyPlayer() {
- mStreamSource.clear();
-
- mPlayer->stop();
- mPlayer.clear();
-
- if (mSurfaceTex == NULL) {
- mSurface.clear();
- mSurfaceControl.clear();
-
- mComposerClient->dispose();
- mComposerClient.clear();
- }
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
deleted file mode 100644
index c9597e0..0000000
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h
+++ /dev/null
@@ -1,84 +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 TUNNEL_RENDERER_H_
-
-#define TUNNEL_RENDERER_H_
-
-#include <gui/Surface.h>
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-struct SurfaceComposerClient;
-struct SurfaceControl;
-struct Surface;
-struct IMediaPlayer;
-struct IStreamListener;
-
-// This class reassembles incoming RTP packets into the correct order
-// and sends the resulting transport stream to a mediaplayer instance
-// for playback.
-struct TunnelRenderer : public AHandler {
- TunnelRenderer(
- const sp<AMessage> &notifyLost,
- const sp<ISurfaceTexture> &surfaceTex);
-
- sp<ABuffer> dequeueBuffer();
-
- enum {
- kWhatQueueBuffer,
- };
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~TunnelRenderer();
-
-private:
- struct PlayerClient;
- struct StreamSource;
-
- mutable Mutex mLock;
-
- sp<AMessage> mNotifyLost;
- sp<ISurfaceTexture> mSurfaceTex;
-
- List<sp<ABuffer> > mPackets;
- int64_t mTotalBytesQueued;
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
- sp<Surface> mSurface;
- sp<PlayerClient> mPlayerClient;
- sp<IMediaPlayer> mPlayer;
- sp<StreamSource> mStreamSource;
-
- int32_t mLastDequeuedExtSeqNo;
- int64_t mFirstFailedAttemptUs;
- bool mRequestedRetransmission;
-
- void initPlayer();
- void destroyPlayer();
-
- void queueBuffer(const sp<ABuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer);
-};
-
-} // namespace android
-
-#endif // TUNNEL_RENDERER_H_
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
deleted file mode 100644
index fcd20d4..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ /dev/null
@@ -1,644 +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 "WifiDisplaySink"
-#include <utils/Log.h>
-
-#include "WifiDisplaySink.h"
-#include "ParsedMessage.h"
-#include "RTPSink.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaErrors.h>
-
-namespace android {
-
-WifiDisplaySink::WifiDisplaySink(
- const sp<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex)
- : mState(UNDEFINED),
- mNetSession(netSession),
- mSurfaceTex(surfaceTex),
- mSessionID(0),
- mNextCSeq(1) {
-}
-
-WifiDisplaySink::~WifiDisplaySink() {
-}
-
-void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("sourceHost", sourceHost);
- msg->setInt32("sourcePort", sourcePort);
- msg->post();
-}
-
-void WifiDisplaySink::start(const char *uri) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("setupURI", uri);
- msg->post();
-}
-
-// 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::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- int32_t sourcePort;
-
- if (msg->findString("setupURI", &mSetupURI)) {
- AString path, user, pass;
- CHECK(ParseURL(
- mSetupURI.c_str(),
- &mRTSPHost, &sourcePort, &path, &user, &pass)
- && user.empty() && pass.empty());
- } else {
- CHECK(msg->findString("sourceHost", &mRTSPHost));
- CHECK(msg->findInt32("sourcePort", &sourcePort));
- }
-
- sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
-
- status_t err = mNetSession->createRTSPClient(
- mRTSPHost.c_str(), sourcePort, notify, &mSessionID);
- CHECK_EQ(err, (status_t)OK);
-
- mState = CONNECTING;
- break;
- }
-
- case kWhatRTSPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::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));
-
- if (sessionID == mSessionID) {
- ALOGI("Lost control connection.");
-
- // The control connection is dead now.
- mNetSession->destroySession(mSessionID);
- mSessionID = 0;
-
- looper()->stop();
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- ALOGI("We're now connected.");
- mState = CONNECTED;
-
- if (!mSetupURI.empty()) {
- status_t err =
- sendDescribe(mSessionID, mSetupURI.c_str());
-
- CHECK_EQ(err, (status_t)OK);
- }
- break;
- }
-
- case ANetworkSession::kWhatData:
- {
- onReceiveClientData(msg);
- break;
- }
-
- case ANetworkSession::kWhatBinaryData:
- {
- CHECK(sUseTCPInterleaving);
-
- 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 kWhatStop:
- {
- looper()->stop();
- 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");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveM2Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- 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;
- 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;
- 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;
- }
-
- mState = PAUSED;
-
- return sendPlay(
- sessionID,
- !mSetupURI.empty()
- ? mSetupURI.c_str() : "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-}
-
-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.");
- return ERROR_MALFORMED;
- }
-
- int rtpPort, rtcpPort;
- if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
- || rtpPort <= 0 || rtpPort > 65535
- || rtcpPort <=0 || rtcpPort > 65535
- || rtcpPort != rtpPort + 1) {
- ALOGE("Invalid server_port description '%s'.",
- serverPortStr.c_str());
-
- return ERROR_MALFORMED;
- }
-
- if (rtpPort & 1) {
- ALOGW("Server picked an odd numbered RTP port.");
- }
-
- return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
-}
-
-status_t WifiDisplaySink::onReceivePlayResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- mState = PLAYING;
-
- 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());
-
- ALOGV("session %d received '%s'",
- sessionID, data->debugString().c_str());
-
- AString method;
- AString uri;
- data->getRequestField(0, &method);
-
- int32_t cseq;
- if (!data->findInt32("cseq", &cseq)) {
- sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
- return;
- }
-
- if (method.startsWith("RTSP/")) {
- // 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;
- 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") {
- 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) {
- 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");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-
- err = sendM2(sessionID);
- CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySink::onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString body =
- "wfd_video_formats: xxx\r\n"
- "wfd_audio_codecs: xxx\r\n"
- "wfd_client_rtp_ports: RTP/AVP/UDP;unicast xxx 0 mode=play\r\n";
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("Content-Type: text/parameters\r\n");
- response.append(StringPrintf("Content-Length: %d\r\n", body.size()));
- response.append("\r\n");
- response.append(body);
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri) {
- uri = "rtsp://xwgntvx.is.livestream-api.com/livestreamiphone/wgntv";
- uri = "rtsp://v2.cache6.c.youtube.com/video.3gp?cid=e101d4bf280055f9&fmt=18";
-
- AString request = StringPrintf("DESCRIBE %s RTSP/1.0\r\n", uri);
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append("Accept: application/sdp\r\n");
- request.append("\r\n");
-
- status_t err = mNetSession->sendRequest(
- sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveDescribeResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
- mRTPSink = new RTPSink(mNetSession, mSurfaceTex);
- looper()->registerHandler(mRTPSink);
-
- status_t err = mRTPSink->init(sUseTCPInterleaving);
-
- if (err != OK) {
- looper()->unregisterHandler(mRTPSink->id());
- mRTPSink.clear();
- return err;
- }
-
- AString request = StringPrintf("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(
- StringPrintf(
- "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
- rtpPort, rtpPort + 1));
- }
-
- request.append("\r\n");
-
- ALOGV("request = '%s'", request.c_str());
-
- err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) {
- AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri);
-
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
- request.append("\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-void WifiDisplaySink::onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- const char *content = data->getContent();
-
- if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {
- status_t err =
- sendSetup(
- sessionID,
- "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-
- CHECK_EQ(err, (status_t)OK);
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.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");
-
- 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 4.1)\r\n");
-
- if (cseq >= 0) {
- response->append(StringPrintf("CSeq: %d\r\n", cseq));
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
deleted file mode 100644
index f886ee5..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ /dev/null
@@ -1,147 +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 WIFI_DISPLAY_SINK_H_
-
-#define WIFI_DISPLAY_SINK_H_
-
-#include "ANetworkSession.h"
-
-#include <gui/Surface.h>
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-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<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex = NULL);
-
- void start(const char *sourceHost, int32_t sourcePort);
- void start(const char *uri);
-
-protected:
- virtual ~WifiDisplaySink();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum State {
- UNDEFINED,
- CONNECTING,
- CONNECTED,
- PAUSED,
- PLAYING,
- };
-
- enum {
- kWhatStart,
- kWhatRTSPNotify,
- kWhatStop,
- };
-
- 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<ANetworkSession> mNetSession;
- sp<ISurfaceTexture> mSurfaceTex;
- AString mSetupURI;
- AString mRTSPHost;
- int32_t mSessionID;
-
- int32_t mNextCSeq;
-
- KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
-
- sp<RTPSink> mRTPSink;
- AString mPlaybackSessionID;
- int32_t mPlaybackSessionTimeoutSecs;
-
- 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 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);
-
- 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);
-
- DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
-};
-
-} // namespace android
-
-#endif // WIFI_DISPLAY_SINK_H_
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 01a394f..753b3ec 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -21,9 +21,10 @@
#include "Converter.h"
#include "MediaPuller.h"
+#include "include/avc_utils.h"
#include <cutils/properties.h>
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
#include <media/ICrypto.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.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,37 +43,72 @@ namespace android {
Converter::Converter(
const sp<AMessage> &notify,
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
,mFirstSilentFrameUs(-1ll)
,mInSilentMode(false)
#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;
+
+ mIsH264 = !strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC);
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime.c_str())) {
+ mIsPCMAudio = true;
}
+}
- CHECK(!usePCMAudio || !mIsVideo);
+static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
+ void *mbuf;
+ if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
+ && mbuf != NULL) {
+ ALOGV("releasing mbuf %p", mbuf);
- mInitCheck = initEncoder();
+ accessUnit->meta()->setPointer("mediaBuffer", NULL);
- if (mInitCheck != OK) {
- if (mEncoder != NULL) {
- mEncoder->release();
- mEncoder.clear();
- }
+ static_cast<MediaBuffer *>(mbuf)->release();
+ mbuf = NULL;
+ }
+}
+
+void Converter::releaseEncoder() {
+ if (mEncoder == NULL) {
+ return;
+ }
+
+ mEncoder->release();
+ mEncoder.clear();
+
+ while (!mInputBufferQueue.empty()) {
+ sp<ABuffer> accessUnit = *mInputBufferQueue.begin();
+ mInputBufferQueue.erase(mInputBufferQueue.begin());
+
+ ReleaseMediaBufferReference(accessUnit);
}
+
+ for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
+ sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
+ ReleaseMediaBufferReference(accessUnit);
+ }
+
+ mEncoderInputBuffers.clear();
+ mEncoderOutputBuffers.clear();
}
Converter::~Converter() {
@@ -82,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 {
@@ -94,7 +143,13 @@ sp<AMessage> Converter::getOutputFormat() const {
return mOutputFormat;
}
-static int32_t getBitrate(const char *propName, int32_t defaultValue) {
+bool Converter::needToManuallyPrependSPSPPS() const {
+ return mNeedToManuallyPrependSPSPPS;
+}
+
+// static
+int32_t Converter::GetInt32Property(
+ const char *propName, int32_t defaultValue) {
char val[PROPERTY_VALUE_MAX];
if (property_get(propName, val, NULL)) {
char *end;
@@ -109,23 +164,10 @@ static int32_t getBitrate(const char *propName, int32_t defaultValue) {
}
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(
@@ -136,16 +178,13 @@ status_t Converter::initEncoder() {
}
}
- mOutputFormat = mInputFormat->dup();
-
if (mIsPCMAudio) {
return OK;
}
- mOutputFormat->setString("mime", outputMIME.c_str());
-
- int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000);
- int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000);
+ int32_t audioBitrate = GetInt32Property("media.wfd.audio-bitrate", 128000);
+ int32_t videoBitrate = GetInt32Property("media.wfd.video-bitrate", 5000000);
+ mPrevVideoBitrate = videoBitrate;
ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
audioBitrate, videoBitrate);
@@ -156,22 +195,78 @@ status_t Converter::initEncoder() {
mOutputFormat->setInt32("bitrate", videoBitrate);
mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
mOutputFormat->setInt32("frame-rate", 30);
- mOutputFormat->setInt32("i-frame-interval", 1); // Iframes every 1 secs
- mOutputFormat->setInt32("prepend-sps-pps-to-idr-frames", 1);
+ mOutputFormat->setInt32("i-frame-interval", 15); // Iframes every 15 secs
+
+ // Configure encoder to use intra macroblock refresh mode
+ mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic);
+
+ int width, height, mbs;
+ if (!mOutputFormat->findInt32("width", &width)
+ || !mOutputFormat->findInt32("height", &height)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ // Update macroblocks in a cyclic fashion with 10% of all MBs within
+ // frame gets updated at one time. It takes about 10 frames to
+ // completely update a whole video frame. If the frame rate is 30,
+ // it takes about 333 ms in the best case (if next frame is not an IDR)
+ // to recover from a lost/corrupted packet.
+ mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100;
+ mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs);
}
ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
- status_t err = mEncoder->configure(
- mOutputFormat,
- NULL /* nativeWindow */,
- NULL /* crypto */,
- MediaCodec::CONFIGURE_FLAG_ENCODE);
+ mNeedToManuallyPrependSPSPPS = false;
+
+ status_t err = NO_INIT;
+
+ if (!isAudio) {
+ sp<AMessage> tmp = mOutputFormat->dup();
+ tmp->setInt32("prepend-sps-pps-to-idr-frames", 1);
+
+ err = mEncoder->configure(
+ tmp,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+ if (err == OK) {
+ // Encoder supported prepending SPS/PPS, we don't need to emulate
+ // it.
+ mOutputFormat = tmp;
+ } else {
+ mNeedToManuallyPrependSPSPPS = true;
+
+ ALOGI("We going to manually prepend SPS and PPS to IDR frames.");
+ }
+ }
+
+ if (err != OK) {
+ // We'll get here for audio or if we failed to configure the encoder
+ // to automatically prepend SPS/PPS in the case of video.
+
+ err = mEncoder->configure(
+ mOutputFormat,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+ }
if (err != OK) {
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) {
@@ -184,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) {
@@ -223,16 +328,7 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) {
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
- void *mbuf;
- if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
- && mbuf != NULL) {
- ALOGV("releasing mbuf %p", mbuf);
-
- accessUnit->meta()->setPointer("mediaBuffer", NULL);
-
- static_cast<MediaBuffer *>(mbuf)->release();
- mbuf = NULL;
- }
+ ReleaseMediaBufferReference(accessUnit);
}
break;
}
@@ -249,6 +345,16 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) {
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
+ if (mNumFramesToDrop > 0 || mEncodingSuspended) {
+ if (mNumFramesToDrop > 0) {
+ --mNumFramesToDrop;
+ ALOGI("dropping frame.");
+ }
+
+ ReleaseMediaBufferReference(accessUnit);
+ break;
+ }
+
#if 0
void *mbuf;
if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
@@ -326,7 +432,7 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) {
}
if (mIsVideo) {
- ALOGI("requesting IDR frame");
+ ALOGV("requesting IDR frame");
mEncoder->requestIDRFrame();
}
break;
@@ -334,16 +440,49 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) {
case kWhatShutdown:
{
- ALOGI("shutting down encoder");
+ ALOGI("shutting down %s encoder", mIsVideo ? "video" : "audio");
- if (mEncoder != NULL) {
- mEncoder->release();
- mEncoder.clear();
- }
+ 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;
+ }
+
+ case kWhatDropAFrame:
+ {
+ ++mNumFramesToDrop;
+ 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;
}
@@ -532,32 +671,57 @@ 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);
if (err != OK) {
+ if (err == INFO_FORMAT_CHANGED) {
+ continue;
+ } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
+ mEncoder->getOutputBuffers(&mEncoderOutputBuffers);
+ continue;
+ }
+
if (err == -EAGAIN) {
err = OK;
}
@@ -569,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);
@@ -589,7 +797,9 @@ status_t Converter::doMoreWork() {
}
}
- mEncoder->releaseOutputBuffer(bufferIndex);
+ if (!handle) {
+ mEncoder->releaseOutputBuffer(bufferIndex);
+ }
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
break;
@@ -603,4 +813,32 @@ void Converter::requestIDRFrame() {
(new AMessage(kWhatRequestIDRFrame, id()))->post();
}
+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;
+}
+
+void Converter::setVideoBitrate(int32_t bitRate) {
+ if (mIsVideo && mEncoder != NULL && bitRate != mPrevVideoBitrate) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("video-bitrate", bitRate);
+
+ mEncoder->setParameters(params);
+
+ mPrevVideoBitrate = bitRate;
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 2cdeda3..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,55 +32,80 @@ 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> &notify,
- 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> &notify,
+ const sp<ALooper> &codecLooper,
+ const sp<AMessage> &outputFormat,
+ uint32_t flags = 0);
+
+ status_t init();
- status_t initCheck() const;
+ sp<IGraphicBufferProducer> getGraphicBufferProducer();
size_t getInputBufferCount() const;
sp<AMessage> getOutputFormat() const;
+ bool needToManuallyPrependSPSPPS() const;
void feedAccessUnit(const sp<ABuffer> &accessUnit);
void signalEOS();
void requestIDRFrame();
+ void dropAFrame();
+ void suspendEncoding(bool suspend);
+
+ void shutdownAsync();
+
+ int32_t getVideoBitrate() const;
+ void setVideoBitrate(int32_t bitrate);
+
+ static int32_t GetInt32Property(const char *propName, int32_t defaultValue);
+
enum {
- kWhatAccessUnit,
- kWhatEOS,
- kWhatError,
+ // MUST not conflict with private enums below.
+ kWhatMediaPullerNotify = 'pulN',
};
+protected:
+ virtual ~Converter();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
enum {
kWhatDoMoreWork,
kWhatRequestIDRFrame,
+ kWhatSuspendEncoding,
kWhatShutdown,
- kWhatMediaPullerNotify,
kWhatEncoderActivity,
+ kWhatDropAFrame,
+ kWhatReleaseOutputBuffer,
};
- void shutdownAsync();
-
-protected:
- virtual ~Converter();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- status_t mInitCheck;
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;
@@ -89,6 +113,8 @@ private:
List<sp<ABuffer> > mInputBufferQueue;
+ sp<ABuffer> mCSD0;
+
bool mDoMoreWorkPending;
#if ENABLE_SILENCE_DETECTION
@@ -98,7 +124,13 @@ private:
sp<ABuffer> mPartialAudioAU;
+ int32_t mPrevVideoBitrate;
+
+ int32_t mNumFramesToDrop;
+ bool mEncodingSuspended;
+
status_t initEncoder();
+ void releaseEncoder();
status_t feedEncoderInputBuffers();
@@ -114,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 ab69c4a..7e8891d 100644
--- a/media/libstagefright/wifi-display/source/MediaPuller.cpp
+++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp
@@ -34,7 +34,8 @@ MediaPuller::MediaPuller(
: mSource(source),
mNotify(notify),
mPullGeneration(0),
- mIsAudio(false) {
+ mIsAudio(false),
+ mPaused(false) {
sp<MetaData> meta = source->getFormat();
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
@@ -71,6 +72,14 @@ void MediaPuller::stopAsync(const sp<AMessage> &notify) {
msg->post();
}
+void MediaPuller::pause() {
+ (new AMessage(kWhatPause, id()))->post();
+}
+
+void MediaPuller::resume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStart:
@@ -84,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) {
@@ -95,7 +107,6 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
-
response->postReply(replyID);
break;
}
@@ -130,6 +141,16 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
MediaBuffer *mbuf;
status_t err = mSource->read(&mbuf);
+ if (mPaused) {
+ if (err == OK) {
+ mbuf->release();
+ mbuf = NULL;
+ }
+
+ schedulePull();
+ break;
+ }
+
if (err != OK) {
if (err == ERROR_END_OF_STREAM) {
ALOGI("stream ended.");
@@ -176,6 +197,18 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatPause:
+ {
+ mPaused = true;
+ break;
+ }
+
+ case kWhatResume:
+ {
+ mPaused = false;
+ break;
+ }
+
default:
TRESPASS();
}
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.h b/media/libstagefright/wifi-display/source/MediaPuller.h
index 728da7b..1291bb3 100644
--- a/media/libstagefright/wifi-display/source/MediaPuller.h
+++ b/media/libstagefright/wifi-display/source/MediaPuller.h
@@ -35,6 +35,9 @@ struct MediaPuller : public AHandler {
status_t start();
void stopAsync(const sp<AMessage> &notify);
+ void pause();
+ void resume();
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual ~MediaPuller();
@@ -44,12 +47,15 @@ private:
kWhatStart,
kWhatStop,
kWhatPull,
+ kWhatPause,
+ kWhatResume,
};
sp<MediaSource> mSource;
sp<AMessage> mNotify;
int32_t mPullGeneration;
bool mIsAudio;
+ bool mPaused;
status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg);
void schedulePull();
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index f1e7140..286ea13 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -23,13 +23,11 @@
#include "Converter.h"
#include "MediaPuller.h"
#include "RepeaterSource.h"
-#include "Sender.h"
-#include "TSPacketizer.h"
#include "include/avc_utils.h"
+#include "WifiDisplaySource.h"
#include <binder/IServiceManager.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
+#include <cutils/properties.h>
#include <media/IHDCP.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -40,10 +38,9 @@
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/NuMediaExtractor.h>
#include <media/stagefright/SurfaceMediaSource.h>
#include <media/stagefright/Utils.h>
@@ -62,26 +59,35 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler {
const sp<MediaPuller> &mediaPuller,
const sp<Converter> &converter);
+ Track(const sp<AMessage> &notify, const sp<AMessage> &format);
+
void setRepeaterSource(const sp<RepeaterSource> &source);
sp<AMessage> getFormat();
bool isAudio() const;
const sp<Converter> &converter() const;
- ssize_t packetizerTrackIndex() const;
+ const sp<RepeaterSource> &repeaterSource() const;
- void setPacketizerTrackIndex(size_t index);
+ ssize_t mediaSenderTrackIndex() const;
+ void setMediaSenderTrackIndex(size_t index);
status_t start();
void stopAsync();
+ void pause();
+ void resume();
+
void queueAccessUnit(const sp<ABuffer> &accessUnit);
sp<ABuffer> dequeueAccessUnit();
bool hasOutputBuffer(int64_t *timeUs) const;
void queueOutputBuffer(const sp<ABuffer> &accessUnit);
sp<ABuffer> dequeueOutputBuffer();
+
+#if SUSPEND_VIDEO_IF_IDLE
bool isSuspended() const;
+#endif
size_t countQueuedOutputBuffers() const {
return mQueuedOutputBuffers.size();
@@ -103,8 +109,9 @@ private:
sp<ALooper> mCodecLooper;
sp<MediaPuller> mMediaPuller;
sp<Converter> mConverter;
+ sp<AMessage> mFormat;
bool mStarted;
- ssize_t mPacketizerTrackIndex;
+ ssize_t mMediaSenderTrackIndex;
bool mIsAudio;
List<sp<ABuffer> > mQueuedAccessUnits;
sp<RepeaterSource> mRepeaterSource;
@@ -128,11 +135,19 @@ WifiDisplaySource::PlaybackSession::Track::Track(
mMediaPuller(mediaPuller),
mConverter(converter),
mStarted(false),
- mPacketizerTrackIndex(-1),
mIsAudio(IsAudioFormat(mConverter->getOutputFormat())),
mLastOutputBufferQueuedTimeUs(-1ll) {
}
+WifiDisplaySource::PlaybackSession::Track::Track(
+ const sp<AMessage> &notify, const sp<AMessage> &format)
+ : mNotify(notify),
+ mFormat(format),
+ mStarted(false),
+ mIsAudio(IsAudioFormat(format)),
+ mLastOutputBufferQueuedTimeUs(-1ll) {
+}
+
WifiDisplaySource::PlaybackSession::Track::~Track() {
CHECK(!mStarted);
}
@@ -147,7 +162,7 @@ bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat(
}
sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
- return mConverter->getOutputFormat();
+ return mFormat != NULL ? mFormat : mConverter->getOutputFormat();
}
bool WifiDisplaySource::PlaybackSession::Track::isAudio() const {
@@ -158,13 +173,19 @@ const sp<Converter> &WifiDisplaySource::PlaybackSession::Track::converter() cons
return mConverter;
}
-ssize_t WifiDisplaySource::PlaybackSession::Track::packetizerTrackIndex() const {
- return mPacketizerTrackIndex;
+const sp<RepeaterSource> &
+WifiDisplaySource::PlaybackSession::Track::repeaterSource() const {
+ return mRepeaterSource;
+}
+
+ssize_t WifiDisplaySource::PlaybackSession::Track::mediaSenderTrackIndex() const {
+ CHECK_GE(mMediaSenderTrackIndex, 0);
+ return mMediaSenderTrackIndex;
}
-void WifiDisplaySource::PlaybackSession::Track::setPacketizerTrackIndex(size_t index) {
- CHECK_LT(mPacketizerTrackIndex, 0);
- mPacketizerTrackIndex = index;
+void WifiDisplaySource::PlaybackSession::Track::setMediaSenderTrackIndex(
+ size_t index) {
+ mMediaSenderTrackIndex = index;
}
status_t WifiDisplaySource::PlaybackSession::Track::start() {
@@ -188,7 +209,9 @@ status_t WifiDisplaySource::PlaybackSession::Track::start() {
void WifiDisplaySource::PlaybackSession::Track::stopAsync() {
ALOGV("Track::stopAsync isAudio=%d", mIsAudio);
- mConverter->shutdownAsync();
+ if (mConverter != NULL) {
+ mConverter->shutdownAsync();
+ }
sp<AMessage> msg = new AMessage(kWhatMediaPullerStopped, id());
@@ -200,10 +223,19 @@ void WifiDisplaySource::PlaybackSession::Track::stopAsync() {
mMediaPuller->stopAsync(msg);
} else {
+ mStarted = false;
msg->post();
}
}
+void WifiDisplaySource::PlaybackSession::Track::pause() {
+ mMediaPuller->pause();
+}
+
+void WifiDisplaySource::PlaybackSession::Track::resume() {
+ mMediaPuller->resume();
+}
+
void WifiDisplaySource::PlaybackSession::Track::onMessageReceived(
const sp<AMessage> &msg) {
switch (msg->what()) {
@@ -279,7 +311,6 @@ bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer(
void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer(
const sp<ABuffer> &accessUnit) {
mQueuedOutputBuffers.push_back(accessUnit);
-
mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs();
}
@@ -292,6 +323,7 @@ sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() {
return outputBuffer;
}
+#if SUSPEND_VIDEO_IF_IDLE
bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
if (!mQueuedOutputBuffers.empty()) {
return false;
@@ -307,6 +339,7 @@ bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
// this track suspended for the time being.
return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll;
}
+#endif
////////////////////////////////////////////////////////////////////////////////
@@ -314,44 +347,72 @@ WifiDisplaySource::PlaybackSession::PlaybackSession(
const sp<ANetworkSession> &netSession,
const sp<AMessage> &notify,
const in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp)
+ const sp<IHDCP> &hdcp,
+ const char *path)
: mNetSession(netSession),
mNotify(notify),
mInterfaceAddr(interfaceAddr),
mHDCP(hdcp),
+ mLocalRTPPort(-1),
mWeAreDead(false),
+ mPaused(false),
mLastLifesignUs(),
mVideoTrackIndex(-1),
mPrevTimeUs(-1ll),
- mAllTracksHavePacketizerIndex(false) {
+ mPullExtractorPending(false),
+ mPullExtractorGeneration(0),
+ mFirstSampleTimeRealUs(-1ll),
+ mFirstSampleTimeUs(-1ll) {
+ if (path != NULL) {
+ mMediaPath.setTo(path);
+ }
}
status_t WifiDisplaySource::PlaybackSession::init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- Sender::TransportMode transportMode,
- bool usePCMAudio) {
- status_t err = setupPacketizer(usePCMAudio);
+ const char *clientIP,
+ int32_t clientRtp,
+ RTPSender::TransportMode rtpMode,
+ int32_t clientRtcp,
+ RTPSender::TransportMode rtcpMode,
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex,
+ VideoFormats::ProfileType videoProfileType,
+ VideoFormats::LevelType videoLevelType) {
+ sp<AMessage> notify = new AMessage(kWhatMediaSenderNotify, id());
+ mMediaSender = new MediaSender(mNetSession, notify);
+ looper()->registerHandler(mMediaSender);
+
+ mMediaSender->setHDCP(mHDCP);
+
+ status_t err = setupPacketizer(
+ enableAudio,
+ usePCMAudio,
+ enableVideo,
+ videoResolutionType,
+ videoResolutionIndex,
+ videoProfileType,
+ videoLevelType);
- if (err != OK) {
- return err;
+ if (err == OK) {
+ err = mMediaSender->initAsync(
+ -1 /* trackIndex */,
+ clientIP,
+ clientRtp,
+ rtpMode,
+ clientRtcp,
+ rtcpMode,
+ &mLocalRTPPort);
}
- sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
- mSender = new Sender(mNetSession, notify);
-
- mSenderLooper = new ALooper;
- mSenderLooper->setName("sender_looper");
-
- mSenderLooper->start(
- false /* runOnCallingThread */,
- false /* canCallJava */,
- PRIORITY_AUDIO);
-
- mSenderLooper->registerHandler(mSender);
+ if (err != OK) {
+ mLocalRTPPort = -1;
- err = mSender->init(clientIP, clientRtp, clientRtcp, transportMode);
+ looper()->unregisterHandler(mMediaSender->id());
+ mMediaSender.clear();
- if (err != OK) {
return err;
}
@@ -364,7 +425,7 @@ WifiDisplaySource::PlaybackSession::~PlaybackSession() {
}
int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const {
- return mSender->getRTPPort();
+ return mLocalRTPPort;
}
int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const {
@@ -378,22 +439,12 @@ void WifiDisplaySource::PlaybackSession::updateLiveness() {
status_t WifiDisplaySource::PlaybackSession::play() {
updateLiveness();
- return OK;
-}
+ (new AMessage(kWhatResume, id()))->post();
-status_t WifiDisplaySource::PlaybackSession::finishPlay() {
- // XXX Give the dongle a second to bind its sockets.
- (new AMessage(kWhatFinishPlay, id()))->post(1000000ll);
return OK;
}
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
- return mSender->finishInit();
-}
-
-status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
- mSender->scheduleSendSR();
-
+status_t WifiDisplaySource::PlaybackSession::onMediaSenderInitialized() {
for (size_t i = 0; i < mTracks.size(); ++i) {
CHECK_EQ((status_t)OK, mTracks.editValueAt(i)->start());
}
@@ -408,6 +459,8 @@ status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
status_t WifiDisplaySource::PlaybackSession::pause() {
updateLiveness();
+ (new AMessage(kWhatPause, id()))->post();
+
return OK;
}
@@ -438,39 +491,18 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
CHECK(msg->findSize("trackIndex", &trackIndex));
if (what == Converter::kWhatAccessUnit) {
- const sp<Track> &track = mTracks.valueFor(trackIndex);
-
- ssize_t packetizerTrackIndex = track->packetizerTrackIndex();
-
- if (packetizerTrackIndex < 0) {
- packetizerTrackIndex =
- mPacketizer->addTrack(track->getFormat());
-
- CHECK_GE(packetizerTrackIndex, 0);
-
- track->setPacketizerTrackIndex(packetizerTrackIndex);
-
- if (allTracksHavePacketizerIndex()) {
- status_t err = packetizeQueuedAccessUnits();
-
- if (err != OK) {
- notifySessionDead();
- break;
- }
- }
- }
-
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
- if (!allTracksHavePacketizerIndex()) {
- track->queueAccessUnit(accessUnit);
- break;
- }
+ const sp<Track> &track = mTracks.valueFor(trackIndex);
- track->queueOutputBuffer(accessUnit);
+ status_t err = mMediaSender->queueAccessUnit(
+ track->mediaSenderTrackIndex(),
+ accessUnit);
- drainAccessUnits();
+ if (err != OK) {
+ notifySessionDead();
+ }
break;
} else if (what == Converter::kWhatEOS) {
CHECK_EQ(what, Converter::kWhatEOS);
@@ -489,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;
@@ -502,25 +534,40 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
break;
}
- case kWhatSenderNotify:
+ case kWhatMediaSenderNotify:
{
int32_t what;
CHECK(msg->findInt32("what", &what));
- if (what == Sender::kWhatInitDone) {
- onFinishPlay2();
- } else if (what == Sender::kWhatSessionDead) {
+ if (what == MediaSender::kWhatInitDone) {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ if (err == OK) {
+ onMediaSenderInitialized();
+ } else {
+ notifySessionDead();
+ }
+ } else if (what == MediaSender::kWhatError) {
notifySessionDead();
+ } else if (what == MediaSender::kWhatNetworkStall) {
+ size_t numBytesQueued;
+ CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
+
+ if (mVideoTrackIndex >= 0) {
+ const sp<Track> &videoTrack =
+ mTracks.valueFor(mVideoTrackIndex);
+
+ sp<Converter> converter = videoTrack->converter();
+ if (converter != NULL) {
+ converter->dropAFrame();
+ }
+ }
+ } else if (what == MediaSender::kWhatInformSender) {
+ onSinkFeedback(msg);
} else {
TRESPASS();
}
-
- break;
- }
-
- case kWhatFinishPlay:
- {
- onFinishPlay();
break;
}
@@ -545,11 +592,8 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
break;
}
- mSenderLooper->unregisterHandler(mSender->id());
- mSender.clear();
- mSenderLooper.clear();
-
- mPacketizer.clear();
+ looper()->unregisterHandler(mMediaSender->id());
+ mMediaSender.clear();
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatSessionDestroyed);
@@ -558,25 +602,56 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
break;
}
- case kWhatPacketize:
+ case kWhatPause:
{
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
+ if (mExtractor != NULL) {
+ ++mPullExtractorGeneration;
+ mFirstSampleTimeRealUs = -1ll;
+ mFirstSampleTimeUs = -1ll;
+ }
- sp<ABuffer> accessUnit;
- CHECK(msg->findBuffer("accessUnit", &accessUnit));
+ if (mPaused) {
+ break;
+ }
-#if 0
- if ((ssize_t)trackIndex == mVideoTrackIndex) {
- int64_t nowUs = ALooper::GetNowUs();
- static int64_t prevNowUs = 0ll;
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ mTracks.editValueAt(i)->pause();
+ }
- ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs);
+ mPaused = true;
+ break;
+ }
- prevNowUs = nowUs;
+ case kWhatResume:
+ {
+ if (mExtractor != NULL) {
+ schedulePullExtractor();
}
-#endif
+ if (!mPaused) {
+ break;
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ mTracks.editValueAt(i)->resume();
+ }
+
+ mPaused = false;
+ break;
+ }
+
+ case kWhatPullExtractorSample:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mPullExtractorGeneration) {
+ break;
+ }
+
+ mPullExtractorPending = false;
+
+ onPullExtractor();
break;
}
@@ -585,23 +660,255 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
}
}
-status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) {
- mPacketizer = new TSPacketizer;
+void WifiDisplaySource::PlaybackSession::onSinkFeedback(const sp<AMessage> &msg) {
+ int64_t avgLatencyUs;
+ CHECK(msg->findInt64("avgLatencyUs", &avgLatencyUs));
- status_t err = addVideoSource();
+ 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();
+
+ mExtractor = new NuMediaExtractor;
+
+ status_t err = mExtractor->setDataSource(mMediaPath.c_str());
if (err != OK) {
return err;
}
+ size_t n = mExtractor->countTracks();
+ bool haveAudio = false;
+ bool haveVideo = false;
+ for (size_t i = 0; i < n; ++i) {
+ sp<AMessage> format;
+ err = mExtractor->getTrackFormat(i, &format);
+
+ if (err != OK) {
+ continue;
+ }
+
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+
+ bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
+ bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
+
+ if (isAudio && enableAudio && !haveAudio) {
+ haveAudio = true;
+ } else if (isVideo && enableVideo && !haveVideo) {
+ haveVideo = true;
+ } else {
+ continue;
+ }
+
+ err = mExtractor->selectTrack(i);
+
+ size_t trackIndex = mTracks.size();
+
+ sp<AMessage> notify = new AMessage(kWhatTrackNotify, id());
+ notify->setSize("trackIndex", trackIndex);
+
+ sp<Track> track = new Track(notify, format);
+ looper()->registerHandler(track);
+
+ mTracks.add(trackIndex, track);
+
+ mExtractorTrackToInternalTrack.add(i, trackIndex);
+
+ if (isVideo) {
+ mVideoTrackIndex = trackIndex;
+ }
+
+ uint32_t flags = MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
+
+ ssize_t mediaSenderTrackIndex =
+ mMediaSender->addTrack(format, flags);
+ CHECK_GE(mediaSenderTrackIndex, 0);
+
+ track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
+
+ if ((haveAudio || !enableAudio) && (haveVideo || !enableVideo)) {
+ break;
+ }
+ }
+
+ return OK;
+}
+
+void WifiDisplaySource::PlaybackSession::schedulePullExtractor() {
+ if (mPullExtractorPending) {
+ return;
+ }
+
+ int64_t sampleTimeUs;
+ status_t err = mExtractor->getSampleTime(&sampleTimeUs);
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+ if (mFirstSampleTimeRealUs < 0ll) {
+ mFirstSampleTimeRealUs = nowUs;
+ mFirstSampleTimeUs = sampleTimeUs;
+ }
+
+ int64_t whenUs = sampleTimeUs - mFirstSampleTimeUs + mFirstSampleTimeRealUs;
+
+ sp<AMessage> msg = new AMessage(kWhatPullExtractorSample, id());
+ msg->setInt32("generation", mPullExtractorGeneration);
+ msg->post(whenUs - nowUs);
+
+ mPullExtractorPending = true;
+}
+
+void WifiDisplaySource::PlaybackSession::onPullExtractor() {
+ sp<ABuffer> accessUnit = new ABuffer(1024 * 1024);
+ status_t err = mExtractor->readSampleData(accessUnit);
+ if (err != OK) {
+ // EOS.
+ return;
+ }
+
+ int64_t timeUs;
+ CHECK_EQ((status_t)OK, mExtractor->getSampleTime(&timeUs));
+
+ accessUnit->meta()->setInt64(
+ "timeUs", mFirstSampleTimeRealUs + timeUs - mFirstSampleTimeUs);
+
+ size_t trackIndex;
+ CHECK_EQ((status_t)OK, mExtractor->getSampleTrackIndex(&trackIndex));
+
+ sp<AMessage> msg = new AMessage(kWhatConverterNotify, id());
+
+ msg->setSize(
+ "trackIndex", mExtractorTrackToInternalTrack.valueFor(trackIndex));
+
+ msg->setInt32("what", Converter::kWhatAccessUnit);
+ msg->setBuffer("accessUnit", accessUnit);
+ msg->post();
+
+ mExtractor->advance();
+
+ schedulePullExtractor();
+}
+
+status_t WifiDisplaySource::PlaybackSession::setupPacketizer(
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex,
+ VideoFormats::ProfileType videoProfileType,
+ VideoFormats::LevelType videoLevelType) {
+ CHECK(enableAudio || enableVideo);
+
+ if (!mMediaPath.empty()) {
+ return setupMediaPacketizer(enableAudio, enableVideo);
+ }
+
+ if (enableVideo) {
+ status_t err = addVideoSource(
+ videoResolutionType, videoResolutionIndex, videoProfileType,
+ videoLevelType);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ if (!enableAudio) {
+ return OK;
+ }
+
return addAudioSource(usePCMAudio);
}
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");
@@ -630,24 +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);
-
- if (converter->initCheck() != OK) {
- return converter->initCheck();
- }
+ sp<Converter> converter = new Converter(notify, codecLooper, format);
looper()->registerHandler(converter);
+ err = converter->init();
+ if (err != OK) {
+ ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err);
+
+ looper()->unregisterHandler(converter->id());
+ return err;
+ }
+
notify = new AMessage(Converter::kWhatMediaPullerNotify, converter->id());
notify->setSize("trackIndex", trackIndex);
@@ -676,30 +996,55 @@ status_t WifiDisplaySource::PlaybackSession::addSource(
mVideoTrackIndex = trackIndex;
}
+ uint32_t flags = 0;
+ if (converter->needToManuallyPrependSPSPPS()) {
+ flags |= MediaSender::FLAG_MANUALLY_PREPEND_SPS_PPS;
+ }
+
+ ssize_t mediaSenderTrackIndex =
+ mMediaSender->addTrack(converter->getOutputFormat(), flags);
+ CHECK_GE(mediaSenderTrackIndex, 0);
+
+ track->setMediaSenderTrackIndex(mediaSenderTrackIndex);
+
return OK;
}
-status_t WifiDisplaySource::PlaybackSession::addVideoSource() {
- sp<SurfaceMediaSource> source = new SurfaceMediaSource(width(), height());
+status_t WifiDisplaySource::PlaybackSession::addVideoSource(
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex,
+ VideoFormats::ProfileType videoProfileType,
+ VideoFormats::LevelType videoLevelType) {
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ CHECK(VideoFormats::GetConfiguration(
+ videoResolutionType,
+ videoResolutionIndex,
+ &width,
+ &height,
+ &framesPerSecond,
+ &interlaced));
+
+ unsigned profileIdc, levelIdc, constraintSet;
+ CHECK(VideoFormats::GetProfileLevel(
+ videoProfileType,
+ videoLevelType,
+ &profileIdc,
+ &levelIdc,
+ &constraintSet));
+
+ sp<SurfaceMediaSource> source = new SurfaceMediaSource(width, height);
source->setUseAbsoluteTimestamps();
-#if 1
sp<RepeaterSource> videoSource =
- new RepeaterSource(source, 30.0 /* rateHz */);
-#endif
+ new RepeaterSource(source, framesPerSecond);
-#if 1
size_t numInputBuffers;
status_t err = addSource(
true /* isVideo */, videoSource, true /* isRepeaterSource */,
- false /* usePCMAudio */, &numInputBuffers);
-#else
- size_t numInputBuffers;
- status_t err = addSource(
- true /* isVideo */, source, false /* isRepeaterSource */,
- false /* usePCMAudio */, &numInputBuffers);
-#endif
+ false /* usePCMAudio */, profileIdc, levelIdc, constraintSet,
+ &numInputBuffers);
if (err != OK) {
return err;
@@ -722,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");
@@ -730,18 +1076,10 @@ status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) {
return OK;
}
-sp<ISurfaceTexture> WifiDisplaySource::PlaybackSession::getSurfaceTexture() {
+sp<IGraphicBufferProducer> WifiDisplaySource::PlaybackSession::getSurfaceTexture() {
return mBufferQueue;
}
-int32_t WifiDisplaySource::PlaybackSession::width() const {
- return 1280;
-}
-
-int32_t WifiDisplaySource::PlaybackSession::height() const {
- return 720;
-}
-
void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
for (size_t i = 0; i < mTracks.size(); ++i) {
const sp<Track> &track = mTracks.valueAt(i);
@@ -750,155 +1088,6 @@ void WifiDisplaySource::PlaybackSession::requestIDRFrame() {
}
}
-bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
- if (mAllTracksHavePacketizerIndex) {
- return true;
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- if (mTracks.valueAt(i)->packetizerTrackIndex() < 0) {
- return false;
- }
- }
-
- mAllTracksHavePacketizerIndex = true;
-
- return true;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit,
- sp<ABuffer> *packets) {
- const sp<Track> &track = mTracks.valueFor(trackIndex);
-
- uint32_t flags = 0;
-
- bool isHDCPEncrypted = false;
- uint64_t inputCTR;
- uint8_t HDCP_private_data[16];
- if (mHDCP != NULL && !track->isAudio()) {
- isHDCPEncrypted = true;
-
- status_t err = mHDCP->encrypt(
- accessUnit->data(), accessUnit->size(),
- trackIndex /* streamCTR */,
- &inputCTR,
- accessUnit->data());
-
- if (err != OK) {
- ALOGE("Failed to HDCP-encrypt media data (err %d)",
- err);
-
- return err;
- }
-
- HDCP_private_data[0] = 0x00;
-
- HDCP_private_data[1] =
- (((trackIndex >> 30) & 3) << 1) | 1;
-
- HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
-
- HDCP_private_data[3] =
- (((trackIndex >> 15) & 0x7f) << 1) | 1;
-
- HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
-
- HDCP_private_data[5] =
- ((trackIndex & 0x7f) << 1) | 1;
-
- HDCP_private_data[6] = 0x00;
-
- HDCP_private_data[7] =
- (((inputCTR >> 60) & 0x0f) << 1) | 1;
-
- HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
-
- HDCP_private_data[9] =
- (((inputCTR >> 45) & 0x7f) << 1) | 1;
-
- HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
-
- HDCP_private_data[11] =
- (((inputCTR >> 30) & 0x7f) << 1) | 1;
-
- HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
-
- HDCP_private_data[13] =
- (((inputCTR >> 15) & 0x7f) << 1) | 1;
-
- HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
-
- HDCP_private_data[15] =
- ((inputCTR & 0x7f) << 1) | 1;
-
-#if 0
- ALOGI("HDCP_private_data:");
- hexdump(HDCP_private_data, sizeof(HDCP_private_data));
-
- ABitReader br(HDCP_private_data, sizeof(HDCP_private_data));
- CHECK_EQ(br.getBits(13), 0);
- CHECK_EQ(br.getBits(2), (trackIndex >> 30) & 3);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), (trackIndex >> 15) & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), trackIndex & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(11), 0);
- CHECK_EQ(br.getBits(4), (inputCTR >> 60) & 0xf);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), (inputCTR >> 45) & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), (inputCTR >> 30) & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), (inputCTR >> 15) & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
- CHECK_EQ(br.getBits(15), inputCTR & 0x7fff);
- CHECK_EQ(br.getBits(1), 1u);
-#endif
-
- flags |= TSPacketizer::IS_ENCRYPTED;
- }
-
- int64_t timeUs = ALooper::GetNowUs();
- if (mPrevTimeUs < 0ll || mPrevTimeUs + 100000ll <= timeUs) {
- flags |= TSPacketizer::EMIT_PCR;
- flags |= TSPacketizer::EMIT_PAT_AND_PMT;
-
- mPrevTimeUs = timeUs;
- }
-
- mPacketizer->packetize(
- track->packetizerTrackIndex(), accessUnit, packets, flags,
- !isHDCPEncrypted ? NULL : HDCP_private_data,
- !isHDCPEncrypted ? 0 : sizeof(HDCP_private_data),
- track->isAudio() ? 2 : 0 /* numStuffingBytes */);
-
- return OK;
-}
-
-status_t WifiDisplaySource::PlaybackSession::packetizeQueuedAccessUnits() {
- for (;;) {
- bool gotMoreData = false;
- for (size_t i = 0; i < mTracks.size(); ++i) {
- size_t trackIndex = mTracks.keyAt(i);
- const sp<Track> &track = mTracks.valueAt(i);
-
- sp<ABuffer> accessUnit = track->dequeueAccessUnit();
- if (accessUnit != NULL) {
- track->queueOutputBuffer(accessUnit);
- gotMoreData = true;
- }
- }
-
- if (!gotMoreData) {
- break;
- }
- }
-
- return OK;
-}
-
void WifiDisplaySource::PlaybackSession::notifySessionDead() {
// Inform WifiDisplaySource of our premature death (wish).
sp<AMessage> notify = mNotify->dup();
@@ -908,57 +1097,5 @@ void WifiDisplaySource::PlaybackSession::notifySessionDead() {
mWeAreDead = true;
}
-void WifiDisplaySource::PlaybackSession::drainAccessUnits() {
- ALOGV("audio/video has %d/%d buffers ready.",
- mTracks.valueFor(1)->countQueuedOutputBuffers(),
- mTracks.valueFor(0)->countQueuedOutputBuffers());
-
- while (drainAccessUnit()) {
- }
-}
-
-bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
- ssize_t minTrackIndex = -1;
- int64_t minTimeUs = -1ll;
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- const sp<Track> &track = mTracks.valueAt(i);
-
- int64_t timeUs;
- if (track->hasOutputBuffer(&timeUs)) {
- if (minTrackIndex < 0 || timeUs < minTimeUs) {
- minTrackIndex = mTracks.keyAt(i);
- minTimeUs = timeUs;
- }
- } else if (!track->isSuspended()) {
- // We still consider this track "live", so it should keep
- // delivering output data whose time stamps we'll have to
- // consider for proper interleaving.
- return false;
- }
- }
-
- if (minTrackIndex < 0) {
- return false;
- }
-
- const sp<Track> &track = mTracks.valueFor(minTrackIndex);
- sp<ABuffer> accessUnit = track->dequeueOutputBuffer();
-
- sp<ABuffer> packets;
- status_t err = packetizeAccessUnit(minTrackIndex, accessUnit, &packets);
-
- if (err != OK) {
- notifySessionDead();
- }
-
- if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
- packets->meta()->setInt32("isVideo", 1);
- }
- mSender->queuePackets(minTimeUs, packets);
-
- return true;
-}
-
} // namespace android
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index cc8b244..5c8ee94 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -18,7 +18,8 @@
#define PLAYBACK_SESSION_H_
-#include "Sender.h"
+#include "MediaSender.h"
+#include "VideoFormats.h"
#include "WifiDisplaySource.h"
namespace android {
@@ -26,10 +27,11 @@ namespace android {
struct ABuffer;
struct BufferQueue;
struct IHDCP;
-struct ISurfaceTexture;
+struct IGraphicBufferProducer;
struct MediaPuller;
struct MediaSource;
-struct TSPacketizer;
+struct MediaSender;
+struct NuMediaExtractor;
// Encapsulates the state of an RTP/RTCP session in the context of wifi
// display.
@@ -38,12 +40,22 @@ struct WifiDisplaySource::PlaybackSession : public AHandler {
const sp<ANetworkSession> &netSession,
const sp<AMessage> &notify,
const struct in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp);
+ const sp<IHDCP> &hdcp,
+ const char *path = NULL);
status_t init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- Sender::TransportMode transportMode,
- bool usePCMAudio);
+ const char *clientIP,
+ int32_t clientRtp,
+ RTPSender::TransportMode rtpMode,
+ int32_t clientRtcp,
+ RTPSender::TransportMode rtcpMode,
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex,
+ VideoFormats::ProfileType videoProfileType,
+ VideoFormats::LevelType videoLevelType);
void destroyAsync();
@@ -56,9 +68,7 @@ struct WifiDisplaySource::PlaybackSession : public AHandler {
status_t finishPlay();
status_t pause();
- sp<ISurfaceTexture> getSurfaceTexture();
- int32_t width() const;
- int32_t height() const;
+ sp<IGraphicBufferProducer> getSurfaceTexture();
void requestIDRFrame();
@@ -80,23 +90,27 @@ private:
kWhatMediaPullerNotify,
kWhatConverterNotify,
kWhatTrackNotify,
- kWhatSenderNotify,
kWhatUpdateSurface,
- kWhatFinishPlay,
- kWhatPacketize,
+ kWhatPause,
+ kWhatResume,
+ kWhatMediaSenderNotify,
+ kWhatPullExtractorSample,
};
sp<ANetworkSession> mNetSession;
- sp<Sender> mSender;
- sp<ALooper> mSenderLooper;
sp<AMessage> mNotify;
in_addr mInterfaceAddr;
sp<IHDCP> mHDCP;
+ AString mMediaPath;
+
+ sp<MediaSender> mMediaSender;
+ int32_t mLocalRTPPort;
+
bool mWeAreDead;
+ bool mPaused;
int64_t mLastLifesignUs;
- sp<TSPacketizer> mPacketizer;
sp<BufferQueue> mBufferQueue;
KeyedVector<size_t, sp<Track> > mTracks;
@@ -104,40 +118,50 @@ private:
int64_t mPrevTimeUs;
- bool mAllTracksHavePacketizerIndex;
+ sp<NuMediaExtractor> mExtractor;
+ KeyedVector<size_t, size_t> mExtractorTrackToInternalTrack;
+ bool mPullExtractorPending;
+ int32_t mPullExtractorGeneration;
+ int64_t mFirstSampleTimeRealUs;
+ int64_t mFirstSampleTimeUs;
- status_t setupPacketizer(bool usePCMAudio);
+ status_t setupMediaPacketizer(bool enableAudio, bool enableVideo);
+
+ status_t setupPacketizer(
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ 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();
- status_t addAudioSource(bool usePCMAudio);
-
- ssize_t appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush);
-
- status_t onFinishPlay();
- status_t onFinishPlay2();
+ status_t addVideoSource(
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex,
+ VideoFormats::ProfileType videoProfileType,
+ VideoFormats::LevelType videoLevelType);
- bool allTracksHavePacketizerIndex();
-
- status_t packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit,
- sp<ABuffer> *packets);
+ status_t addAudioSource(bool usePCMAudio);
- status_t packetizeQueuedAccessUnits();
+ status_t onMediaSenderInitialized();
void notifySessionDead();
- void drainAccessUnits();
+ void schedulePullExtractor();
+ void onPullExtractor();
- // Returns true iff an access unit was successfully drained.
- bool drainAccessUnit();
+ void onSinkFeedback(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
};
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
index 641e63f..cc8dee3 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
@@ -27,6 +27,25 @@ RepeaterSource::~RepeaterSource() {
CHECK(!mStarted);
}
+double RepeaterSource::getFrameRate() const {
+ return mRateHz;
+}
+
+void RepeaterSource::setFrameRate(double rateHz) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (rateHz == mRateHz) {
+ return;
+ }
+
+ if (mStartTimeUs >= 0ll) {
+ int64_t nextTimeUs = mStartTimeUs + (mFrameCount * 1000000ll) / mRateHz;
+ mStartTimeUs = nextTimeUs;
+ mFrameCount = 0;
+ }
+ mRateHz = rateHz;
+}
+
status_t RepeaterSource::start(MetaData *params) {
CHECK(!mStarted);
@@ -125,11 +144,14 @@ status_t RepeaterSource::read(
return mResult;
}
+#if SUSPEND_VIDEO_IF_IDLE
int64_t nowUs = ALooper::GetNowUs();
if (nowUs - mLastBufferUpdateUs > 1000000ll) {
mLastBufferUpdateUs = -1ll;
stale = true;
- } else {
+ } else
+#endif
+ {
mBuffer->add_ref();
*buffer = mBuffer;
(*buffer)->meta_data()->setInt64(kKeyTime, bufferTimeUs);
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h
index e4aa2b6..8d414fd 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.h
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.h
@@ -6,6 +6,8 @@
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/MediaSource.h>
+#define SUSPEND_VIDEO_IF_IDLE 0
+
namespace android {
// This MediaSource delivers frames at a constant rate by repeating buffers
@@ -26,6 +28,9 @@ struct RepeaterSource : public MediaSource {
// send updates in a while, this is its wakeup call.
void wakeUp();
+ double getFrameRate() const;
+ void setFrameRate(double rateHz);
+
protected:
virtual ~RepeaterSource();
diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
deleted file mode 100644
index ea12424..0000000
--- a/media/libstagefright/wifi-display/source/Sender.cpp
+++ /dev/null
@@ -1,979 +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 "Sender"
-#include <utils/Log.h>
-
-#include "Sender.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/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-#include <math.h>
-
-#define DEBUG_JITTER 0
-
-namespace android {
-
-////////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_JITTER
-struct TimeSeries {
- TimeSeries();
-
- void add(double val);
-
- double mean() const;
- double sdev() const;
-
-private:
- enum {
- kHistorySize = 20
- };
- double mValues[kHistorySize];
-
- size_t mCount;
- double mSum;
-};
-
-TimeSeries::TimeSeries()
- : mCount(0),
- mSum(0.0) {
-}
-
-void TimeSeries::add(double val) {
- if (mCount < kHistorySize) {
- mValues[mCount++] = val;
- mSum += val;
- } else {
- mSum -= mValues[0];
- memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double));
- mValues[kHistorySize - 1] = val;
- mSum += val;
- }
-}
-
-double TimeSeries::mean() const {
- if (mCount < 1) {
- return 0.0;
- }
-
- return mSum / mCount;
-}
-
-double TimeSeries::sdev() const {
- if (mCount < 1) {
- return 0.0;
- }
-
- double m = mean();
-
- double sum = 0.0;
- for (size_t i = 0; i < mCount; ++i) {
- double tmp = mValues[i] - m;
- tmp *= tmp;
-
- sum += tmp;
- }
-
- return sqrt(sum / mCount);
-}
-#endif // DEBUG_JITTER
-
-////////////////////////////////////////////////////////////////////////////////
-
-static size_t kMaxRTPPacketSize = 1500;
-static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
-
-Sender::Sender(
- const sp<ANetworkSession> &netSession,
- const sp<AMessage> &notify)
- : mNetSession(netSession),
- mNotify(notify),
- mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
- mTransportMode(TRANSPORT_UDP),
- mRTPChannel(0),
- mRTCPChannel(0),
- mRTPPort(0),
- mRTPSessionID(0),
- mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION
- mRTPRetransmissionSessionID(0),
- mRTCPRetransmissionSessionID(0),
-#endif
- mClientRTPPort(0),
- mClientRTCPPort(0),
- mRTPConnected(false),
- mRTCPConnected(false),
- mFirstOutputBufferReadyTimeUs(-1ll),
- mFirstOutputBufferSentTimeUs(-1ll),
- mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION
- mRTPRetransmissionSeqNo(0),
-#endif
- mLastNTPTime(0),
- mLastRTPTime(0),
- mNumRTPSent(0),
- mNumRTPOctetsSent(0),
- mNumSRsSent(0),
- mSendSRPending(false)
-#if ENABLE_RETRANSMISSION
- ,mHistoryLength(0)
-#endif
-#if TRACK_BANDWIDTH
- ,mFirstPacketTimeUs(-1ll)
- ,mTotalBytesSent(0ll)
-#endif
-#if LOG_TRANSPORT_STREAM
- ,mLogFile(NULL)
-#endif
-{
- mTSQueue->setRange(0, 12);
-
-#if LOG_TRANSPORT_STREAM
- mLogFile = fopen("/system/etc/log.ts", "wb");
-#endif
-}
-
-Sender::~Sender() {
-#if ENABLE_RETRANSMISSION
- if (mRTCPRetransmissionSessionID != 0) {
- mNetSession->destroySession(mRTCPRetransmissionSessionID);
- }
-
- if (mRTPRetransmissionSessionID != 0) {
- mNetSession->destroySession(mRTPRetransmissionSessionID);
- }
-#endif
-
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- }
-
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- }
-
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fclose(mLogFile);
- mLogFile = NULL;
- }
-#endif
-}
-
-status_t Sender::init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- TransportMode transportMode) {
- mClientIP = clientIP;
- mTransportMode = transportMode;
-
- if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
- mRTPChannel = clientRtp;
- mRTCPChannel = clientRtcp;
- mRTPPort = 0;
- mRTPSessionID = 0;
- mRTCPSessionID = 0;
- return OK;
- }
-
- mRTPChannel = 0;
- mRTCPChannel = 0;
-
- if (mTransportMode == TRANSPORT_TCP) {
- // XXX This is wrong, we need to allocate sockets here, we only
- // need to do this because the dongles are not establishing their
- // end until after PLAY instead of before SETUP.
- mRTPPort = 20000;
- mRTPSessionID = 0;
- mRTCPSessionID = 0;
- mClientRTPPort = clientRtp;
- mClientRTCPPort = clientRtcp;
- return OK;
- }
-
- int serverRtp;
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
-#if ENABLE_RETRANSMISSION
- sp<AMessage> rtpRetransmissionNotify =
- new AMessage(kWhatRTPRetransmissionNotify, id());
-
- sp<AMessage> rtcpRetransmissionNotify =
- new AMessage(kWhatRTCPRetransmissionNotify, id());
-#endif
-
- status_t err;
- for (serverRtp = 15550;; serverRtp += 2) {
- int32_t rtpSession;
- if (mTransportMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- serverRtp, clientIP, clientRtp,
- rtpNotify, &rtpSession);
- } else {
- err = mNetSession->createTCPDatagramSession(
- serverRtp, clientIP, clientRtp,
- rtpNotify, &rtpSession);
- }
-
- if (err != OK) {
- ALOGI("failed to create RTP socket on port %d", serverRtp);
- continue;
- }
-
- int32_t rtcpSession = 0;
-
- if (clientRtcp >= 0) {
- if (mTransportMode == TRANSPORT_UDP) {
- err = mNetSession->createUDPSession(
- serverRtp + 1, clientIP, clientRtcp,
- rtcpNotify, &rtcpSession);
- } else {
- err = mNetSession->createTCPDatagramSession(
- serverRtp + 1, clientIP, clientRtcp,
- rtcpNotify, &rtcpSession);
- }
-
- if (err != OK) {
- ALOGI("failed to create RTCP socket on port %d", serverRtp + 1);
-
- mNetSession->destroySession(rtpSession);
- continue;
- }
- }
-
-#if ENABLE_RETRANSMISSION
- if (mTransportMode == TRANSPORT_UDP) {
- int32_t rtpRetransmissionSession;
-
- err = mNetSession->createUDPSession(
- serverRtp + kRetransmissionPortOffset,
- clientIP,
- clientRtp + kRetransmissionPortOffset,
- rtpRetransmissionNotify,
- &rtpRetransmissionSession);
-
- if (err != OK) {
- mNetSession->destroySession(rtcpSession);
- mNetSession->destroySession(rtpSession);
- continue;
- }
-
- CHECK_GE(clientRtcp, 0);
-
- int32_t rtcpRetransmissionSession;
- err = mNetSession->createUDPSession(
- serverRtp + 1 + kRetransmissionPortOffset,
- clientIP,
- clientRtp + 1 + kRetransmissionPortOffset,
- rtcpRetransmissionNotify,
- &rtcpRetransmissionSession);
-
- if (err != OK) {
- mNetSession->destroySession(rtpRetransmissionSession);
- mNetSession->destroySession(rtcpSession);
- mNetSession->destroySession(rtpSession);
- continue;
- }
-
- mRTPRetransmissionSessionID = rtpRetransmissionSession;
- mRTCPRetransmissionSessionID = rtcpRetransmissionSession;
-
- ALOGI("rtpRetransmissionSessionID = %d, "
- "rtcpRetransmissionSessionID = %d",
- rtpRetransmissionSession, rtcpRetransmissionSession);
- }
-#endif
-
- mRTPPort = serverRtp;
- mRTPSessionID = rtpSession;
- mRTCPSessionID = rtcpSession;
-
- ALOGI("rtpSessionID = %d, rtcpSessionID = %d", rtpSession, rtcpSession);
- break;
- }
-
- if (mRTPPort == 0) {
- return UNKNOWN_ERROR;
- }
-
- return OK;
-}
-
-status_t Sender::finishInit() {
- if (mTransportMode != TRANSPORT_TCP) {
- notifyInitDone();
- return OK;
- }
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
-
- status_t err = mNetSession->createTCPDatagramSession(
- mRTPPort, mClientIP.c_str(), mClientRTPPort,
- rtpNotify, &mRTPSessionID);
-
- if (err != OK) {
- return err;
- }
-
- if (mClientRTCPPort >= 0) {
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-
- err = mNetSession->createTCPDatagramSession(
- mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
- rtcpNotify, &mRTCPSessionID);
-
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-
-int32_t Sender::getRTPPort() const {
- return mRTPPort;
-}
-
-void Sender::queuePackets(
- int64_t timeUs, const sp<ABuffer> &packets) {
- bool isVideo = false;
-
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
- }
-
- int64_t delayUs;
- int64_t whenUs;
-
- if (mFirstOutputBufferReadyTimeUs < 0ll) {
- mFirstOutputBufferReadyTimeUs = timeUs;
- mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs();
- delayUs = 0ll;
- } else {
- int64_t nowUs = ALooper::GetNowUs();
-
- whenUs = (timeUs - mFirstOutputBufferReadyTimeUs)
- + mFirstOutputBufferSentTimeUs;
-
- delayUs = whenUs - nowUs;
- }
-
- sp<AMessage> msg = new AMessage(kWhatQueuePackets, id());
- msg->setBuffer("packets", packets);
-
- packets->meta()->setInt64("timeUs", timeUs);
- packets->meta()->setInt64("whenUs", whenUs);
- packets->meta()->setInt64("delayUs", delayUs);
- msg->post(delayUs > 0 ? delayUs : 0);
-}
-
-void Sender::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRTPNotify:
- case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION
- case kWhatRTPRetransmissionNotify:
- case kWhatRTCPRetransmissionNotify:
-#endif
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- int32_t errorOccuredDuringSend;
- CHECK(msg->findInt32("send", &errorOccuredDuringSend));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION
- || msg->what() == kWhatRTPRetransmissionNotify
-#endif
- ) && !errorOccuredDuringSend) {
- // This is ok, we don't expect to receive anything on
- // the RTP socket.
- break;
- }
-
- ALOGE("An error occurred during %s in session %d "
- "(%d, '%s' (%s)).",
- errorOccuredDuringSend ? "send" : "receive",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mRTPSessionID) {
- mRTPSessionID = 0;
- } else if (sessionID == mRTCPSessionID) {
- mRTCPSessionID = 0;
- }
-#if ENABLE_RETRANSMISSION
- else if (sessionID == mRTPRetransmissionSessionID) {
- mRTPRetransmissionSessionID = 0;
- } else if (sessionID == mRTCPRetransmissionSessionID) {
- mRTCPRetransmissionSessionID = 0;
- }
-#endif
-
- notifySessionDead();
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- status_t err;
- if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION
- || msg->what() == kWhatRTCPRetransmissionNotify
-#endif
- )
- {
- err = parseRTCP(data);
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- CHECK_EQ(mTransportMode, TRANSPORT_TCP);
-
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- if (sessionID == mRTPSessionID) {
- CHECK(!mRTPConnected);
- mRTPConnected = true;
- ALOGI("RTP Session now connected.");
- } else if (sessionID == mRTCPSessionID) {
- CHECK(!mRTCPConnected);
- mRTCPConnected = true;
- ALOGI("RTCP Session now connected.");
- } else {
- TRESPASS();
- }
-
- if (mRTPConnected
- && (mClientRTCPPort < 0 || mRTCPConnected)) {
- notifyInitDone();
- }
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatQueuePackets:
- {
- sp<ABuffer> packets;
- CHECK(msg->findBuffer("packets", &packets));
-
- onQueuePackets(packets);
- break;
- }
-
- case kWhatSendSR:
- {
- mSendSRPending = false;
-
- if (mRTCPSessionID == 0) {
- break;
- }
-
- onSendSR();
-
- scheduleSendSR();
- break;
- }
- }
-}
-
-void Sender::onQueuePackets(const sp<ABuffer> &packets) {
-#if DEBUG_JITTER
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- static int64_t lastTimeUs = 0ll;
- int64_t nowUs = ALooper::GetNowUs();
-
- static TimeSeries series;
- series.add((double)(nowUs - lastTimeUs));
-
- ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f",
- nowUs - lastTimeUs, series.mean(), series.sdev());
-
- lastTimeUs = nowUs;
- }
-#endif
-
- int64_t startTimeUs = ALooper::GetNowUs();
-
- for (size_t offset = 0;
- offset < packets->size(); offset += 188) {
- bool lastTSPacket = (offset + 188 >= packets->size());
-
- appendTSData(
- packets->data() + offset,
- 188,
- true /* timeDiscontinuity */,
- lastTSPacket /* flush */);
- }
-
-#if 0
- int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs;
-
- int64_t whenUs;
- CHECK(packets->meta()->findInt64("whenUs", &whenUs));
-
- int64_t delayUs;
- CHECK(packets->meta()->findInt64("delayUs", &delayUs));
-
- bool isVideo = false;
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- if (nowUs - whenUs > 2000) {
- ALOGI("[%s] delayUs = %lld us, delta = %lld us",
- isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs);
- }
-#endif
-
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fwrite(packets->data(), 1, packets->size(), mLogFile);
- }
-#endif
-}
-
-ssize_t Sender::appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush) {
- CHECK_EQ(size, 188);
-
- CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
-
- memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
- mTSQueue->setRange(0, mTSQueue->size() + size);
-
- if (flush || mTSQueue->size() == mTSQueue->capacity()) {
- // flush
-
- int64_t nowUs = ALooper::GetNowUs();
-
-#if TRACK_BANDWIDTH
- if (mFirstPacketTimeUs < 0ll) {
- mFirstPacketTimeUs = nowUs;
- }
-#endif
-
- // 90kHz time scale
- uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
- uint8_t *rtp = mTSQueue->data();
- rtp[0] = 0x80;
- rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- ++mRTPSeqNo;
- ++mNumRTPSent;
- mNumRTPOctetsSent += mTSQueue->size() - 12;
-
- mLastRTPTime = rtpTime;
- mLastNTPTime = GetNowNTP();
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
-
- sp<ABuffer> data = new ABuffer(mTSQueue->size());
- memcpy(data->data(), rtp, mTSQueue->size());
-
- notify->setInt32("channel", mRTPChannel);
- notify->setBuffer("data", data);
- notify->post();
- } else {
- sendPacket(mRTPSessionID, rtp, mTSQueue->size());
-
-#if TRACK_BANDWIDTH
- mTotalBytesSent += mTSQueue->size();
- int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
- if (delayUs > 0ll) {
- ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
- mTotalBytesSent * 8.0 / delayUs);
- }
-#endif
- }
-
-#if ENABLE_RETRANSMISSION
- mTSQueue->setInt32Data(mRTPSeqNo - 1);
-
- mHistory.push_back(mTSQueue);
- ++mHistoryLength;
-
- if (mHistoryLength > kMaxHistoryLength) {
- mTSQueue = *mHistory.begin();
- mHistory.erase(mHistory.begin());
-
- --mHistoryLength;
- } else {
- mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
- }
-#endif
-
- mTSQueue->setRange(0, 12);
- }
-
- return size;
-}
-
-void Sender::scheduleSendSR() {
- if (mSendSRPending || mRTCPSessionID == 0) {
- return;
- }
-
- mSendSRPending = true;
- (new AMessage(kWhatSendSR, id()))->post(kSendSRIntervalUs);
-}
-
-void Sender::addSR(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
-
- // TODO: Use macros/utility functions to clean up all the bitshifts below.
-
- data[0] = 0x80 | 0;
- data[1] = 200; // SR
- data[2] = 0;
- data[3] = 6;
- data[4] = kSourceID >> 24;
- data[5] = (kSourceID >> 16) & 0xff;
- data[6] = (kSourceID >> 8) & 0xff;
- data[7] = kSourceID & 0xff;
-
- data[8] = mLastNTPTime >> (64 - 8);
- data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
- data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
- data[11] = (mLastNTPTime >> 32) & 0xff;
- data[12] = (mLastNTPTime >> 24) & 0xff;
- data[13] = (mLastNTPTime >> 16) & 0xff;
- data[14] = (mLastNTPTime >> 8) & 0xff;
- data[15] = mLastNTPTime & 0xff;
-
- data[16] = (mLastRTPTime >> 24) & 0xff;
- data[17] = (mLastRTPTime >> 16) & 0xff;
- data[18] = (mLastRTPTime >> 8) & 0xff;
- data[19] = mLastRTPTime & 0xff;
-
- data[20] = mNumRTPSent >> 24;
- data[21] = (mNumRTPSent >> 16) & 0xff;
- data[22] = (mNumRTPSent >> 8) & 0xff;
- data[23] = mNumRTPSent & 0xff;
-
- data[24] = mNumRTPOctetsSent >> 24;
- data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
- data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
- data[27] = mNumRTPOctetsSent & 0xff;
-
- buffer->setRange(buffer->offset(), buffer->size() + 28);
-}
-
-void Sender::addSDES(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
- data[0] = 0x80 | 1;
- data[1] = 202; // SDES
- data[4] = kSourceID >> 24;
- data[5] = (kSourceID >> 16) & 0xff;
- data[6] = (kSourceID >> 8) & 0xff;
- data[7] = kSourceID & 0xff;
-
- size_t offset = 8;
-
- data[offset++] = 1; // CNAME
-
- static const char *kCNAME = "someone@somewhere";
- data[offset++] = strlen(kCNAME);
-
- memcpy(&data[offset], kCNAME, strlen(kCNAME));
- offset += strlen(kCNAME);
-
- data[offset++] = 7; // NOTE
-
- static const char *kNOTE = "Hell's frozen over.";
- data[offset++] = strlen(kNOTE);
-
- memcpy(&data[offset], kNOTE, strlen(kNOTE));
- offset += strlen(kNOTE);
-
- 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);
-}
-
-// static
-uint64_t Sender::GetNowNTP() {
- uint64_t nowUs = ALooper::GetNowUs();
-
- nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
-
- uint64_t hi = nowUs / 1000000ll;
- uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
-
- return (hi << 32) | lo;
-}
-
-void Sender::onSendSR() {
- sp<ABuffer> buffer = new ABuffer(1500);
- buffer->setRange(0, 0);
-
- addSR(buffer);
- addSDES(buffer);
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
- notify->setInt32("channel", mRTCPChannel);
- notify->setBuffer("data", buffer);
- notify->post();
- } else {
- sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
- }
-
- ++mNumSRsSent;
-}
-
-#if ENABLE_RETRANSMISSION
-status_t Sender::parseTSFB(
- const uint8_t *data, size_t size) {
- if ((data[0] & 0x1f) != 1) {
- return ERROR_UNSUPPORTED; // We only support NACK for now.
- }
-
- uint32_t srcId = U32_AT(&data[8]);
- if (srcId != kSourceID) {
- return ERROR_MALFORMED;
- }
-
- for (size_t i = 12; i < size; i += 4) {
- uint16_t seqNo = U16_AT(&data[i]);
- uint16_t blp = U16_AT(&data[i + 2]);
-
- List<sp<ABuffer> >::iterator it = mHistory.begin();
- bool foundSeqNo = false;
- while (it != mHistory.end()) {
- const sp<ABuffer> &buffer = *it;
-
- uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
-
- bool retransmit = false;
- if (bufferSeqNo == seqNo) {
- retransmit = true;
- } else if (blp != 0) {
- for (size_t i = 0; i < 16; ++i) {
- if ((blp & (1 << i))
- && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
- blp &= ~(1 << i);
- retransmit = true;
- }
- }
- }
-
- if (retransmit) {
- ALOGI("retransmitting seqNo %d", bufferSeqNo);
-
- sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
- uint8_t *rtp = retransRTP->data();
- memcpy(rtp, buffer->data(), 12);
- rtp[2] = (mRTPRetransmissionSeqNo >> 8) & 0xff;
- rtp[3] = mRTPRetransmissionSeqNo & 0xff;
- rtp[12] = (bufferSeqNo >> 8) & 0xff;
- rtp[13] = bufferSeqNo & 0xff;
- memcpy(&rtp[14], buffer->data() + 12, buffer->size() - 12);
-
- ++mRTPRetransmissionSeqNo;
-
- sendPacket(
- mRTPRetransmissionSessionID,
- retransRTP->data(), retransRTP->size());
-
- if (bufferSeqNo == seqNo) {
- foundSeqNo = true;
- }
-
- if (foundSeqNo && blp == 0) {
- break;
- }
- }
-
- ++it;
- }
-
- if (!foundSeqNo || blp != 0) {
- ALOGI("Some sequence numbers were no longer available for "
- "retransmission");
- }
- }
-
- return OK;
-}
-#endif
-
-status_t Sender::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:
- case 201: // RR
- case 202: // SDES
- case 203:
- case 204: // APP
- break;
-
-#if ENABLE_RETRANSMISSION
- case 205: // TSFB (transport layer specific feedback)
- parseTSFB(data, headerLength);
- break;
-#endif
-
- case 206: // PSFB (payload specific feedback)
- hexdump(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 Sender::sendPacket(
- int32_t sessionID, const void *data, size_t size) {
- return mNetSession->sendRequest(sessionID, data, size);
-}
-
-void Sender::notifyInitDone() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatInitDone);
- notify->post();
-}
-
-void Sender::notifySessionDead() {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatSessionDead);
- notify->post();
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h
deleted file mode 100644
index e476e84..0000000
--- a/media/libstagefright/wifi-display/source/Sender.h
+++ /dev/null
@@ -1,166 +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 SENDER_H_
-
-#define SENDER_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-#define LOG_TRANSPORT_STREAM 0
-#define ENABLE_RETRANSMISSION 0
-#define TRACK_BANDWIDTH 0
-
-struct ABuffer;
-struct ANetworkSession;
-
-struct Sender : public AHandler {
- Sender(const sp<ANetworkSession> &netSession, const sp<AMessage> &notify);
-
- enum {
- kWhatInitDone,
- kWhatSessionDead,
- kWhatBinaryData,
- };
-
- enum TransportMode {
- TRANSPORT_UDP,
- TRANSPORT_TCP_INTERLEAVED,
- TRANSPORT_TCP,
- };
- status_t init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- TransportMode transportMode);
-
- status_t finishInit();
-
- int32_t getRTPPort() const;
-
- void queuePackets(int64_t timeUs, const sp<ABuffer> &packets);
- void scheduleSendSR();
-
-protected:
- virtual ~Sender();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatQueuePackets,
- kWhatSendSR,
- kWhatRTPNotify,
- kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION
- kWhatRTPRetransmissionNotify,
- kWhatRTCPRetransmissionNotify
-#endif
- };
-
- static const int64_t kSendSRIntervalUs = 10000000ll;
-
- static const uint32_t kSourceID = 0xdeadbeef;
- static const size_t kMaxHistoryLength = 128;
-
-#if ENABLE_RETRANSMISSION
- static const size_t kRetransmissionPortOffset = 120;
-#endif
-
- sp<ANetworkSession> mNetSession;
- sp<AMessage> mNotify;
-
- sp<ABuffer> mTSQueue;
-
- TransportMode mTransportMode;
- AString mClientIP;
-
- // in TCP mode
- int32_t mRTPChannel;
- int32_t mRTCPChannel;
-
- // in UDP mode
- int32_t mRTPPort;
- int32_t mRTPSessionID;
- int32_t mRTCPSessionID;
-
-#if ENABLE_RETRANSMISSION
- int32_t mRTPRetransmissionSessionID;
- int32_t mRTCPRetransmissionSessionID;
-#endif
-
- int32_t mClientRTPPort;
- int32_t mClientRTCPPort;
- bool mRTPConnected;
- bool mRTCPConnected;
-
-
- int64_t mFirstOutputBufferReadyTimeUs;
- int64_t mFirstOutputBufferSentTimeUs;
-
- uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION
- uint32_t mRTPRetransmissionSeqNo;
-#endif
-
- uint64_t mLastNTPTime;
- uint32_t mLastRTPTime;
- uint32_t mNumRTPSent;
- uint32_t mNumRTPOctetsSent;
- uint32_t mNumSRsSent;
-
- bool mSendSRPending;
-
-#if ENABLE_RETRANSMISSION
- List<sp<ABuffer> > mHistory;
- size_t mHistoryLength;
-#endif
-
-#if TRACK_BANDWIDTH
- int64_t mFirstPacketTimeUs;
- uint64_t mTotalBytesSent;
-#endif
-
- void onSendSR();
- void addSR(const sp<ABuffer> &buffer);
- void addSDES(const sp<ABuffer> &buffer);
- static uint64_t GetNowNTP();
-
-#if LOG_TRANSPORT_STREAM
- FILE *mLogFile;
-#endif
-
- ssize_t appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush);
-
- void onQueuePackets(const sp<ABuffer> &packets);
-
-#if ENABLE_RETRANSMISSION
- status_t parseTSFB(const uint8_t *data, size_t size);
-#endif
-
- status_t parseRTCP(const sp<ABuffer> &buffer);
-
- status_t sendPacket(int32_t sessionID, const void *data, size_t size);
-
- void notifyInitDone();
- void notifySessionDead();
-
- DISALLOW_EVIL_CONSTRUCTORS(Sender);
-};
-
-} // namespace android
-
-#endif // SENDER_H_
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index a5679ad..edcc087 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -58,6 +58,7 @@ struct TSPacketizer::Track : public RefBase {
sp<ABuffer> descriptorAt(size_t index) const;
void finalize();
+ void extractCSDIfNecessary();
protected:
virtual ~Track();
@@ -77,6 +78,7 @@ private:
bool mAudioLacksATDSHeaders;
bool mFinalized;
+ bool mExtractedCSD;
DISALLOW_EVIL_CONSTRUCTORS(Track);
};
@@ -90,14 +92,21 @@ TSPacketizer::Track::Track(
mStreamID(streamID),
mContinuityCounter(0),
mAudioLacksATDSHeaders(false),
- mFinalized(false) {
+ mFinalized(false),
+ mExtractedCSD(false) {
CHECK(format->findString("mime", &mMIME));
+}
+
+void TSPacketizer::Track::extractCSDIfNecessary() {
+ if (mExtractedCSD) {
+ return;
+ }
if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
|| !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
for (size_t i = 0;; ++i) {
sp<ABuffer> csd;
- if (!format->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
+ if (!mFormat->findBuffer(StringPrintf("csd-%d", i).c_str(), &csd)) {
break;
}
@@ -111,6 +120,8 @@ TSPacketizer::Track::Track(
}
}
}
+
+ mExtractedCSD = true;
}
TSPacketizer::Track::~Track() {
@@ -250,12 +261,24 @@ void TSPacketizer::Track::finalize() {
data[0] = 40; // descriptor_tag
data[1] = 4; // descriptor_length
- CHECK_EQ(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;
@@ -319,10 +342,38 @@ void TSPacketizer::Track::finalize() {
////////////////////////////////////////////////////////////////////////////////
-TSPacketizer::TSPacketizer()
- : mPATContinuityCounter(0),
+TSPacketizer::TSPacketizer(uint32_t flags)
+ : mFlags(flags),
+ mPATContinuityCounter(0),
mPMTContinuityCounter(0) {
initCrcTable();
+
+ if (flags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR)) {
+ int32_t hdcpVersion;
+ if (flags & EMIT_HDCP20_DESCRIPTOR) {
+ CHECK(!(flags & EMIT_HDCP21_DESCRIPTOR));
+ hdcpVersion = 0x20;
+ } else {
+ CHECK(!(flags & EMIT_HDCP20_DESCRIPTOR));
+
+ // HDCP2.0 _and_ HDCP 2.1 specs say to set the version
+ // inside the HDCP descriptor to 0x20!!!
+ hdcpVersion = 0x20;
+ }
+
+ // HDCP descriptor
+ sp<ABuffer> descriptor = new ABuffer(7);
+ uint8_t *data = descriptor->data();
+ data[0] = 0x05; // descriptor_tag
+ data[1] = 5; // descriptor_length
+ data[2] = 'H';
+ data[3] = 'D';
+ data[4] = 'C';
+ data[5] = 'P';
+ data[6] = hdcpVersion;
+
+ mProgramInfoDescriptors.push_back(descriptor);
+ }
}
TSPacketizer::~TSPacketizer() {
@@ -388,6 +439,17 @@ ssize_t TSPacketizer::addTrack(const sp<AMessage> &format) {
return mTracks.add(track);
}
+status_t TSPacketizer::extractCSDIfNecessary(size_t trackIndex) {
+ if (trackIndex >= mTracks.size()) {
+ return -ERANGE;
+ }
+
+ const sp<Track> &track = mTracks.itemAt(trackIndex);
+ track->extractCSDIfNecessary();
+
+ return OK;
+}
+
status_t TSPacketizer::packetize(
size_t trackIndex,
const sp<ABuffer> &_accessUnit,
@@ -452,16 +514,121 @@ status_t TSPacketizer::packetize(
// reserved = b1
// the first fragment of "buffer" follows
+ // Each transport packet (except for the last one contributing to the PES
+ // payload) must contain a multiple of 16 bytes of payload per HDCP spec.
+ bool alignPayload =
+ (mFlags & (EMIT_HDCP20_DESCRIPTOR | EMIT_HDCP21_DESCRIPTOR));
+
+ /*
+ a) The very first PES transport stream packet contains
+
+ 4 bytes of TS header
+ ... padding
+ 14 bytes of static PES header
+ PES_private_data_len + 1 bytes (only if PES_private_data_len > 0)
+ numStuffingBytes bytes
+
+ followed by the payload
+
+ b) Subsequent PES transport stream packets contain
+
+ 4 bytes of TS header
+ ... padding
+
+ followed by the payload
+ */
+
size_t PES_packet_length = accessUnit->size() + 8 + numStuffingBytes;
if (PES_private_data_len > 0) {
PES_packet_length += PES_private_data_len + 1;
}
- size_t numTSPackets;
- if (PES_packet_length <= 178) {
- numTSPackets = 1;
- } else {
- numTSPackets = 1 + ((PES_packet_length - 178) + 183) / 184;
+ size_t numTSPackets = 1;
+
+ {
+ // Make sure the PES header fits into a single TS packet:
+ size_t PES_header_size = 14 + numStuffingBytes;
+ if (PES_private_data_len > 0) {
+ PES_header_size += PES_private_data_len + 1;
+ }
+
+ CHECK_LE(PES_header_size, 188u - 4u);
+
+ size_t sizeAvailableForPayload = 188 - 4 - PES_header_size;
+ size_t numBytesOfPayload = accessUnit->size();
+
+ if (numBytesOfPayload > sizeAvailableForPayload) {
+ numBytesOfPayload = sizeAvailableForPayload;
+
+ if (alignPayload && numBytesOfPayload > 16) {
+ numBytesOfPayload -= (numBytesOfPayload % 16);
+ }
+ }
+
+ size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
+ ALOGV("packet 1 contains %zd padding bytes and %zd bytes of payload",
+ numPaddingBytes, numBytesOfPayload);
+
+ size_t numBytesOfPayloadRemaining = accessUnit->size() - numBytesOfPayload;
+
+#if 0
+ // The following hopefully illustrates the logic that led to the
+ // more efficient computation in the #else block...
+
+ while (numBytesOfPayloadRemaining > 0) {
+ size_t sizeAvailableForPayload = 188 - 4;
+
+ size_t numBytesOfPayload = numBytesOfPayloadRemaining;
+
+ if (numBytesOfPayload > sizeAvailableForPayload) {
+ numBytesOfPayload = sizeAvailableForPayload;
+
+ if (alignPayload && numBytesOfPayload > 16) {
+ numBytesOfPayload -= (numBytesOfPayload % 16);
+ }
+ }
+
+ size_t numPaddingBytes = sizeAvailableForPayload - numBytesOfPayload;
+ ALOGI("packet %zd contains %zd padding bytes and %zd bytes of payload",
+ numTSPackets + 1, numPaddingBytes, numBytesOfPayload);
+
+ numBytesOfPayloadRemaining -= numBytesOfPayload;
+ ++numTSPackets;
+ }
+#else
+ // This is how many bytes of payload each subsequent TS packet
+ // can contain at most.
+ sizeAvailableForPayload = 188 - 4;
+ size_t sizeAvailableForAlignedPayload = sizeAvailableForPayload;
+ if (alignPayload) {
+ // We're only going to use a subset of the available space
+ // since we need to make each fragment a multiple of 16 in size.
+ sizeAvailableForAlignedPayload -=
+ (sizeAvailableForAlignedPayload % 16);
+ }
+
+ size_t numFullTSPackets =
+ numBytesOfPayloadRemaining / sizeAvailableForAlignedPayload;
+
+ numTSPackets += numFullTSPackets;
+
+ numBytesOfPayloadRemaining -=
+ numFullTSPackets * sizeAvailableForAlignedPayload;
+
+ // numBytesOfPayloadRemaining < sizeAvailableForAlignedPayload
+ if (numFullTSPackets == 0 && numBytesOfPayloadRemaining > 0) {
+ // There wasn't enough payload left to form a full aligned payload,
+ // the last packet doesn't have to be aligned.
+ ++numTSPackets;
+ } else if (numFullTSPackets > 0
+ && numBytesOfPayloadRemaining
+ + sizeAvailableForAlignedPayload > sizeAvailableForPayload) {
+ // The last packet emitted had a full aligned payload and together
+ // with the bytes remaining does exceed the unaligned payload
+ // size, so we need another packet.
+ ++numTSPackets;
+ }
+#endif
}
if (flags & EMIT_PAT_AND_PMT) {
@@ -564,8 +731,9 @@ status_t TSPacketizer::packetize(
// reserved = b111
// PCR_PID = kPCR_PID (13 bits)
// reserved = b1111
- // program_info_length = 0x000
- // one or more elementary stream descriptions follow:
+ // program_info_length = 0x???
+ // program_info_descriptors follow
+ // one or more elementary stream descriptions follow:
// stream_type = 0x??
// reserved = b111
// elementary_PID = b? ???? ???? ???? (13 bits)
@@ -597,8 +765,21 @@ status_t TSPacketizer::packetize(
*ptr++ = 0x00;
*ptr++ = 0xe0 | (kPID_PCR >> 8);
*ptr++ = kPID_PCR & 0xff;
- *ptr++ = 0xf0;
- *ptr++ = 0x00;
+
+ size_t program_info_length = 0;
+ for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
+ program_info_length += mProgramInfoDescriptors.itemAt(i)->size();
+ }
+
+ CHECK_LT(program_info_length, 0x400);
+ *ptr++ = 0xf0 | (program_info_length >> 8);
+ *ptr++ = (program_info_length & 0xff);
+
+ for (size_t i = 0; i < mProgramInfoDescriptors.size(); ++i) {
+ const sp<ABuffer> &desc = mProgramInfoDescriptors.itemAt(i);
+ memcpy(ptr, desc->data(), desc->size());
+ ptr += desc->size();
+ }
for (size_t i = 0; i < mTracks.size(); ++i) {
const sp<Track> &track = mTracks.itemAt(i);
@@ -691,8 +872,6 @@ status_t TSPacketizer::packetize(
uint64_t PTS = (timeUs * 9ll) / 100ll;
- bool padding = (PES_packet_length < (188 - 10));
-
if (PES_packet_length >= 65536) {
// This really should only happen for video.
CHECK(track->isVideo());
@@ -701,19 +880,37 @@ status_t TSPacketizer::packetize(
PES_packet_length = 0;
}
+ size_t sizeAvailableForPayload = 188 - 4 - 14 - numStuffingBytes;
+ if (PES_private_data_len > 0) {
+ sizeAvailableForPayload -= PES_private_data_len + 1;
+ }
+
+ size_t copy = accessUnit->size();
+
+ if (copy > sizeAvailableForPayload) {
+ copy = sizeAvailableForPayload;
+
+ if (alignPayload && copy > 16) {
+ copy -= (copy % 16);
+ }
+ }
+
+ size_t numPaddingBytes = sizeAvailableForPayload - copy;
+
uint8_t *ptr = packetDataStart;
*ptr++ = 0x47;
*ptr++ = 0x40 | (track->PID() >> 8);
*ptr++ = track->PID() & 0xff;
- *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter();
- if (padding) {
- size_t paddingSize = 188 - 10 - PES_packet_length;
- *ptr++ = paddingSize - 1;
- if (paddingSize >= 2) {
+ *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10)
+ | track->incrementContinuityCounter();
+
+ if (numPaddingBytes > 0) {
+ *ptr++ = numPaddingBytes - 1;
+ if (numPaddingBytes >= 2) {
*ptr++ = 0x00;
- memset(ptr, 0xff, paddingSize - 2);
- ptr += paddingSize - 2;
+ memset(ptr, 0xff, numPaddingBytes - 2);
+ ptr += numPaddingBytes - 2;
}
}
@@ -749,25 +946,14 @@ status_t TSPacketizer::packetize(
*ptr++ = 0xff;
}
- // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload
-
- size_t sizeLeft = packetDataStart + 188 - ptr;
- size_t copy = accessUnit->size();
- if (copy > sizeLeft) {
- copy = sizeLeft;
- }
-
memcpy(ptr, accessUnit->data(), copy);
ptr += copy;
- CHECK_EQ(sizeLeft, copy);
- memset(ptr, 0xff, sizeLeft - copy);
+ CHECK_EQ(ptr, packetDataStart + 188);
packetDataStart += 188;
size_t offset = copy;
while (offset < accessUnit->size()) {
- bool padding = (accessUnit->size() - offset) < (188 - 4);
-
// for subsequent fragments of "buffer":
// 0x47
// transport_error_indicator = b0
@@ -779,35 +965,40 @@ status_t TSPacketizer::packetize(
// continuity_counter = b????
// the fragment of "buffer" follows.
+ size_t sizeAvailableForPayload = 188 - 4;
+
+ size_t copy = accessUnit->size() - offset;
+
+ if (copy > sizeAvailableForPayload) {
+ copy = sizeAvailableForPayload;
+
+ if (alignPayload && copy > 16) {
+ copy -= (copy % 16);
+ }
+ }
+
+ size_t numPaddingBytes = sizeAvailableForPayload - copy;
+
uint8_t *ptr = packetDataStart;
*ptr++ = 0x47;
*ptr++ = 0x00 | (track->PID() >> 8);
*ptr++ = track->PID() & 0xff;
- *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter();
+ *ptr++ = (numPaddingBytes > 0 ? 0x30 : 0x10)
+ | track->incrementContinuityCounter();
- if (padding) {
- size_t paddingSize = 188 - 4 - (accessUnit->size() - offset);
- *ptr++ = paddingSize - 1;
- if (paddingSize >= 2) {
+ if (numPaddingBytes > 0) {
+ *ptr++ = numPaddingBytes - 1;
+ if (numPaddingBytes >= 2) {
*ptr++ = 0x00;
- memset(ptr, 0xff, paddingSize - 2);
- ptr += paddingSize - 2;
+ memset(ptr, 0xff, numPaddingBytes - 2);
+ ptr += numPaddingBytes - 2;
}
}
- // 4 bytes of TS header leave 188 - 4 = 184 bytes for the payload
-
- size_t sizeLeft = packetDataStart + 188 - ptr;
- size_t copy = accessUnit->size() - offset;
- if (copy > sizeLeft) {
- copy = sizeLeft;
- }
-
memcpy(ptr, accessUnit->data() + offset, copy);
ptr += copy;
- CHECK_EQ(sizeLeft, copy);
- memset(ptr, 0xff, sizeLeft - copy);
+ CHECK_EQ(ptr, packetDataStart + 188);
offset += copy;
packetDataStart += 188;
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index a37917d..4a664ee 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -32,7 +32,11 @@ struct AMessage;
// Emits metadata tables (PAT and PMT) and timestamp stream (PCR) based
// on flags.
struct TSPacketizer : public RefBase {
- TSPacketizer();
+ enum {
+ EMIT_HDCP20_DESCRIPTOR = 1,
+ EMIT_HDCP21_DESCRIPTOR = 2,
+ };
+ TSPacketizer(uint32_t flags);
// Returns trackIndex or error.
ssize_t addTrack(const sp<AMessage> &format);
@@ -50,6 +54,8 @@ struct TSPacketizer : public RefBase {
const uint8_t *PES_private_data, size_t PES_private_data_len,
size_t numStuffingBytes = 0);
+ status_t extractCSDIfNecessary(size_t trackIndex);
+
// XXX to be removed once encoder config option takes care of this for
// encrypted mode.
sp<ABuffer> prependCSD(
@@ -66,8 +72,11 @@ private:
struct Track;
+ uint32_t mFlags;
Vector<sp<Track> > mTracks;
+ Vector<sp<ABuffer> > mProgramInfoDescriptors;
+
unsigned mPATContinuityCounter;
unsigned mPMTContinuityCounter;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index b16c5d0..05e4018 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -21,18 +21,19 @@
#include "WifiDisplaySource.h"
#include "PlaybackSession.h"
#include "Parameters.h"
-#include "ParsedMessage.h"
-#include "Sender.h"
+#include "rtp/RTPSender.h"
#include <binder/IServiceManager.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <media/IHDCP.h>
#include <media/IMediaPlayerService.h>
#include <media/IRemoteDisplayClient.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ParsedMessage.h>
#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
#include <arpa/inet.h>
#include <cutils/properties.h>
@@ -41,9 +42,13 @@
namespace android {
+// static
+const AString WifiDisplaySource::sUserAgent = MakeUserAgent();
+
WifiDisplaySource::WifiDisplaySource(
const sp<ANetworkSession> &netSession,
- const sp<IRemoteDisplayClient> &client)
+ const sp<IRemoteDisplayClient> &client,
+ const char *path)
: mState(INITIALIZED),
mNetSession(netSession),
mClient(client),
@@ -58,48 +63,71 @@ WifiDisplaySource::WifiDisplaySource(
mIsHDCP2_0(false),
mHDCPPort(0),
mHDCPInitializationComplete(false),
- mSetupTriggerDeferred(false)
-{
+ mSetupTriggerDeferred(false),
+ mPlaybackSessionEstablished(false) {
+ if (path != NULL) {
+ mMediaPath.setTo(path);
+ }
+
+ mSupportedSourceVideoFormats.disableAll();
+
+ 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() {
}
-status_t WifiDisplaySource::start(const char *iface) {
- CHECK_EQ(mState, INITIALIZED);
-
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("iface", iface);
-
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
+static status_t PostAndAwaitResponse(
+ const sp<AMessage> &msg, sp<AMessage> *response) {
+ status_t err = msg->postAndAwaitResponse(response);
if (err != OK) {
return err;
}
- if (!response->findInt32("err", &err)) {
+ if (response == NULL || !(*response)->findInt32("err", &err)) {
err = OK;
}
return err;
}
+status_t WifiDisplaySource::start(const char *iface) {
+ CHECK_EQ(mState, INITIALIZED);
+
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+ msg->setString("iface", iface);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t WifiDisplaySource::stop() {
sp<AMessage> msg = new AMessage(kWhatStop, id());
sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
+ return PostAndAwaitResponse(msg, &response);
+}
- if (err != OK) {
- return err;
- }
+status_t WifiDisplaySource::pause() {
+ sp<AMessage> msg = new AMessage(kWhatPause, id());
- if (!response->findInt32("err", &err)) {
- err = OK;
- }
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
- return err;
+status_t WifiDisplaySource::resume() {
+ sp<AMessage> msg = new AMessage(kWhatResume, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
}
void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
@@ -144,9 +172,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
}
}
- if (err == OK) {
- mState = AWAITING_CLIENT_CONNECTION;
- }
+ mState = AWAITING_CLIENT_CONNECTION;
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
@@ -236,6 +262,26 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
mClient->onDisplayError(
IRemoteDisplayClient::kDisplayErrorUnknown);
}
+
+#if 0
+ // testing only.
+ char val[PROPERTY_VALUE_MAX];
+ if (property_get("media.wfd.trigger", val, NULL)) {
+ if (!strcasecmp(val, "pause") && mState == PLAYING) {
+ mState = PLAYING_TO_PAUSED;
+ sendTrigger(mClientSessionID, TRIGGER_PAUSE);
+ } else if (!strcasecmp(val, "play")
+ && mState == PAUSED) {
+ mState = PAUSED_TO_PLAYING;
+ sendTrigger(mClientSessionID, TRIGGER_PLAY);
+ }
+ }
+#endif
+ break;
+ }
+
+ case ANetworkSession::kWhatNetworkStall:
+ {
break;
}
@@ -254,8 +300,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
if (mState >= AWAITING_CLIENT_PLAY) {
// We have a session, i.e. a previous SETUP succeeded.
- status_t err = sendM5(
- mClientSessionID, true /* requestShutdown */);
+ status_t err = sendTrigger(
+ mClientSessionID, TRIGGER_TEARDOWN);
if (err == OK) {
mState = AWAITING_CLIENT_TEARDOWN;
@@ -273,6 +319,46 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatPause:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ status_t err = OK;
+
+ if (mState != PLAYING) {
+ err = INVALID_OPERATION;
+ } else {
+ mState = PLAYING_TO_PAUSED;
+ sendTrigger(mClientSessionID, TRIGGER_PAUSE);
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatResume:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ status_t err = OK;
+
+ if (mState != PAUSED) {
+ err = INVALID_OPERATION;
+ } else {
+ mState = PAUSED_TO_PLAYING;
+ sendTrigger(mClientSessionID, TRIGGER_PLAY);
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
case kWhatReapDeadClients:
{
mReaperPending = false;
@@ -311,16 +397,43 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
mClient->onDisplayError(
IRemoteDisplayClient::kDisplayErrorUnknown);
} else if (what == PlaybackSession::kWhatSessionEstablished) {
+ mPlaybackSessionEstablished = true;
+
if (mClient != NULL) {
- mClient->onDisplayConnected(
- mClientInfo.mPlaybackSession->getSurfaceTexture(),
- mClientInfo.mPlaybackSession->width(),
- mClientInfo.mPlaybackSession->height(),
- mUsingHDCP
- ? IRemoteDisplayClient::kDisplayFlagSecure
- : 0);
+ if (!mSinkSupportsVideo) {
+ mClient->onDisplayConnected(
+ NULL, // SurfaceTexture
+ 0, // width,
+ 0, // height,
+ mUsingHDCP
+ ? IRemoteDisplayClient::kDisplayFlagSecure
+ : 0,
+ 0);
+ } else {
+ size_t width, height;
+
+ CHECK(VideoFormats::GetConfiguration(
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex,
+ &width,
+ &height,
+ NULL /* framesPerSecond */,
+ NULL /* interlaced */));
+
+ mClient->onDisplayConnected(
+ mClientInfo.mPlaybackSession
+ ->getSurfaceTexture(),
+ width,
+ height,
+ mUsingHDCP
+ ? IRemoteDisplayClient::kDisplayFlagSecure
+ : 0,
+ playbackSessionID);
+ }
}
+ finishPlay();
+
if (mState == ABOUT_TO_PLAY) {
mState = PLAYING;
}
@@ -400,7 +513,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) {
if (mSetupTriggerDeferred) {
mSetupTriggerDeferred = false;
- sendM5(mClientSessionID, false /* requestShutdown */);
+ sendTrigger(mClientSessionID, TRIGGER_SETUP);
}
break;
}
@@ -501,49 +614,41 @@ status_t WifiDisplaySource::sendM3(int32_t sessionID) {
}
status_t WifiDisplaySource::sendM4(int32_t sessionID) {
- // wfd_video_formats:
- // 1 byte "native"
- // 1 byte "preferred-display-mode-supported" 0 or 1
- // one or more avc codec structures
- // 1 byte profile
- // 1 byte level
- // 4 byte CEA mask
- // 4 byte VESA mask
- // 4 byte HH mask
- // 1 byte latency
- // 2 byte min-slice-slice
- // 2 byte slice-enc-params
- // 1 byte framerate-control-support
- // max-hres (none or 2 byte)
- // max-vres (none or 2 byte)
-
CHECK_EQ(sessionID, mClientSessionID);
- AString transportString = "UDP";
-
- char val[PROPERTY_VALUE_MAX];
- if (property_get("media.wfd.enable-tcp", val, NULL)
- && (!strcasecmp("true", val) || !strcmp("1", val))) {
- ALOGI("Using TCP transport.");
- transportString = "TCP";
- }
-
- // For 720p60:
- // use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
- // For 720p30:
- // use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
- // For 720p24:
- // use "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n"
- AString body = StringPrintf(
- "wfd_video_formats: "
- "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
- "wfd_audio_codecs: %s\r\n"
- "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n"
- "wfd_client_rtp_ports: RTP/AVP/%s;unicast %d 0 mode=play\r\n",
- (mUsingPCMAudio
- ? "LPCM 00000002 00" // 2 ch PCM 48kHz
- : "AAC 00000001 00"), // 2 ch AAC 48kHz
- mClientInfo.mLocalIP.c_str(), transportString.c_str(), mChosenRTPPort);
+ AString body;
+
+ if (mSinkSupportsVideo) {
+ body.append("wfd_video_formats: ");
+
+ VideoFormats chosenVideoFormat;
+ chosenVideoFormat.disableAll();
+ chosenVideoFormat.setNativeResolution(
+ mChosenVideoResolutionType, mChosenVideoResolutionIndex);
+ chosenVideoFormat.setProfileLevel(
+ mChosenVideoResolutionType, mChosenVideoResolutionIndex,
+ mChosenVideoProfile, mChosenVideoLevel);
+
+ body.append(chosenVideoFormat.getFormatSpec(true /* forM4Message */));
+ body.append("\r\n");
+ }
+
+ if (mSinkSupportsAudio) {
+ body.append(
+ StringPrintf("wfd_audio_codecs: %s\r\n",
+ (mUsingPCMAudio
+ ? "LPCM 00000002 00" // 2 ch PCM 48kHz
+ : "AAC 00000001 00"))); // 2 ch AAC 48kHz
+ }
+
+ body.append(
+ StringPrintf(
+ "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n",
+ mClientInfo.mLocalIP.c_str()));
+
+ body.append(
+ StringPrintf(
+ "wfd_client_rtp_ports: %s\r\n", mWfdClientRtpPorts.c_str()));
AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
AppendCommonResponse(&request, mNextCSeq);
@@ -568,13 +673,25 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) {
return OK;
}
-status_t WifiDisplaySource::sendM5(int32_t sessionID, bool requestShutdown) {
+status_t WifiDisplaySource::sendTrigger(
+ int32_t sessionID, TriggerType triggerType) {
AString body = "wfd_trigger_method: ";
- if (requestShutdown) {
- ALOGI("Sending TEARDOWN trigger.");
- body.append("TEARDOWN");
- } else {
- body.append("SETUP");
+ switch (triggerType) {
+ case TRIGGER_SETUP:
+ body.append("SETUP");
+ break;
+ case TRIGGER_TEARDOWN:
+ ALOGI("Sending TEARDOWN trigger.");
+ body.append("TEARDOWN");
+ break;
+ case TRIGGER_PAUSE:
+ body.append("PAUSE");
+ break;
+ case TRIGGER_PLAY:
+ body.append("PLAY");
+ break;
+ default:
+ TRESPASS();
}
body.append("\r\n");
@@ -623,6 +740,8 @@ status_t WifiDisplaySource::sendM16(int32_t sessionID) {
++mNextCSeq;
+ scheduleKeepAlive(sessionID);
+
return OK;
}
@@ -694,53 +813,120 @@ status_t WifiDisplaySource::onReceiveM3Response(
return ERROR_MALFORMED;
}
- unsigned port0, port1;
+ unsigned port0 = 0, port1 = 0;
if (sscanf(value.c_str(),
"RTP/AVP/UDP;unicast %u %u mode=play",
&port0,
- &port1) != 2
- || port0 == 0 || port0 > 65535 || port1 != 0) {
- ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)",
+ &port1) == 2
+ || sscanf(value.c_str(),
+ "RTP/AVP/TCP;unicast %u %u mode=play",
+ &port0,
+ &port1) == 2) {
+ if (port0 == 0 || port0 > 65535 || port1 != 0) {
+ ALOGE("Sink chose its wfd_client_rtp_ports poorly (%s)",
+ value.c_str());
+
+ return ERROR_MALFORMED;
+ }
+ } else if (strcmp(value.c_str(), "RTP/AVP/TCP;interleaved mode=play")) {
+ ALOGE("Unsupported value for wfd_client_rtp_ports (%s)",
value.c_str());
- return ERROR_MALFORMED;
+ return ERROR_UNSUPPORTED;
}
+ mWfdClientRtpPorts = value;
mChosenRTPPort = port0;
+ if (!params->findParameter("wfd_video_formats", &value)) {
+ ALOGE("Sink doesn't report its choice of wfd_video_formats.");
+ return ERROR_MALFORMED;
+ }
+
+ mSinkSupportsVideo = false;
+
+ if (!(value == "none")) {
+ mSinkSupportsVideo = true;
+ if (!mSupportedSinkVideoFormats.parseFormatSpec(value.c_str())) {
+ ALOGE("Failed to parse sink provided wfd_video_formats (%s)",
+ value.c_str());
+
+ return ERROR_MALFORMED;
+ }
+
+ if (!VideoFormats::PickBestFormat(
+ mSupportedSinkVideoFormats,
+ mSupportedSourceVideoFormats,
+ &mChosenVideoResolutionType,
+ &mChosenVideoResolutionIndex,
+ &mChosenVideoProfile,
+ &mChosenVideoLevel)) {
+ ALOGE("Sink and source share no commonly supported video "
+ "formats.");
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ size_t width, height, framesPerSecond;
+ bool interlaced;
+ CHECK(VideoFormats::GetConfiguration(
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex,
+ &width,
+ &height,
+ &framesPerSecond,
+ &interlaced));
+
+ 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.");
+ }
+
if (!params->findParameter("wfd_audio_codecs", &value)) {
ALOGE("Sink doesn't report its choice of wfd_audio_codecs.");
return ERROR_MALFORMED;
}
- if (value == "none") {
- ALOGE("Sink doesn't support audio at all.");
- return ERROR_UNSUPPORTED;
- }
+ mSinkSupportsAudio = false;
- uint32_t modes;
- GetAudioModes(value.c_str(), "AAC", &modes);
+ if (!(value == "none")) {
+ mSinkSupportsAudio = true;
- bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz
+ uint32_t modes;
+ GetAudioModes(value.c_str(), "AAC", &modes);
- GetAudioModes(value.c_str(), "LPCM", &modes);
+ bool supportsAAC = (modes & 1) != 0; // AAC 2ch 48kHz
- bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
+ GetAudioModes(value.c_str(), "LPCM", &modes);
- char val[PROPERTY_VALUE_MAX];
- if (supportsPCM
- && property_get("media.wfd.use-pcm-audio", val, NULL)
- && (!strcasecmp("true", val) || !strcmp("1", val))) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
- } else if (supportsAAC) {
- ALOGI("Using AAC audio.");
- mUsingPCMAudio = false;
- } else if (supportsPCM) {
- ALOGI("Using PCM audio.");
- mUsingPCMAudio = true;
+ bool supportsPCM = (modes & 2) != 0; // LPCM 2ch 48kHz
+
+ char val[PROPERTY_VALUE_MAX];
+ if (supportsPCM
+ && property_get("media.wfd.use-pcm-audio", val, NULL)
+ && (!strcasecmp("true", val) || !strcmp("1", val))) {
+ ALOGI("Using PCM audio.");
+ mUsingPCMAudio = true;
+ } else if (supportsAAC) {
+ ALOGI("Using AAC audio.");
+ mUsingPCMAudio = false;
+ } else if (supportsPCM) {
+ ALOGI("Using PCM audio.");
+ mUsingPCMAudio = true;
+ } else {
+ ALOGI("Sink doesn't support an audio format we do.");
+ return ERROR_UNSUPPORTED;
+ }
} else {
- ALOGI("Sink doesn't support an audio format we do.");
+ ALOGI("Sink doesn't support audio at all.");
+ }
+
+ if (!mSinkSupportsVideo && !mSinkSupportsAudio) {
+ ALOGE("Sink supports neither video nor audio...");
return ERROR_UNSUPPORTED;
}
@@ -773,8 +959,10 @@ status_t WifiDisplaySource::onReceiveM3Response(
status_t err = makeHDCP();
if (err != OK) {
- ALOGE("Unable to instantiate HDCP component.");
- return err;
+ ALOGE("Unable to instantiate HDCP component. "
+ "Not using HDCP after all.");
+
+ mUsingHDCP = false;
}
}
@@ -799,7 +987,7 @@ status_t WifiDisplaySource::onReceiveM4Response(
return OK;
}
- return sendM5(sessionID, false /* requestShutdown */);
+ return sendTrigger(sessionID, TRIGGER_SETUP);
}
status_t WifiDisplaySource::onReceiveM5Response(
@@ -824,8 +1012,6 @@ status_t WifiDisplaySource::onReceiveM16Response(
if (mClientInfo.mPlaybackSession != NULL) {
mClientInfo.mPlaybackSession->updateLiveness();
-
- scheduleKeepAlive(sessionID);
}
return OK;
@@ -982,7 +1168,7 @@ status_t WifiDisplaySource::onSetupRequest(
return ERROR_MALFORMED;
}
- Sender::TransportMode transportMode = Sender::TRANSPORT_UDP;
+ RTPSender::TransportMode rtpMode = RTPSender::TRANSPORT_UDP;
int clientRtp, clientRtcp;
if (transport.startsWith("RTP/AVP/TCP;")) {
@@ -991,7 +1177,7 @@ status_t WifiDisplaySource::onSetupRequest(
transport.c_str(), "interleaved", &interleaved)
&& sscanf(interleaved.c_str(), "%d-%d",
&clientRtp, &clientRtcp) == 2) {
- transportMode = Sender::TRANSPORT_TCP_INTERLEAVED;
+ rtpMode = RTPSender::TRANSPORT_TCP_INTERLEAVED;
} else {
bool badRequest = false;
@@ -1013,7 +1199,7 @@ status_t WifiDisplaySource::onSetupRequest(
return ERROR_MALFORMED;
}
- transportMode = Sender::TRANSPORT_TCP;
+ rtpMode = RTPSender::TRANSPORT_TCP;
}
} else if (transport.startsWith("RTP/AVP;unicast;")
|| transport.startsWith("RTP/AVP/UDP;unicast;")) {
@@ -1055,7 +1241,7 @@ status_t WifiDisplaySource::onSetupRequest(
sp<PlaybackSession> playbackSession =
new PlaybackSession(
- mNetSession, notify, mInterfaceAddr, mHDCP);
+ mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str());
looper()->registerHandler(playbackSession);
@@ -1072,12 +1258,24 @@ status_t WifiDisplaySource::onSetupRequest(
return ERROR_MALFORMED;
}
+ RTPSender::TransportMode rtcpMode = RTPSender::TRANSPORT_UDP;
+ if (clientRtcp < 0) {
+ rtcpMode = RTPSender::TRANSPORT_NONE;
+ }
+
status_t err = playbackSession->init(
mClientInfo.mRemoteIP.c_str(),
clientRtp,
+ rtpMode,
clientRtcp,
- transportMode,
- mUsingPCMAudio);
+ rtcpMode,
+ mSinkSupportsAudio,
+ mUsingPCMAudio,
+ mSinkSupportsVideo,
+ mChosenVideoResolutionType,
+ mChosenVideoResolutionIndex,
+ mChosenVideoProfile,
+ mChosenVideoLevel);
if (err != OK) {
looper()->unregisterHandler(playbackSession->id());
@@ -1101,7 +1299,7 @@ status_t WifiDisplaySource::onSetupRequest(
AString response = "RTSP/1.0 200 OK\r\n";
AppendCommonResponse(&response, cseq, playbackSessionID);
- if (transportMode == Sender::TRANSPORT_TCP_INTERLEAVED) {
+ if (rtpMode == RTPSender::TRANSPORT_TCP_INTERLEAVED) {
response.append(
StringPrintf(
"Transport: RTP/AVP/TCP;interleaved=%d-%d;",
@@ -1110,7 +1308,7 @@ status_t WifiDisplaySource::onSetupRequest(
int32_t serverRtp = playbackSession->getRTPPort();
AString transportString = "UDP";
- if (transportMode == Sender::TRANSPORT_TCP) {
+ if (rtpMode == RTPSender::TRANSPORT_TCP) {
transportString = "TCP";
}
@@ -1160,23 +1358,39 @@ status_t WifiDisplaySource::onPlayRequest(
return ERROR_MALFORMED;
}
- ALOGI("Received PLAY request.");
+ if (mState != AWAITING_CLIENT_PLAY
+ && mState != PAUSED_TO_PLAYING
+ && mState != PAUSED) {
+ ALOGW("Received PLAY request but we're in state %d", mState);
- status_t err = playbackSession->play();
- CHECK_EQ(err, (status_t)OK);
+ sendErrorResponse(
+ sessionID, "455 Method Not Valid in This State", cseq);
+
+ return INVALID_OPERATION;
+ }
+
+ ALOGI("Received PLAY request.");
+ if (mPlaybackSessionEstablished) {
+ finishPlay();
+ } else {
+ ALOGI("deferring PLAY request until session established.");
+ }
AString response = "RTSP/1.0 200 OK\r\n";
AppendCommonResponse(&response, cseq, playbackSessionID);
response.append("Range: npt=now-\r\n");
response.append("\r\n");
- err = mNetSession->sendRequest(sessionID, response.c_str());
+ status_t err = mNetSession->sendRequest(sessionID, response.c_str());
if (err != OK) {
return err;
}
- playbackSession->finishPlay();
+ if (mState == PAUSED_TO_PLAYING || mPlaybackSessionEstablished) {
+ mState = PLAYING;
+ return OK;
+ }
CHECK_EQ(mState, AWAITING_CLIENT_PLAY);
mState = ABOUT_TO_PLAY;
@@ -1184,6 +1398,14 @@ status_t WifiDisplaySource::onPlayRequest(
return OK;
}
+void WifiDisplaySource::finishPlay() {
+ const sp<PlaybackSession> &playbackSession =
+ mClientInfo.mPlaybackSession;
+
+ status_t err = playbackSession->play();
+ CHECK_EQ(err, (status_t)OK);
+}
+
status_t WifiDisplaySource::onPauseRequest(
int32_t sessionID,
int32_t cseq,
@@ -1197,6 +1419,12 @@ status_t WifiDisplaySource::onPauseRequest(
return ERROR_MALFORMED;
}
+ ALOGI("Received PAUSE request.");
+
+ if (mState != PLAYING_TO_PAUSED && mState != PLAYING) {
+ return INVALID_OPERATION;
+ }
+
status_t err = playbackSession->pause();
CHECK_EQ(err, (status_t)OK);
@@ -1206,6 +1434,12 @@ status_t WifiDisplaySource::onPauseRequest(
err = mNetSession->sendRequest(sessionID, response.c_str());
+ if (err != OK) {
+ return err;
+ }
+
+ mState = PAUSED;
+
return err;
}
@@ -1347,7 +1581,7 @@ void WifiDisplaySource::AppendCommonResponse(
response->append(buf);
response->append("\r\n");
- response->append("Server: Mine/1.0\r\n");
+ response->append(StringPrintf("Server: %s\r\n", sUserAgent.c_str()));
if (cseq >= 0) {
response->append(StringPrintf("CSeq: %d\r\n", cseq));
@@ -1457,10 +1691,13 @@ void WifiDisplaySource::HDCPObserver::notify(
status_t WifiDisplaySource::makeHDCP() {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
CHECK(service != NULL);
- mHDCP = service->makeHDCP();
+ mHDCP = service->makeHDCP(true /* createEncryptionModule */);
if (mHDCP == NULL) {
return ERROR_UNSUPPORTED;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 02fa0a6..750265f 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -18,9 +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>
@@ -37,11 +38,15 @@ struct WifiDisplaySource : public AHandler {
WifiDisplaySource(
const sp<ANetworkSession> &netSession,
- const sp<IRemoteDisplayClient> &client);
+ const sp<IRemoteDisplayClient> &client,
+ const char *path = NULL);
status_t start(const char *iface);
status_t stop();
+ status_t pause();
+ status_t resume();
+
protected:
virtual ~WifiDisplaySource();
virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -57,6 +62,9 @@ private:
AWAITING_CLIENT_PLAY,
ABOUT_TO_PLAY,
PLAYING,
+ PLAYING_TO_PAUSED,
+ PAUSED,
+ PAUSED_TO_PLAYING,
AWAITING_CLIENT_TEARDOWN,
STOPPING,
STOPPED,
@@ -66,6 +74,8 @@ private:
kWhatStart,
kWhatRTSPNotify,
kWhatStop,
+ kWhatPause,
+ kWhatResume,
kWhatReapDeadClients,
kWhatPlaybackSessionNotify,
kWhatKeepAlive,
@@ -101,16 +111,31 @@ private:
static const int64_t kPlaybackSessionTimeoutUs =
kPlaybackSessionTimeoutSecs * 1000000ll;
+ static const AString sUserAgent;
+
State mState;
+ VideoFormats mSupportedSourceVideoFormats;
sp<ANetworkSession> mNetSession;
sp<IRemoteDisplayClient> mClient;
+ AString mMediaPath;
struct in_addr mInterfaceAddr;
int32_t mSessionID;
uint32_t mStopReplyID;
+ AString mWfdClientRtpPorts;
int32_t mChosenRTPPort; // extracted from "wfd_client_rtp_ports"
+ bool mSinkSupportsVideo;
+ VideoFormats mSupportedSinkVideoFormats;
+
+ VideoFormats::ResolutionType mChosenVideoResolutionType;
+ size_t mChosenVideoResolutionIndex;
+ VideoFormats::ProfileType mChosenVideoProfile;
+ VideoFormats::LevelType mChosenVideoLevel;
+
+ bool mSinkSupportsAudio;
+
bool mUsingPCMAudio;
int32_t mClientSessionID;
@@ -139,13 +164,25 @@ private:
bool mHDCPInitializationComplete;
bool mSetupTriggerDeferred;
+ bool mPlaybackSessionEstablished;
+
status_t makeHDCP();
// <<<< HDCP specific section
status_t sendM1(int32_t sessionID);
status_t sendM3(int32_t sessionID);
status_t sendM4(int32_t sessionID);
- status_t sendM5(int32_t sessionID, bool requestShutdown);
+
+ enum TriggerType {
+ TRIGGER_SETUP,
+ TRIGGER_TEARDOWN,
+ TRIGGER_PAUSE,
+ TRIGGER_PLAY,
+ };
+
+ // M5
+ status_t sendTrigger(int32_t sessionID, TriggerType triggerType);
+
status_t sendM16(int32_t sessionID);
status_t onReceiveM1Response(
@@ -225,6 +262,8 @@ private:
void finishStopAfterDisconnectingClient();
void finishStop2();
+ void finishPlay();
+
DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySource);
};
diff --git a/media/libstagefright/wifi-display/udptest.cpp b/media/libstagefright/wifi-display/udptest.cpp
deleted file mode 100644
index 1cd82c3..0000000
--- a/media/libstagefright/wifi-display/udptest.cpp
+++ /dev/null
@@ -1,355 +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_NEBUG 0
-#define LOG_TAG "udptest"
-#include <utils/Log.h>
-
-#include "ANetworkSession.h"
-
-#include <binder/ProcessState.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-struct TestHandler : public AHandler {
- TestHandler(const sp<ANetworkSession> &netSession);
-
- void startServer(unsigned localPort);
- void startClient(const char *remoteHost, unsigned remotePort);
-
-protected:
- virtual ~TestHandler();
-
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum {
- kWhatStartServer,
- kWhatStartClient,
- kWhatUDPNotify,
- kWhatSendPacket,
- };
-
- sp<ANetworkSession> mNetSession;
-
- bool mIsServer;
- bool mConnected;
- int32_t mUDPSession;
- uint32_t mSeqNo;
- double mTotalTimeUs;
- int32_t mCount;
-
- void postSendPacket(int64_t delayUs = 0ll);
-
- DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
-};
-
-TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
- : mNetSession(netSession),
- mIsServer(false),
- mConnected(false),
- mUDPSession(0),
- mSeqNo(0),
- mTotalTimeUs(0.0),
- mCount(0) {
-}
-
-TestHandler::~TestHandler() {
-}
-
-void TestHandler::startServer(unsigned localPort) {
- sp<AMessage> msg = new AMessage(kWhatStartServer, id());
- msg->setInt32("localPort", localPort);
- msg->post();
-}
-
-void TestHandler::startClient(const char *remoteHost, unsigned remotePort) {
- sp<AMessage> msg = new AMessage(kWhatStartClient, id());
- msg->setString("remoteHost", remoteHost);
- msg->setInt32("remotePort", remotePort);
- msg->post();
-}
-
-void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStartClient:
- {
- AString remoteHost;
- CHECK(msg->findString("remoteHost", &remoteHost));
-
- int32_t remotePort;
- CHECK(msg->findInt32("remotePort", &remotePort));
-
- sp<AMessage> notify = new AMessage(kWhatUDPNotify, id());
-
- CHECK_EQ((status_t)OK,
- mNetSession->createUDPSession(
- 0 /* localPort */,
- remoteHost.c_str(),
- remotePort,
- notify,
- &mUDPSession));
-
- postSendPacket();
- break;
- }
-
- case kWhatStartServer:
- {
- mIsServer = true;
-
- int32_t localPort;
- CHECK(msg->findInt32("localPort", &localPort));
-
- sp<AMessage> notify = new AMessage(kWhatUDPNotify, id());
-
- CHECK_EQ((status_t)OK,
- mNetSession->createUDPSession(
- localPort, notify, &mUDPSession));
-
- break;
- }
-
- case kWhatSendPacket:
- {
- char buffer[12];
- memset(buffer, 0, sizeof(buffer));
-
- buffer[0] = mSeqNo >> 24;
- buffer[1] = (mSeqNo >> 16) & 0xff;
- buffer[2] = (mSeqNo >> 8) & 0xff;
- buffer[3] = mSeqNo & 0xff;
- ++mSeqNo;
-
- int64_t nowUs = ALooper::GetNowUs();
- buffer[4] = nowUs >> 56;
- buffer[5] = (nowUs >> 48) & 0xff;
- buffer[6] = (nowUs >> 40) & 0xff;
- buffer[7] = (nowUs >> 32) & 0xff;
- buffer[8] = (nowUs >> 24) & 0xff;
- buffer[9] = (nowUs >> 16) & 0xff;
- buffer[10] = (nowUs >> 8) & 0xff;
- buffer[11] = nowUs & 0xff;
-
- CHECK_EQ((status_t)OK,
- mNetSession->sendRequest(
- mUDPSession, buffer, sizeof(buffer)));
-
- postSendPacket(20000ll);
- break;
- }
-
- case kWhatUDPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::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);
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- if (mIsServer) {
- if (!mConnected) {
- AString fromAddr;
- CHECK(msg->findString("fromAddr", &fromAddr));
-
- int32_t fromPort;
- CHECK(msg->findInt32("fromPort", &fromPort));
-
- CHECK_EQ((status_t)OK,
- mNetSession->connectUDPSession(
- mUDPSession, fromAddr.c_str(), fromPort));
-
- mConnected = true;
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- sp<ABuffer> buffer = new ABuffer(data->size() + 8);
- memcpy(buffer->data(), data->data(), data->size());
-
- uint8_t *ptr = buffer->data() + data->size();
-
- *ptr++ = nowUs >> 56;
- *ptr++ = (nowUs >> 48) & 0xff;
- *ptr++ = (nowUs >> 40) & 0xff;
- *ptr++ = (nowUs >> 32) & 0xff;
- *ptr++ = (nowUs >> 24) & 0xff;
- *ptr++ = (nowUs >> 16) & 0xff;
- *ptr++ = (nowUs >> 8) & 0xff;
- *ptr++ = nowUs & 0xff;
-
- CHECK_EQ((status_t)OK,
- mNetSession->sendRequest(
- mUDPSession, buffer->data(), buffer->size()));
- } else {
- CHECK_EQ(data->size(), 20u);
-
- uint32_t seqNo = U32_AT(data->data());
- int64_t t1 = U64_AT(data->data() + 4);
- int64_t t2 = U64_AT(data->data() + 12);
-
- int64_t t3;
- CHECK(data->meta()->findInt64("arrivalTimeUs", &t3));
-
-#if 0
- printf("roundtrip seqNo %u, time = %lld us\n",
- seqNo, t3 - t1);
-#else
- mTotalTimeUs += t3 - t1;
- ++mCount;
- printf("avg. roundtrip time %.2f us\n", mTotalTimeUs / mCount);
-#endif
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void TestHandler::postSendPacket(int64_t delayUs) {
- (new AMessage(kWhatSendPacket, id()))->post(delayUs);
-}
-
-} // namespace android
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage: %s -c host[:port]\tconnect to test server\n"
- " -l \tcreate a test server\n",
- me);
-}
-
-int main(int argc, char **argv) {
- using namespace android;
-
- ProcessState::self()->startThreadPool();
-
- int32_t localPort = -1;
- int32_t connectToPort = -1;
- AString connectToHost;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l:")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- connectToHost = optarg;
- connectToPort = 49152;
- } else {
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 1 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- }
- break;
- }
-
- case 'l':
- {
- char *end;
- localPort = strtol(optarg, &end, 10);
-
- if (*end != '\0' || end == optarg
- || localPort < 1 || localPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- break;
- }
-
- case '?':
- case 'h':
- usage(argv[0]);
- exit(1);
- }
- }
-
- if (localPort < 0 && connectToPort < 0) {
- fprintf(stderr,
- "You need to select either client or server mode.\n");
- exit(1);
- }
-
- sp<ANetworkSession> netSession = new ANetworkSession;
- netSession->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<TestHandler> handler = new TestHandler(netSession);
- looper->registerHandler(handler);
-
- if (localPort >= 0) {
- handler->startServer(localPort);
- } else {
- handler->startClient(connectToHost.c_str(), connectToPort);
- }
-
- looper->start(true /* runOnCallingThread */);
-
- return 0;
-}
-
diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp
deleted file mode 100644
index 011edab..0000000
--- a/media/libstagefright/wifi-display/wfd.cpp
+++ /dev/null
@@ -1,123 +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 "sink/WifiDisplaySink.h"
-#include "source/WifiDisplaySource.h"
-
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <media/IMediaPlayerService.h>
-#include <media/stagefright/DataSource.h>
-#include <media/stagefright/foundation/ADebug.h>
-
-namespace android {
-
-} // namespace android
-
-static void usage(const char *me) {
- fprintf(stderr,
- "usage:\n"
- " %s -c host[:port]\tconnect to wifi source\n"
- " -u uri \tconnect to an rtsp uri\n",
- me);
-}
-
-int main(int argc, char **argv) {
- using namespace android;
-
- ProcessState::self()->startThreadPool();
-
- DataSource::RegisterDefaultSniffers();
-
- AString connectToHost;
- int32_t connectToPort = -1;
- AString uri;
-
- int res;
- while ((res = getopt(argc, argv, "hc:l:u:")) >= 0) {
- switch (res) {
- case 'c':
- {
- const char *colonPos = strrchr(optarg, ':');
-
- if (colonPos == NULL) {
- connectToHost = optarg;
- connectToPort = WifiDisplaySource::kWifiDisplayDefaultPort;
- } else {
- connectToHost.setTo(optarg, colonPos - optarg);
-
- char *end;
- connectToPort = strtol(colonPos + 1, &end, 10);
-
- if (*end != '\0' || end == colonPos + 1
- || connectToPort < 1 || connectToPort > 65535) {
- fprintf(stderr, "Illegal port specified.\n");
- exit(1);
- }
- }
- break;
- }
-
- case 'u':
- {
- uri = optarg;
- break;
- }
-
- case '?':
- case 'h':
- default:
- usage(argv[0]);
- exit(1);
- }
- }
-
- if (connectToPort < 0 && uri.empty()) {
- fprintf(stderr,
- "You need to select either source host or uri.\n");
-
- exit(1);
- }
-
- if (connectToPort >= 0 && !uri.empty()) {
- fprintf(stderr,
- "You need to either connect to a wfd host or an rtsp url, "
- "not both.\n");
- exit(1);
- }
-
- sp<ANetworkSession> session = new ANetworkSession;
- session->start();
-
- sp<ALooper> looper = new ALooper;
-
- sp<WifiDisplaySink> sink = new WifiDisplaySink(session);
- looper->registerHandler(sink);
-
- if (connectToPort >= 0) {
- sink->start(connectToHost.c_str(), connectToPort);
- } else {
- sink->start(uri.c_str());
- }
-
- looper->start(true /* runOnCallingThread */);
-
- return 0;
-}