diff options
Diffstat (limited to 'media/libstagefright/wifi-display')
34 files changed, 3272 insertions, 4620 deletions
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 62a6e7f..938d601 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -23,9 +23,11 @@ #include <arpa/inet.h> #include <fcntl.h> +#include <linux/tcp.h> #include <net/if.h> #include <netdb.h> #include <netinet/in.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <media/stagefright/foundation/ABuffer.h> @@ -37,6 +39,7 @@ namespace android { static const size_t kMaxUDPSize = 1500; +static const int32_t kMaxUDPRetries = 200; struct ANetworkSession::NetworkThread : public Thread { NetworkThread(ANetworkSession *session); @@ -79,7 +82,8 @@ struct ANetworkSession::Session : public RefBase { status_t readMore(); status_t writeMore(); - status_t sendRequest(const void *data, ssize_t size); + status_t sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs); void setIsRTSPConnection(bool yesno); @@ -87,24 +91,34 @@ protected: virtual ~Session(); private: + enum { + FRAGMENT_FLAG_TIME_VALID = 1, + }; + struct Fragment { + uint32_t mFlags; + int64_t mTimeUs; + sp<ABuffer> mBuffer; + }; + int32_t mSessionID; State mState; bool mIsRTSPConnection; int mSocket; sp<AMessage> mNotify; bool mSawReceiveFailure, mSawSendFailure; + int32_t mUDPRetries; - // for TCP / stream data - AString mOutBuffer; - - // for UDP / datagrams - List<sp<ABuffer> > mOutDatagrams; + List<Fragment> mOutFragments; AString mInBuffer; + int64_t mLastStallReportUs; + void notifyError(bool send, status_t err, const char *detail); void notify(NotificationReason reason); + void dumpFragmentStats(const Fragment &frag); + DISALLOW_EVIL_CONSTRUCTORS(Session); }; //////////////////////////////////////////////////////////////////////////////// @@ -135,7 +149,9 @@ ANetworkSession::Session::Session( mSocket(s), mNotify(notify), mSawReceiveFailure(false), - mSawSendFailure(false) { + mSawSendFailure(false), + mUDPRetries(kMaxUDPRetries), + mLastStallReportUs(-1ll) { if (mState == CONNECTED) { struct sockaddr_in localAddr; socklen_t localAddrLen = sizeof(localAddr); @@ -216,8 +232,8 @@ bool ANetworkSession::Session::wantsToRead() { bool ANetworkSession::Session::wantsToWrite() { return !mSawSendFailure && (mState == CONNECTING - || (mState == CONNECTED && !mOutBuffer.empty()) - || (mState == DATAGRAM && !mOutDatagrams.empty())); + || (mState == CONNECTED && !mOutFragments.empty()) + || (mState == DATAGRAM && !mOutFragments.empty())); } status_t ANetworkSession::Session::readMore() { @@ -273,8 +289,17 @@ status_t ANetworkSession::Session::readMore() { } if (err != OK) { - notifyError(false /* send */, err, "Recvfrom failed."); - mSawReceiveFailure = true; + if (!mUDPRetries) { + notifyError(false /* send */, err, "Recvfrom failed."); + mSawReceiveFailure = true; + } else { + mUDPRetries--; + ALOGE("Recvfrom failed, %d/%d retries left", + mUDPRetries, kMaxUDPRetries); + err = OK; + } + } else { + mUDPRetries = kMaxUDPRetries; } return err; @@ -314,6 +339,9 @@ status_t ANetworkSession::Session::readMore() { sp<ABuffer> packet = new ABuffer(packetSize); memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize); + int64_t nowUs = ALooper::GetNowUs(); + packet->meta()->setInt64("arrivalTimeUs", nowUs); + sp<AMessage> notify = mNotify->dup(); notify->setInt32("sessionID", mSessionID); notify->setInt32("reason", kWhatDatagram); @@ -399,31 +427,41 @@ status_t ANetworkSession::Session::readMore() { return err; } -status_t ANetworkSession::Session::writeMore() { - if (mState == DATAGRAM) { - CHECK(!mOutDatagrams.empty()); +void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) { +#if 0 + int64_t nowUs = ALooper::GetNowUs(); + int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll; - status_t err; - do { - const sp<ABuffer> &datagram = *mOutDatagrams.begin(); + static const int64_t kMinDelayMs = 0; + static const int64_t kMaxDelayMs = 300; - uint8_t *data = datagram->data(); - if (data[0] == 0x80 && (data[1] & 0x7f) == 33) { - int64_t nowUs = ALooper::GetNowUs(); + const char *kPattern = "########################################"; + size_t kPatternSize = strlen(kPattern); - uint32_t prevRtpTime = U32_AT(&data[4]); + int n = (kPatternSize * (delayMs - kMinDelayMs)) + / (kMaxDelayMs - kMinDelayMs); - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime; + if (n < 0) { + n = 0; + } else if ((size_t)n > kPatternSize) { + n = kPatternSize; + } - ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0); + ALOGI("[%lld]: (%4lld ms) %s\n", + frag.mTimeUs / 1000, + delayMs, + kPattern + kPatternSize - n); +#endif +} - data[4] = rtpTime >> 24; - data[5] = (rtpTime >> 16) & 0xff; - data[6] = (rtpTime >> 8) & 0xff; - data[7] = rtpTime & 0xff; - } +status_t ANetworkSession::Session::writeMore() { + if (mState == DATAGRAM) { + CHECK(!mOutFragments.empty()); + + status_t err; + do { + const Fragment &frag = *mOutFragments.begin(); + const sp<ABuffer> &datagram = frag.mBuffer; int n; do { @@ -433,24 +471,37 @@ status_t ANetworkSession::Session::writeMore() { err = OK; if (n > 0) { - mOutDatagrams.erase(mOutDatagrams.begin()); + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); } else if (n < 0) { err = -errno; } else if (n == 0) { err = -ECONNRESET; } - } while (err == OK && !mOutDatagrams.empty()); + } while (err == OK && !mOutFragments.empty()); if (err == -EAGAIN) { - if (!mOutDatagrams.empty()) { - ALOGI("%d datagrams remain queued.", mOutDatagrams.size()); + if (!mOutFragments.empty()) { + ALOGI("%d datagrams remain queued.", mOutFragments.size()); } err = OK; } if (err != OK) { - notifyError(true /* send */, err, "Send datagram failed."); - mSawSendFailure = true; + if (!mUDPRetries) { + notifyError(true /* send */, err, "Send datagram failed."); + mSawSendFailure = true; + } else { + mUDPRetries--; + ALOGE("Send datagram failed, %d/%d retries left", + mUDPRetries, kMaxUDPRetries); + err = OK; + } + } else { + mUDPRetries = kMaxUDPRetries; } return err; @@ -476,23 +527,37 @@ status_t ANetworkSession::Session::writeMore() { } CHECK_EQ(mState, CONNECTED); - CHECK(!mOutBuffer.empty()); + CHECK(!mOutFragments.empty()); ssize_t n; - do { - n = send(mSocket, mOutBuffer.c_str(), mOutBuffer.size(), 0); - } while (n < 0 && errno == EINTR); + while (!mOutFragments.empty()) { + const Fragment &frag = *mOutFragments.begin(); - status_t err = OK; + do { + n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0); + } while (n < 0 && errno == EINTR); - if (n > 0) { -#if 0 - ALOGI("out:"); - hexdump(mOutBuffer.c_str(), n); -#endif + if (n <= 0) { + break; + } - mOutBuffer.erase(0, n); - } else if (n < 0) { + frag.mBuffer->setRange( + frag.mBuffer->offset() + n, frag.mBuffer->size() - n); + + if (frag.mBuffer->size() > 0) { + break; + } + + if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) { + dumpFragmentStats(frag); + } + + mOutFragments.erase(mOutFragments.begin()); + } + + status_t err = OK; + + if (n < 0) { err = -errno; } else if (n == 0) { err = -ECONNRESET; @@ -503,35 +568,69 @@ status_t ANetworkSession::Session::writeMore() { mSawSendFailure = true; } +#if 0 + int numBytesQueued; + int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued); + if (res == 0 && numBytesQueued > 50 * 1024) { + if (numBytesQueued > 409600) { + ALOGW("!!! numBytesQueued = %d", numBytesQueued); + } + + int64_t nowUs = ALooper::GetNowUs(); + + if (mLastStallReportUs < 0ll + || nowUs > mLastStallReportUs + 100000ll) { + sp<AMessage> msg = mNotify->dup(); + msg->setInt32("sessionID", mSessionID); + msg->setInt32("reason", kWhatNetworkStall); + msg->setSize("numBytesQueued", numBytesQueued); + msg->post(); + + mLastStallReportUs = nowUs; + } + } +#endif + return err; } -status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) { +status_t ANetworkSession::Session::sendRequest( + const void *data, ssize_t size, bool timeValid, int64_t timeUs) { CHECK(mState == CONNECTED || mState == DATAGRAM); - if (mState == DATAGRAM) { - CHECK_GE(size, 0); - - sp<ABuffer> datagram = new ABuffer(size); - memcpy(datagram->data(), data, size); + if (size < 0) { + size = strlen((const char *)data); + } - mOutDatagrams.push_back(datagram); + if (size == 0) { return OK; } + sp<ABuffer> buffer; + if (mState == CONNECTED && !mIsRTSPConnection) { CHECK_LE(size, 65535); - uint8_t prefix[2]; - prefix[0] = size >> 8; - prefix[1] = size & 0xff; + buffer = new ABuffer(size + 2); + buffer->data()[0] = size >> 8; + buffer->data()[1] = size & 0xff; + memcpy(buffer->data() + 2, data, size); + } else { + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + } + + Fragment frag; - mOutBuffer.append((const char *)prefix, sizeof(prefix)); + frag.mFlags = 0; + if (timeValid) { + frag.mFlags = FRAGMENT_FLAG_TIME_VALID; + frag.mTimeUs = timeUs; } - mOutBuffer.append( - (const char *)data, - (size >= 0) ? size : strlen((const char *)data)); + frag.mBuffer = buffer; + + mOutFragments.push_back(frag); return OK; } @@ -770,6 +869,22 @@ status_t ANetworkSession::createClientOrServer( err = -errno; goto bail2; } + } else if (mode == kModeCreateTCPDatagramSessionActive) { + int flag = 1; + res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + + if (res < 0) { + err = -errno; + goto bail2; + } + + int tos = 224; // VOICE + res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + + if (res < 0) { + err = -errno; + goto bail2; + } } err = MakeSocketNonBlocking(s); @@ -946,7 +1061,8 @@ status_t ANetworkSession::connectUDPSession( } status_t ANetworkSession::sendRequest( - int32_t sessionID, const void *data, ssize_t size) { + int32_t sessionID, const void *data, ssize_t size, + bool timeValid, int64_t timeUs) { Mutex::Autolock autoLock(mLock); ssize_t index = mSessions.indexOfKey(sessionID); @@ -957,7 +1073,7 @@ status_t ANetworkSession::sendRequest( const sp<Session> session = mSessions.valueAt(index); - status_t err = session->sendRequest(data, size); + status_t err = session->sendRequest(data, size, timeValid, timeUs); interrupt(); @@ -1091,7 +1207,6 @@ void ANetworkSession::threadLoop() { clientSocket); sp<Session> clientSession = - // using socket sd as sessionID new Session( mNextSessionID++, Session::CONNECTED, diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h index c1acdcc..7c62b29 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.h +++ b/media/libstagefright/wifi-display/ANetworkSession.h @@ -74,7 +74,8 @@ struct ANetworkSession : public RefBase { status_t destroySession(int32_t sessionID); status_t sendRequest( - int32_t sessionID, const void *data, ssize_t size = -1); + int32_t sessionID, const void *data, ssize_t size = -1, + bool timeValid = false, int64_t timeUs = -1ll); enum NotificationReason { kWhatError, @@ -83,6 +84,7 @@ struct ANetworkSession : public RefBase { kWhatData, kWhatDatagram, kWhatBinaryData, + kWhatNetworkStall, }; protected: diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 75098f1..061ae89 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -4,20 +4,17 @@ 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 \ - TimeSeries.cpp \ + VideoFormats.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ @@ -27,6 +24,7 @@ LOCAL_C_INCLUDES:= \ LOCAL_SHARED_LIBRARIES:= \ libbinder \ libcutils \ + liblog \ libgui \ libmedia \ libstagefright \ @@ -55,31 +53,10 @@ LOCAL_SHARED_LIBRARIES:= \ libstagefright_foundation \ libstagefright_wfd \ libutils \ + liblog \ 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..8a3566f --- /dev/null +++ b/media/libstagefright/wifi-display/MediaSender.cpp @@ -0,0 +1,474 @@ +/* + * 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 "ANetworkSession.h" +#include "rtp/RTPSender.h" +#include "source/TSPacketizer.h" + +#include "include/avc_utils.h" + +#include <media/IHDCP.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +namespace android { + +MediaSender::MediaSender( + const sp<ANetworkSession> &netSession, + const sp<AMessage> ¬ify) + : 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; + } + + 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 = 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..64722c5 --- /dev/null +++ b/media/libstagefright/wifi-display/MediaSender.h @@ -0,0 +1,131 @@ +/* + * 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, + }; + + MediaSender( + const sp<ANetworkSession> &netSession, + const sp<AMessage> ¬ify); + + 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/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp deleted file mode 100644 index d882d98..0000000 --- a/media/libstagefright/wifi-display/TimeSeries.cpp +++ /dev/null @@ -1,67 +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 "TimeSeries.h" - -#include <math.h> -#include <string.h> - -namespace android { - -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); -} - -} // namespace android diff --git a/media/libstagefright/wifi-display/VideoFormats.cpp b/media/libstagefright/wifi-display/VideoFormats.cpp new file mode 100644 index 0000000..458b163 --- /dev/null +++ b/media/libstagefright/wifi-display/VideoFormats.cpp @@ -0,0 +1,419 @@ +/* + * 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 { + +VideoFormats::config_t VideoFormats::mConfigs[][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() { + 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::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); + } else { + mResolutionEnabled[type] &= ~(1ul << index); + } +} + +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 = &mConfigs[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) { + mConfigs[i][j].profile = profile; + if (level > mConfigs[i][j].level) + mConfigs[i][j].level = level; + } + } + } + } + + return true; +} + +bool VideoFormats::parseFormatSpec(const char *spec) { + CHECK_EQ(kNumResolutionTypes, 3); + + 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 02 02 %08x %08x %08x 00 0000 0000 00 none none", + forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType), + mResolutionEnabled[0], + mResolutionEnabled[1], + mResolutionEnabled[2]); +} + +// static +bool VideoFormats::PickBestFormat( + const VideoFormats &sinkSupported, + const VideoFormats &sourceSupported, + ResolutionType *chosenType, + size_t *chosenIndex) { +#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; + + 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..01de246 --- /dev/null +++ b/media/libstagefright/wifi-display/VideoFormats.h @@ -0,0 +1,106 @@ +/* + * 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 setResolutionEnabled( + ResolutionType type, size_t index, bool enabled = true); + + bool isResolutionEnabled(ResolutionType type, size_t index) const; + + static bool GetConfiguration( + ResolutionType type, size_t index, + size_t *width, size_t *height, size_t *framesPerSecond, + bool *interlaced); + + 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); + +private: + bool parseH264Codec(const char *spec); + ResolutionType mNativeType; + size_t mNativeIndex; + + uint32_t mResolutionEnabled[kNumResolutionTypes]; + static config_t mConfigs[kNumResolutionTypes][32]; + + DISALLOW_EVIL_CONSTRUCTORS(VideoFormats); +}; + +} // namespace android + +#endif // VIDEO_FORMATS_H_ + diff --git a/media/libstagefright/wifi-display/TimeSeries.h b/media/libstagefright/wifi-display/rtp/RTPBase.h index c818d51..6178f00 100644 --- a/media/libstagefright/wifi-display/TimeSeries.h +++ b/media/libstagefright/wifi-display/rtp/RTPBase.h @@ -1,5 +1,5 @@ /* - * Copyright 2012, The Android Open Source Project + * 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. @@ -14,33 +14,38 @@ * limitations under the License. */ -#ifndef TIME_SERIES_H_ +#ifndef RTP_BASE_H_ -#define TIME_SERIES_H_ - -#include <sys/types.h> +#define RTP_BASE_H_ namespace android { -struct TimeSeries { - TimeSeries(); - - void add(double val); +struct RTPBase { + enum PacketizationMode { + PACKETIZATION_TRANSPORT_STREAM, + PACKETIZATION_H264, + PACKETIZATION_AAC, + PACKETIZATION_NONE, + }; - double mean() const; - double sdev() const; + enum TransportMode { + TRANSPORT_UNDEFINED, + TRANSPORT_NONE, + TRANSPORT_UDP, + TRANSPORT_TCP, + TRANSPORT_TCP_INTERLEAVED, + }; -private: enum { - kHistorySize = 20 + // Really UDP _payload_ size + kMaxUDPPacketSize = 1472, // 1472 good, 1473 bad on Android@Home }; - double mValues[kHistorySize]; - size_t mCount; - double mSum; + static int32_t PickRandomRTPPort(); }; } // namespace android -#endif // TIME_SERIES_H_ +#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..095fd97 --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp @@ -0,0 +1,795 @@ +/* + * 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 "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 "include/avc_utils.h" + +namespace android { + +RTPSender::RTPSender( + const sp<ANetworkSession> &netSession, + const sp<AMessage> ¬ify) + : 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) { + 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..7dc138a --- /dev/null +++ b/media/libstagefright/wifi-display/rtp/RTPSender.h @@ -0,0 +1,120 @@ +/* + * 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, + }; + RTPSender( + const sp<ANetworkSession> &netSession, + const sp<AMessage> ¬ify); + + 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> ¬ifyLost, - 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> ¬ifyLost, - 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 7a87444..5344623 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -23,7 +23,7 @@ #include "MediaPuller.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> @@ -54,6 +54,8 @@ Converter::Converter( ,mFirstSilentFrameUs(-1ll) ,mInSilentMode(false) #endif + ,mPrevVideoBitrate(-1) + ,mNumFramesToDrop(0) { AString mime; CHECK(mInputFormat->findString("mime", &mime)); @@ -67,11 +69,45 @@ Converter::Converter( mInitCheck = initEncoder(); if (mInitCheck != OK) { - if (mEncoder != NULL) { - mEncoder->release(); - mEncoder.clear(); - } + releaseEncoder(); + } +} + +static void ReleaseMediaBufferReference(const sp<ABuffer> &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; + } +} + +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() { @@ -99,7 +135,9 @@ bool Converter::needToManuallyPrependSPSPPS() const { return mNeedToManuallyPrependSPSPPS; } -static int32_t getBitrate(const char *propName, int32_t defaultValue) { +// static +int32_t Converter::GetInt32Property( + const char *propName, int32_t defaultValue) { char val[PROPERTY_VALUE_MAX]; if (property_get(propName, val, NULL)) { char *end; @@ -149,8 +187,9 @@ status_t Converter::initEncoder() { 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); @@ -274,16 +313,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; } @@ -300,6 +330,13 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { sp<ABuffer> accessUnit; CHECK(msg->findBuffer("accessUnit", &accessUnit)); + if (mIsVideo && mNumFramesToDrop) { + --mNumFramesToDrop; + ALOGI("dropping frame."); + ReleaseMediaBufferReference(accessUnit); + break; + } + #if 0 void *mbuf; if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf) @@ -385,12 +422,9 @@ 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)); @@ -398,6 +432,12 @@ void Converter::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatDropAFrame: + { + ++mNumFramesToDrop; + break; + } + default: TRESPASS(); } @@ -609,6 +649,13 @@ status_t Converter::doMoreWork() { &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; } @@ -654,4 +701,23 @@ void Converter::requestIDRFrame() { (new AMessage(kWhatRequestIDRFrame, id()))->post(); } +void Converter::dropAFrame() { + (new AMessage(kWhatDropAFrame, id()))->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("videoBitrate", 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 0665eea..ba297c4 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -51,6 +51,8 @@ struct Converter : public AHandler { void requestIDRFrame(); + void dropAFrame(); + enum { kWhatAccessUnit, kWhatEOS, @@ -63,10 +65,16 @@ struct Converter : public AHandler { kWhatShutdown, kWhatMediaPullerNotify, kWhatEncoderActivity, + kWhatDropAFrame, }; void shutdownAsync(); + int32_t getVideoBitrate() const; + void setVideoBitrate(int32_t bitrate); + + static int32_t GetInt32Property(const char *propName, int32_t defaultValue); + protected: virtual ~Converter(); virtual void onMessageReceived(const sp<AMessage> &msg); @@ -100,7 +108,12 @@ private: sp<ABuffer> mPartialAudioAU; + int32_t mPrevVideoBitrate; + + int32_t mNumFramesToDrop; + status_t initEncoder(); + void releaseEncoder(); status_t feedEncoderInputBuffers(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 916f797..3d7b865 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -23,14 +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> @@ -41,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> @@ -63,15 +59,18 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { const sp<MediaPuller> &mediaPuller, const sp<Converter> &converter); + Track(const sp<AMessage> ¬ify, 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(); @@ -110,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; @@ -135,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> ¬ify, const sp<AMessage> &format) + : mNotify(notify), + mFormat(format), + mStarted(false), + mIsAudio(IsAudioFormat(format)), + mLastOutputBufferQueuedTimeUs(-1ll) { +} + WifiDisplaySource::PlaybackSession::Track::~Track() { CHECK(!mStarted); } @@ -154,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 { @@ -165,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() { @@ -195,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()); @@ -207,6 +223,7 @@ void WifiDisplaySource::PlaybackSession::Track::stopAsync() { mMediaPuller->stopAsync(msg); } else { + mStarted = false; msg->post(); } } @@ -330,45 +347,68 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify, 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) { + 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); - 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; } @@ -381,7 +421,7 @@ WifiDisplaySource::PlaybackSession::~PlaybackSession() { } int32_t WifiDisplaySource::PlaybackSession::getRTPPort() const { - return mSender->getRTPPort(); + return mLocalRTPPort; } int64_t WifiDisplaySource::PlaybackSession::getLastLifesignUs() const { @@ -400,19 +440,7 @@ status_t WifiDisplaySource::PlaybackSession::play() { return OK; } -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()); } @@ -459,44 +487,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) { - sp<AMessage> trackFormat = track->getFormat()->dup(); - if (mHDCP != NULL && !track->isAudio()) { - // HDCP2.0 _and_ HDCP 2.1 specs say to set the version - // inside the HDCP descriptor to 0x20!!! - trackFormat->setInt32("hdcp-version", 0x20); - } - packetizerTrackIndex = mPacketizer->addTrack(trackFormat); - - 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); @@ -528,25 +530,38 @@ 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 { TRESPASS(); } - - break; - } - - case kWhatFinishPlay: - { - onFinishPlay(); break; } @@ -571,11 +586,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); @@ -584,30 +596,14 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } - case kWhatPacketize: + case kWhatPause: { - size_t trackIndex; - CHECK(msg->findSize("trackIndex", &trackIndex)); - - sp<ABuffer> accessUnit; - CHECK(msg->findBuffer("accessUnit", &accessUnit)); - -#if 0 - if ((ssize_t)trackIndex == mVideoTrackIndex) { - int64_t nowUs = ALooper::GetNowUs(); - static int64_t prevNowUs = 0ll; - - ALOGI("sending AU, dNowUs=%lld us", nowUs - prevNowUs); - - prevNowUs = nowUs; + if (mExtractor != NULL) { + ++mPullExtractorGeneration; + mFirstSampleTimeRealUs = -1ll; + mFirstSampleTimeUs = -1ll; } -#endif - break; - } - - case kWhatPause: - { if (mPaused) { break; } @@ -622,6 +618,10 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( case kWhatResume: { + if (mExtractor != NULL) { + schedulePullExtractor(); + } + if (!mPaused) { break; } @@ -634,20 +634,177 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } + case kWhatPullExtractorSample: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mPullExtractorGeneration) { + break; + } + + mPullExtractorPending = false; + + onPullExtractor(); + break; + } + default: TRESPASS(); } } -status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) { - mPacketizer = new TSPacketizer; +status_t WifiDisplaySource::PlaybackSession::setupMediaPacketizer( + bool enableAudio, bool enableVideo) { + DataSource::RegisterDefaultSniffers(); - status_t err = addVideoSource(); + 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) { + CHECK(enableAudio || enableVideo); + + if (!mMediaPath.empty()) { + return setupMediaPacketizer(enableAudio, enableVideo); + } + + if (enableVideo) { + status_t err = addVideoSource( + videoResolutionType, videoResolutionIndex); + + if (err != OK) { + return err; + } + } + + if (!enableAudio) { + return OK; + } + return addAudioSource(usePCMAudio); } @@ -732,30 +889,44 @@ 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) { + size_t width, height, framesPerSecond; + bool interlaced; + CHECK(VideoFormats::GetConfiguration( + videoResolutionType, + videoResolutionIndex, + &width, + &height, + &framesPerSecond, + &interlaced)); + + 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 if (err != OK) { return err; @@ -786,26 +957,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 { -#if USE_1080P - return 1920; -#else - return 1280; -#endif -} - -int32_t WifiDisplaySource::PlaybackSession::height() const { -#if USE_1080P - return 1080; -#else - return 720; -#endif -} - void WifiDisplaySource::PlaybackSession::requestIDRFrame() { for (size_t i = 0; i < mTracks.size(); ++i) { const sp<Track> &track = mTracks.valueAt(i); @@ -814,168 +969,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, 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]; - - bool manuallyPrependSPSPPS = - !track->isAudio() - && track->converter()->needToManuallyPrependSPSPPS() - && IsIDR(accessUnit); - - if (mHDCP != NULL && !track->isAudio()) { - isHDCPEncrypted = true; - - if (manuallyPrependSPSPPS) { - accessUnit = mPacketizer->prependCSD( - track->packetizerTrackIndex(), accessUnit); - } - - 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; - } 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; - } - - 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(); @@ -985,78 +978,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; - } - } -#if SUSPEND_VIDEO_IF_IDLE - 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; - } -#else - else { - // We need access units available on all tracks to be able to - // dequeue the earliest one. - return false; - } -#endif - } - - 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(); - return false; - } - - if ((ssize_t)minTrackIndex == mVideoTrackIndex) { - packets->meta()->setInt32("isVideo", 1); - } - mSender->queuePackets(minTimeUs, packets); - -#if 0 - if (minTrackIndex == mVideoTrackIndex) { - int64_t nowUs = ALooper::GetNowUs(); - - // Latency from "data acquired" to "ready to send if we wanted to". - ALOGI("[%s] latencyUs = %lld ms", - minTrackIndex == mVideoTrackIndex ? "video" : "audio", - (nowUs - minTimeUs) / 1000ll); - } -#endif - - return true; -} - } // namespace android diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index b9d193b..39086a1 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,20 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify, 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); void destroyAsync(); @@ -56,9 +66,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,26 +88,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; @@ -107,9 +116,21 @@ 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); status_t addSource( bool isVideo, @@ -118,29 +139,20 @@ private: bool usePCMAudio, 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); - bool allTracksHavePacketizerIndex(); - - status_t packetizeAccessUnit( - size_t trackIndex, 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 72be927..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); diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h index a13973c..8d414fd 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.h +++ b/media/libstagefright/wifi-display/source/RepeaterSource.h @@ -6,7 +6,7 @@ #include <media/stagefright/foundation/AHandlerReflector.h> #include <media/stagefright/MediaSource.h> -#define SUSPEND_VIDEO_IF_IDLE 1 +#define SUSPEND_VIDEO_IF_IDLE 0 namespace android { @@ -28,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 9048691..0000000 --- a/media/libstagefright/wifi-display/source/Sender.cpp +++ /dev/null @@ -1,870 +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 "TimeSeries.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 { - -static size_t kMaxRTPPacketSize = 1500; -static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188; - -Sender::Sender( - const sp<ANetworkSession> &netSession, - const sp<AMessage> ¬ify) - : mNetSession(netSession), - mNotify(notify), - mTransportMode(TRANSPORT_UDP), - mRTPChannel(0), - mRTCPChannel(0), - mRTPPort(0), - mRTPSessionID(0), - mRTCPSessionID(0), -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - mRTPRetransmissionSessionID(0), - mRTCPRetransmissionSessionID(0), -#endif - mClientRTPPort(0), - mClientRTCPPort(0), - mRTPConnected(false), - mRTCPConnected(false), - mFirstOutputBufferReadyTimeUs(-1ll), - mFirstOutputBufferSentTimeUs(-1ll), - mRTPSeqNo(0), -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 -{ -#if LOG_TRANSPORT_STREAM - mLogFile = fopen("/system/etc/log.ts", "wb"); -#endif -} - -Sender::~Sender() { -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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> &tsPackets) { - const size_t numTSPackets = tsPackets->size() / 188; - - const size_t numRTPPackets = - (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1) - / kMaxNumTSPacketsPerRTPPacket; - - sp<ABuffer> udpPackets = new ABuffer( - numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188)); - - udpPackets->meta()->setInt64("timeUs", timeUs); - - size_t dstOffset = 0; - for (size_t i = 0; i < numTSPackets; ++i) { - if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) { - static const bool kMarkerBit = false; - - uint8_t *rtp = udpPackets->data() + dstOffset; - rtp[0] = 0x80; - rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0); // M-bit - rtp[2] = (mRTPSeqNo >> 8) & 0xff; - rtp[3] = mRTPSeqNo & 0xff; - rtp[4] = 0x00; // rtp time to be filled in later. - rtp[5] = 0x00; - rtp[6] = 0x00; - rtp[7] = 0x00; - rtp[8] = kSourceID >> 24; - rtp[9] = (kSourceID >> 16) & 0xff; - rtp[10] = (kSourceID >> 8) & 0xff; - rtp[11] = kSourceID & 0xff; - - ++mRTPSeqNo; - - dstOffset += 12; - } - - memcpy(udpPackets->data() + dstOffset, - tsPackets->data() + 188 * i, - 188); - - dstOffset += 188; - } - - udpPackets->setRange(0, dstOffset); - - sp<AMessage> msg = new AMessage(kWhatDrainQueue, id()); - msg->setBuffer("udpPackets", udpPackets); - msg->post(); - -#if LOG_TRANSPORT_STREAM - if (mLogFile != NULL) { - fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile); - } -#endif -} - -void Sender::onMessageReceived(const sp<AMessage> &msg) { - switch (msg->what()) { - case kWhatRTPNotify: - case kWhatRTCPNotify: -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - || 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - || 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 kWhatDrainQueue: - { - sp<ABuffer> udpPackets; - CHECK(msg->findBuffer("udpPackets", &udpPackets)); - - onDrainQueue(udpPackets); - break; - } - - case kWhatSendSR: - { - mSendSRPending = false; - - if (mRTCPSessionID == 0) { - break; - } - - onSendSR(); - - scheduleSendSR(); - break; - } - } -} - -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); - -#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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()); -#else - sendPacket( - mRTPSessionID, buffer->data(), buffer->size()); -#endif - - 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(); -} - -void Sender::onDrainQueue(const sp<ABuffer> &udpPackets) { - static const size_t kFullRTPPacketSize = - 12 + 188 * kMaxNumTSPacketsPerRTPPacket; - - size_t srcOffset = 0; - while (srcOffset < udpPackets->size()) { - uint8_t *rtp = udpPackets->data() + srcOffset; - - size_t rtpPacketSize = udpPackets->size() - srcOffset; - if (rtpPacketSize > kFullRTPPacketSize) { - rtpPacketSize = kFullRTPPacketSize; - } - - int64_t nowUs = ALooper::GetNowUs(); - mLastNTPTime = GetNowNTP(); - - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - - rtp[4] = rtpTime >> 24; - rtp[5] = (rtpTime >> 16) & 0xff; - rtp[6] = (rtpTime >> 8) & 0xff; - rtp[7] = rtpTime & 0xff; - - ++mNumRTPSent; - mNumRTPOctetsSent += rtpPacketSize - 12; - - mLastRTPTime = rtpTime; - - if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - - sp<ABuffer> data = new ABuffer(rtpPacketSize); - memcpy(data->data(), rtp, rtpPacketSize); - - notify->setInt32("channel", mRTPChannel); - notify->setBuffer("data", data); - notify->post(); - } else { - sendPacket(mRTPSessionID, rtp, rtpPacketSize); - -#if TRACK_BANDWIDTH - mTotalBytesSent += rtpPacketSize->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 - addToHistory(rtp, rtpPacketSize); -#endif - - srcOffset += rtpPacketSize; - } - -#if 0 - int64_t timeUs; - CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs)); - - ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs); -#endif -} - -#if ENABLE_RETRANSMISSION -void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) { - sp<ABuffer> packet = new ABuffer(rtpPacketSize); - memcpy(packet->data(), rtp, rtpPacketSize); - - unsigned rtpSeqNo = U16_AT(&rtp[2]); - packet->setInt32Data(rtpSeqNo); - - mHistory.push_back(packet); - ++mHistoryLength; - - if (mHistoryLength > kMaxHistoryLength) { - mHistory.erase(mHistory.begin()); - --mHistoryLength; - } -} -#endif - -} // 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 66951f7..0000000 --- a/media/libstagefright/wifi-display/source/Sender.h +++ /dev/null @@ -1,169 +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 TRACK_BANDWIDTH 0 - -#define ENABLE_RETRANSMISSION 1 - -// If retransmission is enabled the following define determines what -// kind we support, if RETRANSMISSION_ACCORDING_TO_RFC_XXXX is 0 -// we'll send NACKs on the original RTCP channel and retransmit packets -// on the original RTP channel, otherwise a separate channel pair is used -// for this purpose. -#define RETRANSMISSION_ACCORDING_TO_RFC_XXXX 0 - -struct ABuffer; -struct ANetworkSession; - -struct Sender : public AHandler { - Sender(const sp<ANetworkSession> &netSession, const sp<AMessage> ¬ify); - - 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> &tsPackets); - void scheduleSendSR(); - -protected: - virtual ~Sender(); - virtual void onMessageReceived(const sp<AMessage> &msg); - -private: - enum { - kWhatDrainQueue, - kWhatSendSR, - kWhatRTPNotify, - kWhatRTCPNotify, -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - kWhatRTPRetransmissionNotify, - kWhatRTCPRetransmissionNotify, -#endif - }; - - static const int64_t kSendSRIntervalUs = 10000000ll; - - static const uint32_t kSourceID = 0xdeadbeef; - static const size_t kMaxHistoryLength = 128; - -#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - static const size_t kRetransmissionPortOffset = 120; -#endif - - sp<ANetworkSession> mNetSession; - sp<AMessage> mNotify; - - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 && RETRANSMISSION_ACCORDING_TO_RFC_XXXX - 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 - -#if LOG_TRANSPORT_STREAM - FILE *mLogFile; -#endif - - void onSendSR(); - void addSR(const sp<ABuffer> &buffer); - void addSDES(const sp<ABuffer> &buffer); - static uint64_t GetNowNTP(); - -#if ENABLE_RETRANSMISSION - status_t parseTSFB(const uint8_t *data, size_t size); - void addToHistory(const uint8_t *rtp, size_t rtpPacketSize); -#endif - - status_t parseRTCP(const sp<ABuffer> &buffer); - - status_t sendPacket(int32_t sessionID, const void *data, size_t size); - - void notifyInitDone(); - void notifySessionDead(); - - void onDrainQueue(const sp<ABuffer> &udpPackets); - - 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 ef57a4d..2c4a373 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,7 +261,7 @@ void TSPacketizer::Track::finalize() { data[0] = 40; // descriptor_tag data[1] = 4; // descriptor_length - CHECK_EQ(mCSD.size(), 1u); + 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); @@ -314,12 +325,31 @@ void TSPacketizer::Track::finalize() { mDescriptors.push_back(descriptor); } - int32_t hdcpVersion; - if (mFormat->findInt32("hdcp-version", &hdcpVersion)) { - // HDCP descriptor + mFinalized = true; +} + +//////////////////////////////////////////////////////////////////////////////// - CHECK(hdcpVersion == 0x20 || hdcpVersion == 0x21); +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 @@ -330,18 +360,8 @@ void TSPacketizer::Track::finalize() { data[5] = 'P'; data[6] = hdcpVersion; - mDescriptors.push_back(descriptor); + mProgramInfoDescriptors.push_back(descriptor); } - - mFinalized = true; -} - -//////////////////////////////////////////////////////////////////////////////// - -TSPacketizer::TSPacketizer() - : mPATContinuityCounter(0), - mPMTContinuityCounter(0) { - initCrcTable(); } TSPacketizer::~TSPacketizer() { @@ -407,6 +427,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, @@ -471,16 +502,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) { @@ -583,8 +719,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) @@ -616,8 +753,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); @@ -710,8 +860,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()); @@ -720,19 +868,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; } } @@ -768,25 +934,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 @@ -798,35 +953,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 08f67f9..22dd0b1 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -22,10 +22,10 @@ #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> @@ -33,6 +33,7 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.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,8 +63,16 @@ 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 } WifiDisplaySource::~WifiDisplaySource() { @@ -151,9 +164,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { } else { err = -EINVAL; } - } - if (err == OK) { mState = AWAITING_CLIENT_CONNECTION; } @@ -253,7 +264,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (!strcasecmp(val, "pause") && mState == PLAYING) { mState = PLAYING_TO_PAUSED; sendTrigger(mClientSessionID, TRIGGER_PAUSE); - } else if (!strcasecmp(val, "play") && mState == PAUSED) { + } else if (!strcasecmp(val, "play") + && mState == PAUSED) { mState = PAUSED_TO_PLAYING; sendTrigger(mClientSessionID, TRIGGER_PLAY); } @@ -262,6 +274,11 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { break; } + case ANetworkSession::kWhatNetworkStall: + { + break; + } + default: TRESPASS(); } @@ -374,16 +391,41 @@ 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); + } 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); + } } + finishPlay(); + if (mState == ABOUT_TO_PLAY) { mState = PLAYING; } @@ -564,55 +606,38 @@ 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" - // For 1080p30: - // use "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n" - AString body = StringPrintf( - "wfd_video_formats: " -#if USE_1080P - "38 00 02 02 00000080 00000000 00000000 00 0000 0000 00 none none\r\n" -#else - "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" -#endif - "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); + + 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); @@ -775,53 +800,115 @@ 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)) { + 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); + } 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; } @@ -1065,7 +1152,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;")) { @@ -1074,7 +1161,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; @@ -1096,7 +1183,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;")) { @@ -1138,7 +1225,7 @@ status_t WifiDisplaySource::onSetupRequest( sp<PlaybackSession> playbackSession = new PlaybackSession( - mNetSession, notify, mInterfaceAddr, mHDCP); + mNetSession, notify, mInterfaceAddr, mHDCP, mMediaPath.c_str()); looper()->registerHandler(playbackSession); @@ -1155,12 +1242,22 @@ 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); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); @@ -1184,7 +1281,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;", @@ -1193,7 +1290,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"; } @@ -1243,17 +1340,28 @@ status_t WifiDisplaySource::onPlayRequest( return ERROR_MALFORMED; } - ALOGI("Received PLAY request."); + if (mState != AWAITING_CLIENT_PLAY) { + 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; @@ -1264,14 +1372,20 @@ status_t WifiDisplaySource::onPlayRequest( return OK; } - playbackSession->finishPlay(); - CHECK_EQ(mState, AWAITING_CLIENT_PLAY); mState = ABOUT_TO_PLAY; 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, @@ -1447,7 +1561,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)); @@ -1557,10 +1671,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 974e070..44d3e4d 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -19,6 +19,7 @@ #define WIFI_DISPLAY_SOURCE_H_ #include "ANetworkSession.h" +#include "VideoFormats.h" #include <media/stagefright/foundation/AHandler.h> @@ -26,8 +27,6 @@ namespace android { -#define USE_1080P 0 - struct IHDCP; struct IRemoteDisplayClient; struct ParsedMessage; @@ -39,7 +38,8 @@ 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(); @@ -111,16 +111,29 @@ 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; + + bool mSinkSupportsAudio; + bool mUsingPCMAudio; int32_t mClientSessionID; @@ -149,6 +162,8 @@ private: bool mHDCPInitializationComplete; bool mSetupTriggerDeferred; + bool mPlaybackSessionEstablished; + status_t makeHDCP(); // <<<< HDCP specific section @@ -245,6 +260,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 index 03a1123..c947765 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -18,11 +18,11 @@ #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 <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> #include <media/AudioSystem.h> #include <media/IMediaPlayerService.h> @@ -30,16 +30,16 @@ #include <media/IRemoteDisplayClient.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <ui/DisplayInfo.h> namespace android { static void usage(const char *me) { fprintf(stderr, "usage:\n" - " %s -c host[:port]\tconnect to wifi source\n" - " -u uri \tconnect to an rtsp uri\n" - " -l ip[:port] \tlisten on the specified port " - "(create a sink)\n", + " %s -l iface[:port]\tcreate a wifi display source\n" + " -f(ilename) \tstream media\n", me); } @@ -47,7 +47,7 @@ struct RemoteDisplayClient : public BnRemoteDisplayClient { RemoteDisplayClient(); virtual void onDisplayConnected( - const sp<ISurfaceTexture> &surfaceTexture, + const sp<IGraphicBufferProducer> &bufferProducer, uint32_t width, uint32_t height, uint32_t flags); @@ -67,7 +67,7 @@ private: bool mDone; sp<SurfaceComposerClient> mComposerClient; - sp<ISurfaceTexture> mSurfaceTexture; + sp<IGraphicBufferProducer> mSurfaceTexture; sp<IBinder> mDisplayBinder; DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); @@ -83,29 +83,31 @@ RemoteDisplayClient::~RemoteDisplayClient() { } void RemoteDisplayClient::onDisplayConnected( - const sp<ISurfaceTexture> &surfaceTexture, + const sp<IGraphicBufferProducer> &bufferProducer, uint32_t width, uint32_t height, uint32_t flags) { ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", width, height, flags); - mSurfaceTexture = surfaceTexture; - mDisplayBinder = mComposerClient->createDisplay( - String8("foo"), false /* secure */); + if (bufferProducer != NULL) { + mSurfaceTexture = bufferProducer; + mDisplayBinder = mComposerClient->createDisplay( + String8("foo"), false /* secure */); - SurfaceComposerClient::openGlobalTransaction(); - mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); + SurfaceComposerClient::openGlobalTransaction(); + mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); - Rect layerStackRect(1280, 720); // XXX fix this. - Rect displayRect(1280, 720); + Rect layerStackRect(1280, 720); // XXX fix this. + Rect displayRect(1280, 720); - mComposerClient->setDisplayProjection( - mDisplayBinder, 0 /* 0 degree rotation */, - layerStackRect, - displayRect); + mComposerClient->setDisplayProjection( + mDisplayBinder, 0 /* 0 degree rotation */, + layerStackRect, + displayRect); - SurfaceComposerClient::closeGlobalTransaction(); + SurfaceComposerClient::closeGlobalTransaction(); + } } void RemoteDisplayClient::onDisplayDisconnected() { @@ -178,6 +180,26 @@ static void createSource(const AString &addr, int32_t port) { enableAudioSubmix(false /* enable */); } +static void createFileSource( + const AString &addr, int32_t port, const char *path) { + sp<ANetworkSession> session = new ANetworkSession; + session->start(); + + sp<ALooper> looper = new ALooper; + looper->start(); + + sp<RemoteDisplayClient> client = new RemoteDisplayClient; + sp<WifiDisplaySource> source = new WifiDisplaySource(session, client, path); + looper->registerHandler(source); + + AString iface = StringPrintf("%s:%d", addr.c_str(), port); + CHECK_EQ((status_t)OK, source->start(iface.c_str())); + + client->waitUntilDone(); + + source->stop(); +} + } // namespace android int main(int argc, char **argv) { @@ -187,41 +209,17 @@ int main(int argc, char **argv) { DataSource::RegisterDefaultSniffers(); - AString connectToHost; - int32_t connectToPort = -1; - AString uri; - AString listenOnAddr; int32_t listenOnPort = -1; + AString path; + int res; - while ((res = getopt(argc, argv, "hc:l:u:")) >= 0) { + while ((res = getopt(argc, argv, "hl:f:")) >= 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': + case 'f': { - uri = optarg; + path = optarg; break; } @@ -255,47 +253,17 @@ int main(int argc, char **argv) { } } - if (connectToPort >= 0 && listenOnPort >= 0) { - fprintf(stderr, - "You can connect to a source or create one, " - "but not both at the same time.\n"); - exit(1); - } - if (listenOnPort >= 0) { - createSource(listenOnAddr, listenOnPort); - exit(0); - } - - 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 (path.empty()) { + createSource(listenOnAddr, listenOnPort); + } else { + createFileSource(listenOnAddr, listenOnPort, path.c_str()); + } - if (connectToPort >= 0) { - sink->start(connectToHost.c_str(), connectToPort); - } else { - sink->start(uri.c_str()); + exit(0); } - looper->start(true /* runOnCallingThread */); + usage(argv[0]); return 0; } |