summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/wifi-display
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/wifi-display')
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.cpp245
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.h4
-rw-r--r--media/libstagefright/wifi-display/Android.mk33
-rw-r--r--media/libstagefright/wifi-display/MediaSender.cpp474
-rw-r--r--media/libstagefright/wifi-display/MediaSender.h131
-rw-r--r--media/libstagefright/wifi-display/Parameters.cpp4
-rw-r--r--media/libstagefright/wifi-display/TimeSeries.cpp67
-rw-r--r--media/libstagefright/wifi-display/VideoFormats.cpp419
-rw-r--r--media/libstagefright/wifi-display/VideoFormats.h106
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPBase.h (renamed from media/libstagefright/wifi-display/TimeSeries.h)39
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.cpp795
-rw-r--r--media/libstagefright/wifi-display/rtp/RTPSender.h120
-rw-r--r--media/libstagefright/wifi-display/sink/LinearRegression.cpp110
-rw-r--r--media/libstagefright/wifi-display/sink/LinearRegression.h52
-rw-r--r--media/libstagefright/wifi-display/sink/RTPSink.cpp806
-rw-r--r--media/libstagefright/wifi-display/sink/RTPSink.h98
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.cpp396
-rw-r--r--media/libstagefright/wifi-display/sink/TunnelRenderer.h84
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp644
-rw-r--r--media/libstagefright/wifi-display/sink/WifiDisplaySink.h147
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp112
-rw-r--r--media/libstagefright/wifi-display/source/Converter.h13
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp700
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.h82
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.cpp19
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.h5
-rw-r--r--media/libstagefright/wifi-display/source/Sender.cpp870
-rw-r--r--media/libstagefright/wifi-display/source/Sender.h169
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.cpp290
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.h11
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp329
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h23
-rw-r--r--media/libstagefright/wifi-display/udptest.cpp355
-rw-r--r--media/libstagefright/wifi-display/wfd.cpp140
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> &notify)
+ : mNetSession(netSession),
+ mNotify(notify),
+ mMode(MODE_UNDEFINED),
+ mGeneration(0),
+ mPrevTimeUs(-1ll),
+ mInitDoneCount(0),
+ mLogFile(NULL) {
+ // mLogFile = fopen("/data/misc/log.ts", "wb");
+}
+
+MediaSender::~MediaSender() {
+ if (mLogFile != NULL) {
+ fclose(mLogFile);
+ mLogFile = NULL;
+ }
+}
+
+status_t MediaSender::setHDCP(const sp<IHDCP> &hdcp) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ mHDCP = hdcp;
+
+ return OK;
+}
+
+ssize_t MediaSender::addTrack(const sp<AMessage> &format, uint32_t flags) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ TrackInfo info;
+ info.mFormat = format;
+ info.mFlags = flags;
+ info.mPacketizerTrackIndex = -1;
+
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ info.mIsAudio = !strncasecmp("audio/", mime.c_str(), 6);
+
+ size_t index = mTrackInfos.size();
+ mTrackInfos.push_back(info);
+
+ return index;
+}
+
+status_t MediaSender::initAsync(
+ ssize_t trackIndex,
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ RTPSender::TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ RTPSender::TransportMode rtcpMode,
+ int32_t *localRTPPort) {
+ if (trackIndex < 0) {
+ if (mMode != MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ uint32_t flags = 0;
+ if (mHDCP != NULL) {
+ // XXX Determine proper HDCP version.
+ flags |= TSPacketizer::EMIT_HDCP20_DESCRIPTOR;
+ }
+ mTSPacketizer = new TSPacketizer(flags);
+
+ status_t err = OK;
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ TrackInfo *info = &mTrackInfos.editItemAt(i);
+
+ ssize_t packetizerTrackIndex =
+ mTSPacketizer->addTrack(info->mFormat);
+
+ if (packetizerTrackIndex < 0) {
+ err = packetizerTrackIndex;
+ break;
+ }
+
+ info->mPacketizerTrackIndex = packetizerTrackIndex;
+ }
+
+ if (err == OK) {
+ sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ notify->setInt32("generation", mGeneration);
+ mTSSender = new RTPSender(mNetSession, notify);
+ looper()->registerHandler(mTSSender);
+
+ err = mTSSender->initAsync(
+ remoteHost,
+ remoteRTPPort,
+ rtpMode,
+ remoteRTCPPort,
+ rtcpMode,
+ localRTPPort);
+
+ if (err != OK) {
+ looper()->unregisterHandler(mTSSender->id());
+ mTSSender.clear();
+ }
+ }
+
+ if (err != OK) {
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ TrackInfo *info = &mTrackInfos.editItemAt(i);
+ info->mPacketizerTrackIndex = -1;
+ }
+
+ mTSPacketizer.clear();
+ return err;
+ }
+
+ mMode = MODE_TRANSPORT_STREAM;
+ mInitDoneCount = 1;
+
+ return OK;
+ }
+
+ if (mMode == MODE_TRANSPORT_STREAM) {
+ return INVALID_OPERATION;
+ }
+
+ if ((size_t)trackIndex >= mTrackInfos.size()) {
+ return -ERANGE;
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+ if (info->mSender != NULL) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
+ notify->setInt32("generation", mGeneration);
+ notify->setSize("trackIndex", trackIndex);
+
+ info->mSender = new RTPSender(mNetSession, notify);
+ looper()->registerHandler(info->mSender);
+
+ status_t err = info->mSender->initAsync(
+ remoteHost,
+ remoteRTPPort,
+ rtpMode,
+ remoteRTCPPort,
+ rtcpMode,
+ localRTPPort);
+
+ if (err != OK) {
+ looper()->unregisterHandler(info->mSender->id());
+ info->mSender.clear();
+
+ return err;
+ }
+
+ if (mMode == MODE_UNDEFINED) {
+ mInitDoneCount = mTrackInfos.size();
+ }
+
+ mMode = MODE_ELEMENTARY_STREAMS;
+
+ return OK;
+}
+
+status_t MediaSender::queueAccessUnit(
+ size_t trackIndex, const sp<ABuffer> &accessUnit) {
+ if (mMode == MODE_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ if (trackIndex >= mTrackInfos.size()) {
+ return -ERANGE;
+ }
+
+ if (mMode == MODE_TRANSPORT_STREAM) {
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+ info->mAccessUnits.push_back(accessUnit);
+
+ mTSPacketizer->extractCSDIfNecessary(info->mPacketizerTrackIndex);
+
+ for (;;) {
+ ssize_t minTrackIndex = -1;
+ int64_t minTimeUs = -1ll;
+
+ for (size_t i = 0; i < mTrackInfos.size(); ++i) {
+ const TrackInfo &info = mTrackInfos.itemAt(i);
+
+ if (info.mAccessUnits.empty()) {
+ minTrackIndex = -1;
+ minTimeUs = -1ll;
+ break;
+ }
+
+ int64_t timeUs;
+ const sp<ABuffer> &accessUnit = *info.mAccessUnits.begin();
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ if (minTrackIndex < 0 || timeUs < minTimeUs) {
+ minTrackIndex = i;
+ minTimeUs = timeUs;
+ }
+ }
+
+ if (minTrackIndex < 0) {
+ return OK;
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(minTrackIndex);
+ sp<ABuffer> accessUnit = *info->mAccessUnits.begin();
+ info->mAccessUnits.erase(info->mAccessUnits.begin());
+
+ sp<ABuffer> tsPackets;
+ status_t err = packetizeAccessUnit(
+ minTrackIndex, accessUnit, &tsPackets);
+
+ if (err == OK) {
+ if (mLogFile != NULL) {
+ fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
+ }
+
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ tsPackets->meta()->setInt64("timeUs", timeUs);
+
+ err = mTSSender->queueBuffer(
+ tsPackets,
+ 33 /* packetType */,
+ RTPSender::PACKETIZATION_TRANSPORT_STREAM);
+ }
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+
+ TrackInfo *info = &mTrackInfos.editItemAt(trackIndex);
+
+ return info->mSender->queueBuffer(
+ accessUnit,
+ info->mIsAudio ? 96 : 97 /* packetType */,
+ info->mIsAudio
+ ? RTPSender::PACKETIZATION_AAC : RTPSender::PACKETIZATION_H264);
+}
+
+void MediaSender::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSenderNotify:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mGeneration) {
+ break;
+ }
+
+ onSenderNotify(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void MediaSender::onSenderNotify(const sp<AMessage> &msg) {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ switch (what) {
+ case RTPSender::kWhatInitDone:
+ {
+ --mInitDoneCount;
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ if (err != OK) {
+ notifyInitDone(err);
+ ++mGeneration;
+ break;
+ }
+
+ if (mInitDoneCount == 0) {
+ notifyInitDone(OK);
+ }
+ break;
+ }
+
+ case RTPSender::kWhatError:
+ {
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ notifyError(err);
+ break;
+ }
+
+ case kWhatNetworkStall:
+ {
+ size_t numBytesQueued;
+ CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
+
+ notifyNetworkStall(numBytesQueued);
+ break;
+ }
+
+ 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> &notify);
+
+ status_t setHDCP(const sp<IHDCP> &hdcp);
+
+ enum FlagBits {
+ FLAG_MANUALLY_PREPEND_SPS_PPS = 1,
+ };
+ ssize_t addTrack(const sp<AMessage> &format, uint32_t flags);
+
+ // If trackIndex == -1, initialize for transport stream muxing.
+ status_t initAsync(
+ ssize_t trackIndex,
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ RTPSender::TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ RTPSender::TransportMode rtcpMode,
+ int32_t *localRTPPort);
+
+ status_t queueAccessUnit(
+ size_t trackIndex, const sp<ABuffer> &accessUnit);
+
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ virtual ~MediaSender();
+
+private:
+ enum {
+ kWhatSenderNotify,
+ };
+
+ enum Mode {
+ MODE_UNDEFINED,
+ MODE_TRANSPORT_STREAM,
+ MODE_ELEMENTARY_STREAMS,
+ };
+
+ struct TrackInfo {
+ sp<AMessage> mFormat;
+ uint32_t mFlags;
+ sp<RTPSender> mSender;
+ List<sp<ABuffer> > mAccessUnits;
+ ssize_t mPacketizerTrackIndex;
+ bool mIsAudio;
+ };
+
+ sp<ANetworkSession> mNetSession;
+ sp<AMessage> mNotify;
+
+ sp<IHDCP> mHDCP;
+
+ Mode mMode;
+ int32_t mGeneration;
+
+ Vector<TrackInfo> mTrackInfos;
+
+ sp<TSPacketizer> mTSPacketizer;
+ sp<RTPSender> mTSSender;
+ int64_t mPrevTimeUs;
+
+ size_t mInitDoneCount;
+
+ FILE *mLogFile;
+
+ void onSenderNotify(const sp<AMessage> &msg);
+
+ void notifyInitDone(status_t err);
+ void notifyError(status_t err);
+ void notifyNetworkStall(size_t numBytesQueued);
+
+ status_t packetizeAccessUnit(
+ size_t trackIndex,
+ sp<ABuffer> accessUnit,
+ sp<ABuffer> *tsPackets);
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaSender);
+};
+
+} // namespace android
+
+#endif // MEDIA_SENDER_H_
+
diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp
index f7118b3..d2a61ea 100644
--- a/media/libstagefright/wifi-display/Parameters.cpp
+++ b/media/libstagefright/wifi-display/Parameters.cpp
@@ -65,7 +65,9 @@ status_t Parameters::parse(const char *data, size_t size) {
mDict.add(name, value);
- i += 2;
+ while (i + 1 < size && data[i] == '\r' && data[i + 1] == '\n') {
+ i += 2;
+ }
}
return OK;
diff --git a/media/libstagefright/wifi-display/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> &notify)
+ : mNetSession(netSession),
+ mNotify(notify),
+ mRTPMode(TRANSPORT_UNDEFINED),
+ mRTCPMode(TRANSPORT_UNDEFINED),
+ mRTPSessionID(0),
+ mRTCPSessionID(0),
+ mRTPConnected(false),
+ mRTCPConnected(false),
+ mLastNTPTime(0),
+ mLastRTPTime(0),
+ mNumRTPSent(0),
+ mNumRTPOctetsSent(0),
+ mNumSRsSent(0),
+ mRTPSeqNo(0),
+ mHistorySize(0) {
+}
+
+RTPSender::~RTPSender() {
+ if (mRTCPSessionID != 0) {
+ mNetSession->destroySession(mRTCPSessionID);
+ mRTCPSessionID = 0;
+ }
+
+ if (mRTPSessionID != 0) {
+ mNetSession->destroySession(mRTPSessionID);
+ mRTPSessionID = 0;
+ }
+}
+
+// static
+int32_t RTPBase::PickRandomRTPPort() {
+ // Pick an even integer in range [1024, 65534)
+
+ static const size_t kRange = (65534 - 1024) / 2;
+
+ return (int32_t)(((float)(kRange + 1) * rand()) / RAND_MAX) * 2 + 1024;
+}
+
+status_t RTPSender::initAsync(
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ TransportMode rtcpMode,
+ int32_t *outLocalRTPPort) {
+ if (mRTPMode != TRANSPORT_UNDEFINED
+ || rtpMode == TRANSPORT_UNDEFINED
+ || rtpMode == TRANSPORT_NONE
+ || rtcpMode == TRANSPORT_UNDEFINED) {
+ return INVALID_OPERATION;
+ }
+
+ CHECK_NE(rtpMode, TRANSPORT_TCP_INTERLEAVED);
+ CHECK_NE(rtcpMode, TRANSPORT_TCP_INTERLEAVED);
+
+ if ((rtcpMode == TRANSPORT_NONE && remoteRTCPPort >= 0)
+ || (rtcpMode != TRANSPORT_NONE && remoteRTCPPort < 0)) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+ sp<AMessage> rtcpNotify;
+ if (remoteRTCPPort >= 0) {
+ rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+ }
+
+ CHECK_EQ(mRTPSessionID, 0);
+ CHECK_EQ(mRTCPSessionID, 0);
+
+ int32_t localRTPPort;
+
+ for (;;) {
+ localRTPPort = PickRandomRTPPort();
+
+ status_t err;
+ if (rtpMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ localRTPPort,
+ remoteHost,
+ remoteRTPPort,
+ rtpNotify,
+ &mRTPSessionID);
+ } else {
+ CHECK_EQ(rtpMode, TRANSPORT_TCP);
+ err = mNetSession->createTCPDatagramSession(
+ localRTPPort,
+ remoteHost,
+ remoteRTPPort,
+ rtpNotify,
+ &mRTPSessionID);
+ }
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (remoteRTCPPort < 0) {
+ break;
+ }
+
+ if (rtcpMode == TRANSPORT_UDP) {
+ err = mNetSession->createUDPSession(
+ localRTPPort + 1,
+ remoteHost,
+ remoteRTCPPort,
+ rtcpNotify,
+ &mRTCPSessionID);
+ } else {
+ CHECK_EQ(rtcpMode, TRANSPORT_TCP);
+ err = mNetSession->createTCPDatagramSession(
+ localRTPPort + 1,
+ remoteHost,
+ remoteRTCPPort,
+ rtcpNotify,
+ &mRTCPSessionID);
+ }
+
+ if (err == OK) {
+ break;
+ }
+
+ mNetSession->destroySession(mRTPSessionID);
+ mRTPSessionID = 0;
+ }
+
+ if (rtpMode == TRANSPORT_UDP) {
+ mRTPConnected = true;
+ }
+
+ if (rtcpMode == TRANSPORT_UDP) {
+ mRTCPConnected = true;
+ }
+
+ mRTPMode = rtpMode;
+ mRTCPMode = rtcpMode;
+ *outLocalRTPPort = localRTPPort;
+
+ if (mRTPMode == TRANSPORT_UDP
+ && (mRTCPMode == TRANSPORT_UDP || mRTCPMode == TRANSPORT_NONE)) {
+ notifyInitDone(OK);
+ }
+
+ return OK;
+}
+
+status_t RTPSender::queueBuffer(
+ const sp<ABuffer> &buffer, uint8_t packetType, PacketizationMode mode) {
+ status_t err;
+
+ switch (mode) {
+ case PACKETIZATION_NONE:
+ err = queueRawPacket(buffer, packetType);
+ break;
+
+ case PACKETIZATION_TRANSPORT_STREAM:
+ err = queueTSPackets(buffer, packetType);
+ break;
+
+ case PACKETIZATION_H264:
+ err = queueAVCBuffer(buffer, packetType);
+ break;
+
+ default:
+ TRESPASS();
+ }
+
+ return err;
+}
+
+status_t RTPSender::queueRawPacket(
+ const sp<ABuffer> &packet, uint8_t packetType) {
+ CHECK_LE(packet->size(), kMaxUDPPacketSize - 12);
+
+ int64_t timeUs;
+ CHECK(packet->meta()->findInt64("timeUs", &timeUs));
+
+ sp<ABuffer> udpPacket = new ABuffer(12 + packet->size());
+
+ udpPacket->setInt32Data(mRTPSeqNo);
+
+ uint8_t *rtp = udpPacket->data();
+ rtp[0] = 0x80;
+ rtp[1] = packetType;
+
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ uint32_t rtpTime = (timeUs * 9) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ memcpy(&rtp[12], packet->data(), packet->size());
+
+ return sendRTPPacket(
+ udpPacket,
+ true /* storeInHistory */,
+ true /* timeValid */,
+ ALooper::GetNowUs());
+}
+
+status_t RTPSender::queueTSPackets(
+ const sp<ABuffer> &tsPackets, uint8_t packetType) {
+ CHECK_EQ(0, tsPackets->size() % 188);
+
+ int64_t timeUs;
+ CHECK(tsPackets->meta()->findInt64("timeUs", &timeUs));
+
+ const size_t numTSPackets = tsPackets->size() / 188;
+
+ size_t srcOffset = 0;
+ while (srcOffset < tsPackets->size()) {
+ sp<ABuffer> udpPacket =
+ new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
+
+ udpPacket->setInt32Data(mRTPSeqNo);
+
+ uint8_t *rtp = udpPacket->data();
+ rtp[0] = 0x80;
+ rtp[1] = packetType;
+
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ int64_t nowUs = ALooper::GetNowUs();
+ uint32_t rtpTime = (nowUs * 9) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ size_t numTSPackets = (tsPackets->size() - srcOffset) / 188;
+ if (numTSPackets > kMaxNumTSPacketsPerRTPPacket) {
+ numTSPackets = kMaxNumTSPacketsPerRTPPacket;
+ }
+
+ memcpy(&rtp[12], tsPackets->data() + srcOffset, numTSPackets * 188);
+
+ udpPacket->setRange(0, 12 + numTSPackets * 188);
+
+ srcOffset += numTSPackets * 188;
+ bool isLastPacket = (srcOffset == tsPackets->size());
+
+ status_t err = sendRTPPacket(
+ udpPacket,
+ true /* storeInHistory */,
+ isLastPacket /* timeValid */,
+ timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t RTPSender::queueAVCBuffer(
+ const sp<ABuffer> &accessUnit, uint8_t packetType) {
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ uint32_t rtpTime = (timeUs * 9 / 100ll);
+
+ List<sp<ABuffer> > packets;
+
+ sp<ABuffer> out = new ABuffer(kMaxUDPPacketSize);
+ size_t outBytesUsed = 12; // Placeholder for RTP header.
+
+ const uint8_t *data = accessUnit->data();
+ size_t size = accessUnit->size();
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(
+ &data, &size, &nalStart, &nalSize,
+ true /* startCodeFollows */) == OK) {
+ size_t bytesNeeded = nalSize + 2;
+ if (outBytesUsed == 12) {
+ ++bytesNeeded;
+ }
+
+ if (outBytesUsed + bytesNeeded > out->capacity()) {
+ bool emitSingleNALPacket = false;
+
+ if (outBytesUsed == 12
+ && outBytesUsed + nalSize <= out->capacity()) {
+ // We haven't emitted anything into the current packet yet and
+ // this NAL unit fits into a single-NAL-unit-packet while
+ // it wouldn't have fit as part of a STAP-A packet.
+
+ memcpy(out->data() + outBytesUsed, nalStart, nalSize);
+ outBytesUsed += nalSize;
+
+ emitSingleNALPacket = true;
+ }
+
+ if (outBytesUsed > 12) {
+ out->setRange(0, outBytesUsed);
+ packets.push_back(out);
+ out = new ABuffer(kMaxUDPPacketSize);
+ outBytesUsed = 12; // Placeholder for RTP header
+ }
+
+ if (emitSingleNALPacket) {
+ continue;
+ }
+ }
+
+ if (outBytesUsed + bytesNeeded <= out->capacity()) {
+ uint8_t *dst = out->data() + outBytesUsed;
+
+ if (outBytesUsed == 12) {
+ *dst++ = 24; // STAP-A header
+ }
+
+ *dst++ = (nalSize >> 8) & 0xff;
+ *dst++ = nalSize & 0xff;
+ memcpy(dst, nalStart, nalSize);
+
+ outBytesUsed += bytesNeeded;
+ continue;
+ }
+
+ // This single NAL unit does not fit into a single RTP packet,
+ // we need to emit an FU-A.
+
+ CHECK_EQ(outBytesUsed, 12u);
+
+ uint8_t nalType = nalStart[0] & 0x1f;
+ uint8_t nri = (nalStart[0] >> 5) & 3;
+
+ size_t srcOffset = 1;
+ while (srcOffset < nalSize) {
+ size_t copy = out->capacity() - outBytesUsed - 2;
+ if (copy > nalSize - srcOffset) {
+ copy = nalSize - srcOffset;
+ }
+
+ uint8_t *dst = out->data() + outBytesUsed;
+ dst[0] = (nri << 5) | 28;
+
+ dst[1] = nalType;
+
+ if (srcOffset == 1) {
+ dst[1] |= 0x80;
+ }
+
+ if (srcOffset + copy == nalSize) {
+ dst[1] |= 0x40;
+ }
+
+ memcpy(&dst[2], nalStart + srcOffset, copy);
+ srcOffset += copy;
+
+ out->setRange(0, outBytesUsed + copy + 2);
+
+ packets.push_back(out);
+ out = new ABuffer(kMaxUDPPacketSize);
+ outBytesUsed = 12; // Placeholder for RTP header
+ }
+ }
+
+ if (outBytesUsed > 12) {
+ out->setRange(0, outBytesUsed);
+ packets.push_back(out);
+ }
+
+ while (!packets.empty()) {
+ sp<ABuffer> out = *packets.begin();
+ packets.erase(packets.begin());
+
+ out->setInt32Data(mRTPSeqNo);
+
+ bool last = packets.empty();
+
+ uint8_t *dst = out->data();
+
+ dst[0] = 0x80;
+
+ dst[1] = packetType;
+ if (last) {
+ dst[1] |= 1 << 7; // M-bit
+ }
+
+ dst[2] = (mRTPSeqNo >> 8) & 0xff;
+ dst[3] = mRTPSeqNo & 0xff;
+ ++mRTPSeqNo;
+
+ dst[4] = rtpTime >> 24;
+ dst[5] = (rtpTime >> 16) & 0xff;
+ dst[6] = (rtpTime >> 8) & 0xff;
+ dst[7] = rtpTime & 0xff;
+ dst[8] = kSourceID >> 24;
+ dst[9] = (kSourceID >> 16) & 0xff;
+ dst[10] = (kSourceID >> 8) & 0xff;
+ dst[11] = kSourceID & 0xff;
+
+ status_t err = sendRTPPacket(out, true /* storeInHistory */);
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t RTPSender::sendRTPPacket(
+ const sp<ABuffer> &buffer, bool storeInHistory,
+ bool timeValid, int64_t timeUs) {
+ CHECK(mRTPConnected);
+
+ status_t err = mNetSession->sendRequest(
+ mRTPSessionID, buffer->data(), buffer->size(),
+ timeValid, timeUs);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mLastNTPTime = GetNowNTP();
+ mLastRTPTime = U32_AT(buffer->data() + 4);
+
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += buffer->size() - 12;
+
+ if (storeInHistory) {
+ if (mHistorySize == kMaxHistorySize) {
+ mHistory.erase(mHistory.begin());
+ } else {
+ ++mHistorySize;
+ }
+ mHistory.push_back(buffer);
+ }
+
+ return OK;
+}
+
+// static
+uint64_t RTPSender::GetNowNTP() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL /* timezone */);
+
+ uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
+
+ nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+ uint64_t hi = nowUs / 1000000ll;
+ uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+ return (hi << 32) | lo;
+}
+
+void RTPSender::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRTPNotify:
+ case kWhatRTCPNotify:
+ onNetNotify(msg->what() == kWhatRTPNotify, msg);
+ break;
+
+ default:
+ TRESPASS();
+ }
+}
+
+void RTPSender::onNetNotify(bool isRTP, const sp<AMessage> &msg) {
+ int32_t reason;
+ CHECK(msg->findInt32("reason", &reason));
+
+ switch (reason) {
+ case ANetworkSession::kWhatError:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ int32_t err;
+ CHECK(msg->findInt32("err", &err));
+
+ int32_t errorOccuredDuringSend;
+ CHECK(msg->findInt32("send", &errorOccuredDuringSend));
+
+ AString detail;
+ CHECK(msg->findString("detail", &detail));
+
+ ALOGE("An error occurred during %s in session %d "
+ "(%d, '%s' (%s)).",
+ errorOccuredDuringSend ? "send" : "receive",
+ sessionID,
+ err,
+ detail.c_str(),
+ strerror(-err));
+
+ mNetSession->destroySession(sessionID);
+
+ if (sessionID == mRTPSessionID) {
+ mRTPSessionID = 0;
+ } else if (sessionID == mRTCPSessionID) {
+ mRTCPSessionID = 0;
+ }
+
+ if (!mRTPConnected
+ || (mRTPMode != TRANSPORT_NONE && !mRTCPConnected)) {
+ // We haven't completed initialization, attach the error
+ // to the notification instead.
+ notifyInitDone(err);
+ break;
+ }
+
+ notifyError(err);
+ break;
+ }
+
+ case ANetworkSession::kWhatDatagram:
+ {
+ sp<ABuffer> data;
+ CHECK(msg->findBuffer("data", &data));
+
+ if (isRTP) {
+ ALOGW("Huh? Received data on RTP connection...");
+ } else {
+ onRTCPData(data);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatConnected:
+ {
+ int32_t sessionID;
+ CHECK(msg->findInt32("sessionID", &sessionID));
+
+ if (isRTP) {
+ CHECK_EQ(mRTPMode, TRANSPORT_TCP);
+ CHECK_EQ(sessionID, mRTPSessionID);
+ mRTPConnected = true;
+ } else {
+ CHECK_EQ(mRTCPMode, TRANSPORT_TCP);
+ CHECK_EQ(sessionID, mRTCPSessionID);
+ mRTCPConnected = true;
+ }
+
+ if (mRTPConnected
+ && (mRTCPMode == TRANSPORT_NONE || mRTCPConnected)) {
+ notifyInitDone(OK);
+ }
+ break;
+ }
+
+ case ANetworkSession::kWhatNetworkStall:
+ {
+ size_t numBytesQueued;
+ CHECK(msg->findSize("numBytesQueued", &numBytesQueued));
+
+ notifyNetworkStall(numBytesQueued);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+status_t RTPSender::onRTCPData(const sp<ABuffer> &buffer) {
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ while (size > 0) {
+ if (size < 8) {
+ // Too short to be a valid RTCP header
+ return ERROR_MALFORMED;
+ }
+
+ if ((data[0] >> 6) != 2) {
+ // Unsupported version.
+ return ERROR_UNSUPPORTED;
+ }
+
+ if (data[0] & 0x20) {
+ // Padding present.
+
+ size_t paddingLength = data[size - 1];
+
+ if (paddingLength + 12 > size) {
+ // If we removed this much padding we'd end up with something
+ // that's too short to be a valid RTP header.
+ return ERROR_MALFORMED;
+ }
+
+ size -= paddingLength;
+ }
+
+ size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+ if (size < headerLength) {
+ // Only received a partial packet?
+ return ERROR_MALFORMED;
+ }
+
+ switch (data[1]) {
+ case 200:
+ case 201: // RR
+ parseReceiverReport(data, headerLength);
+ break;
+
+ case 202: // SDES
+ case 203:
+ break;
+
+ case 204: // APP
+ parseAPP(data, headerLength);
+ break;
+
+ case 205: // TSFB (transport layer specific feedback)
+ parseTSFB(data, headerLength);
+ break;
+
+ case 206: // PSFB (payload specific feedback)
+ // hexdump(data, headerLength);
+ break;
+
+ default:
+ {
+ ALOGW("Unknown RTCP packet type %u of size %d",
+ (unsigned)data[1], headerLength);
+ break;
+ }
+ }
+
+ data += headerLength;
+ size -= headerLength;
+ }
+
+ return OK;
+}
+
+status_t RTPSender::parseReceiverReport(const uint8_t *data, size_t size) {
+ // hexdump(data, size);
+
+ float fractionLost = data[12] / 256.0f;
+
+ ALOGI("lost %.2f %% of packets during report interval.",
+ 100.0f * fractionLost);
+
+ return OK;
+}
+
+status_t RTPSender::parseTSFB(const uint8_t *data, size_t size) {
+ if ((data[0] & 0x1f) != 1) {
+ return ERROR_UNSUPPORTED; // We only support NACK for now.
+ }
+
+ uint32_t srcId = U32_AT(&data[8]);
+ if (srcId != kSourceID) {
+ return ERROR_MALFORMED;
+ }
+
+ for (size_t i = 12; i < size; i += 4) {
+ uint16_t seqNo = U16_AT(&data[i]);
+ uint16_t blp = U16_AT(&data[i + 2]);
+
+ List<sp<ABuffer> >::iterator it = mHistory.begin();
+ bool foundSeqNo = false;
+ while (it != mHistory.end()) {
+ const sp<ABuffer> &buffer = *it;
+
+ uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
+
+ bool retransmit = false;
+ if (bufferSeqNo == seqNo) {
+ retransmit = true;
+ } else if (blp != 0) {
+ for (size_t i = 0; i < 16; ++i) {
+ if ((blp & (1 << i))
+ && (bufferSeqNo == ((seqNo + i + 1) & 0xffff))) {
+ blp &= ~(1 << i);
+ retransmit = true;
+ }
+ }
+ }
+
+ if (retransmit) {
+ ALOGV("retransmitting seqNo %d", bufferSeqNo);
+
+ CHECK_EQ((status_t)OK,
+ sendRTPPacket(buffer, false /* storeInHistory */));
+
+ if (bufferSeqNo == seqNo) {
+ foundSeqNo = true;
+ }
+
+ if (foundSeqNo && blp == 0) {
+ break;
+ }
+ }
+
+ ++it;
+ }
+
+ if (!foundSeqNo || blp != 0) {
+ ALOGI("Some sequence numbers were no longer available for "
+ "retransmission (seqNo = %d, foundSeqNo = %d, blp = 0x%04x)",
+ seqNo, foundSeqNo, blp);
+
+ if (!mHistory.empty()) {
+ int32_t earliest = (*mHistory.begin())->int32Data() & 0xffff;
+ int32_t latest = (*--mHistory.end())->int32Data() & 0xffff;
+
+ ALOGI("have seq numbers from %d - %d", earliest, latest);
+ }
+ }
+ }
+
+ return OK;
+}
+
+status_t RTPSender::parseAPP(const uint8_t *data, size_t size) {
+ 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> &notify);
+
+ status_t initAsync(
+ const char *remoteHost,
+ int32_t remoteRTPPort,
+ TransportMode rtpMode,
+ int32_t remoteRTCPPort,
+ TransportMode rtcpMode,
+ int32_t *outLocalRTPPort);
+
+ status_t queueBuffer(
+ const sp<ABuffer> &buffer,
+ uint8_t packetType,
+ PacketizationMode mode);
+
+protected:
+ virtual ~RTPSender();
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatRTPNotify,
+ kWhatRTCPNotify,
+ };
+
+ enum {
+ kMaxNumTSPacketsPerRTPPacket = (kMaxUDPPacketSize - 12) / 188,
+ kMaxHistorySize = 1024,
+ kSourceID = 0xdeadbeef,
+ };
+
+ sp<ANetworkSession> mNetSession;
+ sp<AMessage> mNotify;
+ TransportMode mRTPMode;
+ TransportMode mRTCPMode;
+ int32_t mRTPSessionID;
+ int32_t mRTCPSessionID;
+ bool mRTPConnected;
+ bool mRTCPConnected;
+
+ uint64_t mLastNTPTime;
+ uint32_t mLastRTPTime;
+ uint32_t mNumRTPSent;
+ uint32_t mNumRTPOctetsSent;
+ uint32_t mNumSRsSent;
+
+ uint32_t mRTPSeqNo;
+
+ List<sp<ABuffer> > mHistory;
+ size_t mHistorySize;
+
+ static uint64_t GetNowNTP();
+
+ status_t queueRawPacket(const sp<ABuffer> &tsPackets, uint8_t packetType);
+ status_t queueTSPackets(const sp<ABuffer> &tsPackets, uint8_t packetType);
+ status_t queueAVCBuffer(const sp<ABuffer> &accessUnit, uint8_t packetType);
+
+ status_t sendRTPPacket(
+ const sp<ABuffer> &packet, bool storeInHistory,
+ bool timeValid = false, int64_t timeUs = -1ll);
+
+ void onNetNotify(bool isRTP, const sp<AMessage> &msg);
+
+ status_t onRTCPData(const sp<ABuffer> &data);
+ status_t parseReceiverReport(const uint8_t *data, size_t size);
+ status_t parseTSFB(const uint8_t *data, size_t size);
+ status_t parseAPP(const uint8_t *data, size_t size);
+
+ void notifyInitDone(status_t err);
+ void notifyError(status_t err);
+ void notifyNetworkStall(size_t numBytesQueued);
+
+ DISALLOW_EVIL_CONSTRUCTORS(RTPSender);
+};
+
+} // namespace android
+
+#endif // RTP_SENDER_H_
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.cpp b/media/libstagefright/wifi-display/sink/LinearRegression.cpp
deleted file mode 100644
index 8cfce37..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "LinearRegression"
-#include <utils/Log.h>
-
-#include "LinearRegression.h"
-
-#include <math.h>
-#include <string.h>
-
-namespace android {
-
-LinearRegression::LinearRegression(size_t historySize)
- : mHistorySize(historySize),
- mCount(0),
- mHistory(new Point[mHistorySize]),
- mSumX(0.0),
- mSumY(0.0) {
-}
-
-LinearRegression::~LinearRegression() {
- delete[] mHistory;
- mHistory = NULL;
-}
-
-void LinearRegression::addPoint(float x, float y) {
- if (mCount == mHistorySize) {
- const Point &oldest = mHistory[0];
-
- mSumX -= oldest.mX;
- mSumY -= oldest.mY;
-
- memmove(&mHistory[0], &mHistory[1], (mHistorySize - 1) * sizeof(Point));
- --mCount;
- }
-
- Point *newest = &mHistory[mCount++];
- newest->mX = x;
- newest->mY = y;
-
- mSumX += x;
- mSumY += y;
-}
-
-bool LinearRegression::approxLine(float *n1, float *n2, float *b) const {
- static const float kEpsilon = 1.0E-4;
-
- if (mCount < 2) {
- return false;
- }
-
- float sumX2 = 0.0f;
- float sumY2 = 0.0f;
- float sumXY = 0.0f;
-
- float meanX = mSumX / (float)mCount;
- float meanY = mSumY / (float)mCount;
-
- for (size_t i = 0; i < mCount; ++i) {
- const Point &p = mHistory[i];
-
- float x = p.mX - meanX;
- float y = p.mY - meanY;
-
- sumX2 += x * x;
- sumY2 += y * y;
- sumXY += x * y;
- }
-
- float T = sumX2 + sumY2;
- float D = sumX2 * sumY2 - sumXY * sumXY;
- float root = sqrt(T * T * 0.25 - D);
-
- float L1 = T * 0.5 - root;
-
- if (fabs(sumXY) > kEpsilon) {
- *n1 = 1.0;
- *n2 = (2.0 * L1 - sumX2) / sumXY;
-
- float mag = sqrt((*n1) * (*n1) + (*n2) * (*n2));
-
- *n1 /= mag;
- *n2 /= mag;
- } else {
- *n1 = 0.0;
- *n2 = 1.0;
- }
-
- *b = (*n1) * meanX + (*n2) * meanY;
-
- return true;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/LinearRegression.h b/media/libstagefright/wifi-display/sink/LinearRegression.h
deleted file mode 100644
index ca6f5a1..0000000
--- a/media/libstagefright/wifi-display/sink/LinearRegression.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LINEAR_REGRESSION_H_
-
-#define LINEAR_REGRESSION_H_
-
-#include <sys/types.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-// Helper class to fit a line to a set of points minimizing the sum of
-// squared (orthogonal) distances from line to individual points.
-struct LinearRegression {
- LinearRegression(size_t historySize);
- ~LinearRegression();
-
- void addPoint(float x, float y);
-
- bool approxLine(float *n1, float *n2, float *b) const;
-
-private:
- struct Point {
- float mX, mY;
- };
-
- size_t mHistorySize;
- size_t mCount;
- Point *mHistory;
-
- float mSumX, mSumY;
-
- DISALLOW_EVIL_CONSTRUCTORS(LinearRegression);
-};
-
-} // namespace android
-
-#endif // LINEAR_REGRESSION_H_
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.cpp b/media/libstagefright/wifi-display/sink/RTPSink.cpp
deleted file mode 100644
index 0918034..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.cpp
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "RTPSink"
-#include <utils/Log.h>
-
-#include "RTPSink.h"
-
-#include "ANetworkSession.h"
-#include "TunnelRenderer.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/Utils.h>
-
-namespace android {
-
-struct RTPSink::Source : public RefBase {
- Source(uint16_t seq, const sp<ABuffer> &buffer,
- const sp<AMessage> queueBufferMsg);
-
- bool updateSeq(uint16_t seq, const sp<ABuffer> &buffer);
-
- void addReportBlock(uint32_t ssrc, const sp<ABuffer> &buf);
-
-protected:
- virtual ~Source();
-
-private:
- static const uint32_t kMinSequential = 2;
- static const uint32_t kMaxDropout = 3000;
- static const uint32_t kMaxMisorder = 100;
- static const uint32_t kRTPSeqMod = 1u << 16;
-
- sp<AMessage> mQueueBufferMsg;
-
- uint16_t mMaxSeq;
- uint32_t mCycles;
- uint32_t mBaseSeq;
- uint32_t mBadSeq;
- uint32_t mProbation;
- uint32_t mReceived;
- uint32_t mExpectedPrior;
- uint32_t mReceivedPrior;
-
- void initSeq(uint16_t seq);
- void queuePacket(const sp<ABuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(Source);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::Source::Source(
- uint16_t seq, const sp<ABuffer> &buffer,
- const sp<AMessage> queueBufferMsg)
- : mQueueBufferMsg(queueBufferMsg),
- mProbation(kMinSequential) {
- initSeq(seq);
- mMaxSeq = seq - 1;
-
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
-}
-
-RTPSink::Source::~Source() {
-}
-
-void RTPSink::Source::initSeq(uint16_t seq) {
- mMaxSeq = seq;
- mCycles = 0;
- mBaseSeq = seq;
- mBadSeq = kRTPSeqMod + 1;
- mReceived = 0;
- mExpectedPrior = 0;
- mReceivedPrior = 0;
-}
-
-bool RTPSink::Source::updateSeq(uint16_t seq, const sp<ABuffer> &buffer) {
- uint16_t udelta = seq - mMaxSeq;
-
- if (mProbation) {
- // Startup phase
-
- if (seq == mMaxSeq + 1) {
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
-
- --mProbation;
- mMaxSeq = seq;
- if (mProbation == 0) {
- initSeq(seq);
- ++mReceived;
-
- return true;
- }
- } else {
- // Packet out of sequence, restart startup phase
-
- mProbation = kMinSequential - 1;
- mMaxSeq = seq;
-
-#if 0
- mPackets.clear();
- mTotalBytesQueued = 0;
- ALOGI("XXX cleared packets");
-#endif
-
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
- }
-
- return false;
- }
-
- if (udelta < kMaxDropout) {
- // In order, with permissible gap.
-
- if (seq < mMaxSeq) {
- // Sequence number wrapped - count another 64K cycle
- mCycles += kRTPSeqMod;
- }
-
- mMaxSeq = seq;
- } else if (udelta <= kRTPSeqMod - kMaxMisorder) {
- // The sequence number made a very large jump
-
- if (seq == mBadSeq) {
- // Two sequential packets -- assume that the other side
- // restarted without telling us so just re-sync
- // (i.e. pretend this was the first packet)
-
- initSeq(seq);
- } else {
- mBadSeq = (seq + 1) & (kRTPSeqMod - 1);
-
- return false;
- }
- } else {
- // Duplicate or reordered packet.
- }
-
- ++mReceived;
-
- buffer->setInt32Data(mCycles | seq);
- queuePacket(buffer);
-
- return true;
-}
-
-void RTPSink::Source::queuePacket(const sp<ABuffer> &buffer) {
- sp<AMessage> msg = mQueueBufferMsg->dup();
- msg->setBuffer("buffer", buffer);
- msg->post();
-}
-
-void RTPSink::Source::addReportBlock(
- uint32_t ssrc, const sp<ABuffer> &buf) {
- uint32_t extMaxSeq = mMaxSeq | mCycles;
- uint32_t expected = extMaxSeq - mBaseSeq + 1;
-
- int64_t lost = (int64_t)expected - (int64_t)mReceived;
- if (lost > 0x7fffff) {
- lost = 0x7fffff;
- } else if (lost < -0x800000) {
- lost = -0x800000;
- }
-
- uint32_t expectedInterval = expected - mExpectedPrior;
- mExpectedPrior = expected;
-
- uint32_t receivedInterval = mReceived - mReceivedPrior;
- mReceivedPrior = mReceived;
-
- int64_t lostInterval = expectedInterval - receivedInterval;
-
- uint8_t fractionLost;
- if (expectedInterval == 0 || lostInterval <=0) {
- fractionLost = 0;
- } else {
- fractionLost = (lostInterval << 8) / expectedInterval;
- }
-
- uint8_t *ptr = buf->data() + buf->size();
-
- ptr[0] = ssrc >> 24;
- ptr[1] = (ssrc >> 16) & 0xff;
- ptr[2] = (ssrc >> 8) & 0xff;
- ptr[3] = ssrc & 0xff;
-
- ptr[4] = fractionLost;
-
- ptr[5] = (lost >> 16) & 0xff;
- ptr[6] = (lost >> 8) & 0xff;
- ptr[7] = lost & 0xff;
-
- ptr[8] = extMaxSeq >> 24;
- ptr[9] = (extMaxSeq >> 16) & 0xff;
- ptr[10] = (extMaxSeq >> 8) & 0xff;
- ptr[11] = extMaxSeq & 0xff;
-
- // XXX TODO:
-
- ptr[12] = 0x00; // interarrival jitter
- ptr[13] = 0x00;
- ptr[14] = 0x00;
- ptr[15] = 0x00;
-
- ptr[16] = 0x00; // last SR
- ptr[17] = 0x00;
- ptr[18] = 0x00;
- ptr[19] = 0x00;
-
- ptr[20] = 0x00; // delay since last SR
- ptr[21] = 0x00;
- ptr[22] = 0x00;
- ptr[23] = 0x00;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-RTPSink::RTPSink(
- const sp<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex)
- : mNetSession(netSession),
- mSurfaceTex(surfaceTex),
- mRTPPort(0),
- mRTPSessionID(0),
- mRTCPSessionID(0),
- mFirstArrivalTimeUs(-1ll),
- mNumPacketsReceived(0ll),
- mRegression(1000),
- mMaxDelayMs(-1ll) {
-}
-
-RTPSink::~RTPSink() {
- if (mRTCPSessionID != 0) {
- mNetSession->destroySession(mRTCPSessionID);
- }
-
- if (mRTPSessionID != 0) {
- mNetSession->destroySession(mRTPSessionID);
- }
-}
-
-status_t RTPSink::init(bool useTCPInterleaving) {
- if (useTCPInterleaving) {
- return OK;
- }
-
- int clientRtp;
-
- sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
- sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
- for (clientRtp = 15550;; clientRtp += 2) {
- int32_t rtpSession;
- status_t err = mNetSession->createUDPSession(
- clientRtp, rtpNotify, &rtpSession);
-
- if (err != OK) {
- ALOGI("failed to create RTP socket on port %d", clientRtp);
- continue;
- }
-
- int32_t rtcpSession;
- err = mNetSession->createUDPSession(
- clientRtp + 1, rtcpNotify, &rtcpSession);
-
- if (err == OK) {
- mRTPPort = clientRtp;
- mRTPSessionID = rtpSession;
- mRTCPSessionID = rtcpSession;
- break;
- }
-
- ALOGI("failed to create RTCP socket on port %d", clientRtp + 1);
- mNetSession->destroySession(rtpSession);
- }
-
- if (mRTPPort == 0) {
- return UNKNOWN_ERROR;
- }
-
- return OK;
-}
-
-int32_t RTPSink::getRTPPort() const {
- return mRTPPort;
-}
-
-void RTPSink::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatRTPNotify:
- case kWhatRTCPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred in session %d (%d, '%s/%s').",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- mNetSession->destroySession(sessionID);
-
- if (sessionID == mRTPSessionID) {
- mRTPSessionID = 0;
- } else if (sessionID == mRTCPSessionID) {
- mRTCPSessionID = 0;
- }
- break;
- }
-
- case ANetworkSession::kWhatDatagram:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- status_t err;
- if (msg->what() == kWhatRTPNotify) {
- err = parseRTP(data);
- } else {
- err = parseRTCP(data);
- }
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatSendRR:
- {
- onSendRR();
- break;
- }
-
- case kWhatPacketLost:
- {
- onPacketLost(msg);
- break;
- }
-
- case kWhatInject:
- {
- int32_t isRTP;
- CHECK(msg->findInt32("isRTP", &isRTP));
-
- sp<ABuffer> buffer;
- CHECK(msg->findBuffer("buffer", &buffer));
-
- status_t err;
- if (isRTP) {
- err = parseRTP(buffer);
- } else {
- err = parseRTCP(buffer);
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-status_t RTPSink::injectPacket(bool isRTP, const sp<ABuffer> &buffer) {
- sp<AMessage> msg = new AMessage(kWhatInject, id());
- msg->setInt32("isRTP", isRTP);
- msg->setBuffer("buffer", buffer);
- msg->post();
-
- return OK;
-}
-
-status_t RTPSink::parseRTP(const sp<ABuffer> &buffer) {
- size_t size = buffer->size();
- if (size < 12) {
- // Too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- const uint8_t *data = buffer->data();
-
- if ((data[0] >> 6) != 2) {
- // Unsupported version.
- return ERROR_UNSUPPORTED;
- }
-
- if (data[0] & 0x20) {
- // Padding present.
-
- size_t paddingLength = data[size - 1];
-
- if (paddingLength + 12 > size) {
- // If we removed this much padding we'd end up with something
- // that's too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- size -= paddingLength;
- }
-
- int numCSRCs = data[0] & 0x0f;
-
- size_t payloadOffset = 12 + 4 * numCSRCs;
-
- if (size < payloadOffset) {
- // Not enough data to fit the basic header and all the CSRC entries.
- return ERROR_MALFORMED;
- }
-
- if (data[0] & 0x10) {
- // Header eXtension present.
-
- if (size < payloadOffset + 4) {
- // Not enough data to fit the basic header, all CSRC entries
- // and the first 4 bytes of the extension header.
-
- return ERROR_MALFORMED;
- }
-
- const uint8_t *extensionData = &data[payloadOffset];
-
- size_t extensionLength =
- 4 * (extensionData[2] << 8 | extensionData[3]);
-
- if (size < payloadOffset + 4 + extensionLength) {
- return ERROR_MALFORMED;
- }
-
- payloadOffset += 4 + extensionLength;
- }
-
- uint32_t srcId = U32_AT(&data[8]);
- uint32_t rtpTime = U32_AT(&data[4]);
- uint16_t seqNo = U16_AT(&data[2]);
-
- int64_t arrivalTimeUs;
- CHECK(buffer->meta()->findInt64("arrivalTimeUs", &arrivalTimeUs));
-
- if (mFirstArrivalTimeUs < 0ll) {
- mFirstArrivalTimeUs = arrivalTimeUs;
- }
- arrivalTimeUs -= mFirstArrivalTimeUs;
-
- int64_t arrivalTimeMedia = (arrivalTimeUs * 9ll) / 100ll;
-
- ALOGV("seqNo: %d, SSRC 0x%08x, diff %lld",
- seqNo, srcId, rtpTime - arrivalTimeMedia);
-
- mRegression.addPoint((float)rtpTime, (float)arrivalTimeMedia);
-
- ++mNumPacketsReceived;
-
- float n1, n2, b;
- if (mRegression.approxLine(&n1, &n2, &b)) {
- ALOGV("Line %lld: %.2f %.2f %.2f, slope %.2f",
- mNumPacketsReceived, n1, n2, b, -n1 / n2);
-
- float expectedArrivalTimeMedia = (b - n1 * (float)rtpTime) / n2;
- float latenessMs = (arrivalTimeMedia - expectedArrivalTimeMedia) / 90.0;
-
- if (mMaxDelayMs < 0ll || latenessMs > mMaxDelayMs) {
- mMaxDelayMs = latenessMs;
- ALOGI("packet was %.2f ms late", latenessMs);
- }
- }
-
- sp<AMessage> meta = buffer->meta();
- meta->setInt32("ssrc", srcId);
- meta->setInt32("rtp-time", rtpTime);
- meta->setInt32("PT", data[1] & 0x7f);
- meta->setInt32("M", data[1] >> 7);
-
- buffer->setRange(payloadOffset, size - payloadOffset);
-
- ssize_t index = mSources.indexOfKey(srcId);
- if (index < 0) {
- if (mRenderer == NULL) {
- sp<AMessage> notifyLost = new AMessage(kWhatPacketLost, id());
- notifyLost->setInt32("ssrc", srcId);
-
- mRenderer = new TunnelRenderer(notifyLost, mSurfaceTex);
- looper()->registerHandler(mRenderer);
- }
-
- sp<AMessage> queueBufferMsg =
- new AMessage(TunnelRenderer::kWhatQueueBuffer, mRenderer->id());
-
- sp<Source> source = new Source(seqNo, buffer, queueBufferMsg);
- mSources.add(srcId, source);
- } else {
- mSources.valueAt(index)->updateSeq(seqNo, buffer);
- }
-
- return OK;
-}
-
-status_t RTPSink::parseRTCP(const sp<ABuffer> &buffer) {
- const uint8_t *data = buffer->data();
- size_t size = buffer->size();
-
- while (size > 0) {
- if (size < 8) {
- // Too short to be a valid RTCP header
- return ERROR_MALFORMED;
- }
-
- if ((data[0] >> 6) != 2) {
- // Unsupported version.
- return ERROR_UNSUPPORTED;
- }
-
- if (data[0] & 0x20) {
- // Padding present.
-
- size_t paddingLength = data[size - 1];
-
- if (paddingLength + 12 > size) {
- // If we removed this much padding we'd end up with something
- // that's too short to be a valid RTP header.
- return ERROR_MALFORMED;
- }
-
- size -= paddingLength;
- }
-
- size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
-
- if (size < headerLength) {
- // Only received a partial packet?
- return ERROR_MALFORMED;
- }
-
- switch (data[1]) {
- case 200:
- {
- parseSR(data, headerLength);
- break;
- }
-
- case 201: // RR
- case 202: // SDES
- case 204: // APP
- break;
-
- case 205: // TSFB (transport layer specific feedback)
- case 206: // PSFB (payload specific feedback)
- // hexdump(data, headerLength);
- break;
-
- case 203:
- {
- parseBYE(data, headerLength);
- break;
- }
-
- default:
- {
- ALOGW("Unknown RTCP packet type %u of size %d",
- (unsigned)data[1], headerLength);
- break;
- }
- }
-
- data += headerLength;
- size -= headerLength;
- }
-
- return OK;
-}
-
-status_t RTPSink::parseBYE(const uint8_t *data, size_t size) {
- size_t SC = data[0] & 0x3f;
-
- if (SC == 0 || size < (4 + SC * 4)) {
- // Packet too short for the minimal BYE header.
- return ERROR_MALFORMED;
- }
-
- uint32_t id = U32_AT(&data[4]);
-
- return OK;
-}
-
-status_t RTPSink::parseSR(const uint8_t *data, size_t size) {
- size_t RC = data[0] & 0x1f;
-
- if (size < (7 + RC * 6) * 4) {
- // Packet too short for the minimal SR header.
- return ERROR_MALFORMED;
- }
-
- uint32_t id = U32_AT(&data[4]);
- uint64_t ntpTime = U64_AT(&data[8]);
- uint32_t rtpTime = U32_AT(&data[16]);
-
- ALOGV("SR: ssrc 0x%08x, ntpTime 0x%016llx, rtpTime 0x%08x",
- id, ntpTime, rtpTime);
-
- return OK;
-}
-
-status_t RTPSink::connect(
- const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort) {
- ALOGI("connecting RTP/RTCP sockets to %s:{%d,%d}",
- host, remoteRtpPort, remoteRtcpPort);
-
- status_t err =
- mNetSession->connectUDPSession(mRTPSessionID, host, remoteRtpPort);
-
- if (err != OK) {
- return err;
- }
-
- err = mNetSession->connectUDPSession(mRTCPSessionID, host, remoteRtcpPort);
-
- if (err != OK) {
- return err;
- }
-
-#if 0
- sp<ABuffer> buf = new ABuffer(1500);
- memset(buf->data(), 0, buf->size());
-
- mNetSession->sendRequest(
- mRTPSessionID, buf->data(), buf->size());
-
- mNetSession->sendRequest(
- mRTCPSessionID, buf->data(), buf->size());
-#endif
-
- scheduleSendRR();
-
- return OK;
-}
-
-void RTPSink::scheduleSendRR() {
- (new AMessage(kWhatSendRR, id()))->post(2000000ll);
-}
-
-void RTPSink::addSDES(const sp<ABuffer> &buffer) {
- uint8_t *data = buffer->data() + buffer->size();
- data[0] = 0x80 | 1;
- data[1] = 202; // SDES
- data[4] = 0xde; // SSRC
- data[5] = 0xad;
- data[6] = 0xbe;
- data[7] = 0xef;
-
- size_t offset = 8;
-
- data[offset++] = 1; // CNAME
-
- AString cname = "stagefright@somewhere";
- data[offset++] = cname.size();
-
- memcpy(&data[offset], cname.c_str(), cname.size());
- offset += cname.size();
-
- data[offset++] = 6; // TOOL
-
- AString tool = "stagefright/1.0";
- data[offset++] = tool.size();
-
- memcpy(&data[offset], tool.c_str(), tool.size());
- offset += tool.size();
-
- data[offset++] = 0;
-
- if ((offset % 4) > 0) {
- size_t count = 4 - (offset % 4);
- switch (count) {
- case 3:
- data[offset++] = 0;
- case 2:
- data[offset++] = 0;
- case 1:
- data[offset++] = 0;
- }
- }
-
- size_t numWords = (offset / 4) - 1;
- data[2] = numWords >> 8;
- data[3] = numWords & 0xff;
-
- buffer->setRange(buffer->offset(), buffer->size() + offset);
-}
-
-void RTPSink::onSendRR() {
- sp<ABuffer> buf = new ABuffer(1500);
- buf->setRange(0, 0);
-
- uint8_t *ptr = buf->data();
- ptr[0] = 0x80 | 0;
- ptr[1] = 201; // RR
- ptr[2] = 0;
- ptr[3] = 1;
- ptr[4] = 0xde; // SSRC
- ptr[5] = 0xad;
- ptr[6] = 0xbe;
- ptr[7] = 0xef;
-
- buf->setRange(0, 8);
-
- size_t numReportBlocks = 0;
- for (size_t i = 0; i < mSources.size(); ++i) {
- uint32_t ssrc = mSources.keyAt(i);
- sp<Source> source = mSources.valueAt(i);
-
- if (numReportBlocks > 31 || buf->size() + 24 > buf->capacity()) {
- // Cannot fit another report block.
- break;
- }
-
- source->addReportBlock(ssrc, buf);
- ++numReportBlocks;
- }
-
- ptr[0] |= numReportBlocks; // 5 bit
-
- size_t sizeInWordsMinus1 = 1 + 6 * numReportBlocks;
- ptr[2] = sizeInWordsMinus1 >> 8;
- ptr[3] = sizeInWordsMinus1 & 0xff;
-
- buf->setRange(0, (sizeInWordsMinus1 + 1) * 4);
-
- addSDES(buf);
-
- mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-
- scheduleSendRR();
-}
-
-void RTPSink::onPacketLost(const sp<AMessage> &msg) {
- uint32_t srcId;
- CHECK(msg->findInt32("ssrc", (int32_t *)&srcId));
-
- int32_t seqNo;
- CHECK(msg->findInt32("seqNo", &seqNo));
-
- int32_t blp = 0;
-
- sp<ABuffer> buf = new ABuffer(1500);
- buf->setRange(0, 0);
-
- uint8_t *ptr = buf->data();
- ptr[0] = 0x80 | 1; // generic NACK
- ptr[1] = 205; // RTPFB
- ptr[2] = 0;
- ptr[3] = 3;
- ptr[4] = 0xde; // sender SSRC
- ptr[5] = 0xad;
- ptr[6] = 0xbe;
- ptr[7] = 0xef;
- ptr[8] = (srcId >> 24) & 0xff;
- ptr[9] = (srcId >> 16) & 0xff;
- ptr[10] = (srcId >> 8) & 0xff;
- ptr[11] = (srcId & 0xff);
- ptr[12] = (seqNo >> 8) & 0xff;
- ptr[13] = (seqNo & 0xff);
- ptr[14] = (blp >> 8) & 0xff;
- ptr[15] = (blp & 0xff);
-
- buf->setRange(0, 16);
-
- mNetSession->sendRequest(mRTCPSessionID, buf->data(), buf->size());
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/RTPSink.h b/media/libstagefright/wifi-display/sink/RTPSink.h
deleted file mode 100644
index a1d127d..0000000
--- a/media/libstagefright/wifi-display/sink/RTPSink.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef RTP_SINK_H_
-
-#define RTP_SINK_H_
-
-#include <media/stagefright/foundation/AHandler.h>
-
-#include "LinearRegression.h"
-
-#include <gui/Surface.h>
-
-namespace android {
-
-struct ABuffer;
-struct ANetworkSession;
-struct TunnelRenderer;
-
-// Creates a pair of sockets for RTP/RTCP traffic, instantiates a renderer
-// for incoming transport stream data and occasionally sends statistics over
-// the RTCP channel.
-struct RTPSink : public AHandler {
- RTPSink(const sp<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex);
-
- // If TCP interleaving is used, no UDP sockets are created, instead
- // incoming RTP/RTCP packets (arriving on the RTSP control connection)
- // are manually injected by WifiDisplaySink.
- status_t init(bool useTCPInterleaving);
-
- status_t connect(
- const char *host, int32_t remoteRtpPort, int32_t remoteRtcpPort);
-
- int32_t getRTPPort() const;
-
- status_t injectPacket(bool isRTP, const sp<ABuffer> &buffer);
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~RTPSink();
-
-private:
- enum {
- kWhatRTPNotify,
- kWhatRTCPNotify,
- kWhatSendRR,
- kWhatPacketLost,
- kWhatInject,
- };
-
- struct Source;
- struct StreamSource;
-
- sp<ANetworkSession> mNetSession;
- sp<ISurfaceTexture> mSurfaceTex;
- KeyedVector<uint32_t, sp<Source> > mSources;
-
- int32_t mRTPPort;
- int32_t mRTPSessionID;
- int32_t mRTCPSessionID;
-
- int64_t mFirstArrivalTimeUs;
- int64_t mNumPacketsReceived;
- LinearRegression mRegression;
- int64_t mMaxDelayMs;
-
- sp<TunnelRenderer> mRenderer;
-
- status_t parseRTP(const sp<ABuffer> &buffer);
- status_t parseRTCP(const sp<ABuffer> &buffer);
- status_t parseBYE(const uint8_t *data, size_t size);
- status_t parseSR(const uint8_t *data, size_t size);
-
- void addSDES(const sp<ABuffer> &buffer);
- void onSendRR();
- void onPacketLost(const sp<AMessage> &msg);
- void scheduleSendRR();
-
- DISALLOW_EVIL_CONSTRUCTORS(RTPSink);
-};
-
-} // namespace android
-
-#endif // RTP_SINK_H_
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp b/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
deleted file mode 100644
index bc35aef..0000000
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.cpp
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "TunnelRenderer"
-#include <utils/Log.h>
-
-#include "TunnelRenderer.h"
-
-#include "ATSParser.h"
-
-#include <binder/IMemory.h>
-#include <binder/IServiceManager.h>
-#include <gui/SurfaceComposerClient.h>
-#include <media/IMediaPlayerService.h>
-#include <media/IStreamSource.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <ui/DisplayInfo.h>
-
-namespace android {
-
-struct TunnelRenderer::PlayerClient : public BnMediaPlayerClient {
- PlayerClient() {}
-
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
- ALOGI("notify %d, %d, %d", msg, ext1, ext2);
- }
-
-protected:
- virtual ~PlayerClient() {}
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(PlayerClient);
-};
-
-struct TunnelRenderer::StreamSource : public BnStreamSource {
- StreamSource(TunnelRenderer *owner);
-
- virtual void setListener(const sp<IStreamListener> &listener);
- virtual void setBuffers(const Vector<sp<IMemory> > &buffers);
-
- virtual void onBufferAvailable(size_t index);
-
- virtual uint32_t flags() const;
-
- void doSomeWork();
-
-protected:
- virtual ~StreamSource();
-
-private:
- mutable Mutex mLock;
-
- TunnelRenderer *mOwner;
-
- sp<IStreamListener> mListener;
-
- Vector<sp<IMemory> > mBuffers;
- List<size_t> mIndicesAvailable;
-
- size_t mNumDeqeued;
-
- DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner)
- : mOwner(owner),
- mNumDeqeued(0) {
-}
-
-TunnelRenderer::StreamSource::~StreamSource() {
-}
-
-void TunnelRenderer::StreamSource::setListener(
- const sp<IStreamListener> &listener) {
- mListener = listener;
-}
-
-void TunnelRenderer::StreamSource::setBuffers(
- const Vector<sp<IMemory> > &buffers) {
- mBuffers = buffers;
-}
-
-void TunnelRenderer::StreamSource::onBufferAvailable(size_t index) {
- CHECK_LT(index, mBuffers.size());
-
- {
- Mutex::Autolock autoLock(mLock);
- mIndicesAvailable.push_back(index);
- }
-
- doSomeWork();
-}
-
-uint32_t TunnelRenderer::StreamSource::flags() const {
- return kFlagAlignedVideoData;
-}
-
-void TunnelRenderer::StreamSource::doSomeWork() {
- Mutex::Autolock autoLock(mLock);
-
- while (!mIndicesAvailable.empty()) {
- sp<ABuffer> srcBuffer = mOwner->dequeueBuffer();
- if (srcBuffer == NULL) {
- break;
- }
-
- ++mNumDeqeued;
-
- if (mNumDeqeued == 1) {
- ALOGI("fixing real time now.");
-
- sp<AMessage> extra = new AMessage;
-
- extra->setInt32(
- IStreamListener::kKeyDiscontinuityMask,
- ATSParser::DISCONTINUITY_ABSOLUTE_TIME);
-
- extra->setInt64("timeUs", ALooper::GetNowUs());
-
- mListener->issueCommand(
- IStreamListener::DISCONTINUITY,
- false /* synchronous */,
- extra);
- }
-
- ALOGV("dequeue TS packet of size %d", srcBuffer->size());
-
- size_t index = *mIndicesAvailable.begin();
- mIndicesAvailable.erase(mIndicesAvailable.begin());
-
- sp<IMemory> mem = mBuffers.itemAt(index);
- CHECK_LE(srcBuffer->size(), mem->size());
- CHECK_EQ((srcBuffer->size() % 188), 0u);
-
- memcpy(mem->pointer(), srcBuffer->data(), srcBuffer->size());
- mListener->queueBuffer(index, srcBuffer->size());
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TunnelRenderer::TunnelRenderer(
- const sp<AMessage> &notifyLost,
- const sp<ISurfaceTexture> &surfaceTex)
- : mNotifyLost(notifyLost),
- mSurfaceTex(surfaceTex),
- mTotalBytesQueued(0ll),
- mLastDequeuedExtSeqNo(-1),
- mFirstFailedAttemptUs(-1ll),
- mRequestedRetransmission(false) {
-}
-
-TunnelRenderer::~TunnelRenderer() {
- destroyPlayer();
-}
-
-void TunnelRenderer::queueBuffer(const sp<ABuffer> &buffer) {
- Mutex::Autolock autoLock(mLock);
-
- mTotalBytesQueued += buffer->size();
-
- if (mPackets.empty()) {
- mPackets.push_back(buffer);
- return;
- }
-
- int32_t newExtendedSeqNo = buffer->int32Data();
-
- List<sp<ABuffer> >::iterator firstIt = mPackets.begin();
- List<sp<ABuffer> >::iterator it = --mPackets.end();
- for (;;) {
- int32_t extendedSeqNo = (*it)->int32Data();
-
- if (extendedSeqNo == newExtendedSeqNo) {
- // Duplicate packet.
- return;
- }
-
- if (extendedSeqNo < newExtendedSeqNo) {
- // Insert new packet after the one at "it".
- mPackets.insert(++it, buffer);
- return;
- }
-
- if (it == firstIt) {
- // Insert new packet before the first existing one.
- mPackets.insert(it, buffer);
- return;
- }
-
- --it;
- }
-}
-
-sp<ABuffer> TunnelRenderer::dequeueBuffer() {
- Mutex::Autolock autoLock(mLock);
-
- sp<ABuffer> buffer;
- int32_t extSeqNo;
- while (!mPackets.empty()) {
- buffer = *mPackets.begin();
- extSeqNo = buffer->int32Data();
-
- if (mLastDequeuedExtSeqNo < 0 || extSeqNo > mLastDequeuedExtSeqNo) {
- break;
- }
-
- // This is a retransmission of a packet we've already returned.
-
- mTotalBytesQueued -= buffer->size();
- buffer.clear();
- extSeqNo = -1;
-
- mPackets.erase(mPackets.begin());
- }
-
- if (mPackets.empty()) {
- if (mFirstFailedAttemptUs < 0ll) {
- mFirstFailedAttemptUs = ALooper::GetNowUs();
- mRequestedRetransmission = false;
- } else {
- ALOGV("no packets available for %.2f secs",
- (ALooper::GetNowUs() - mFirstFailedAttemptUs) / 1E6);
- }
-
- return NULL;
- }
-
- if (mLastDequeuedExtSeqNo < 0 || extSeqNo == mLastDequeuedExtSeqNo + 1) {
- if (mRequestedRetransmission) {
- ALOGI("Recovered after requesting retransmission of %d",
- extSeqNo);
- }
-
- mLastDequeuedExtSeqNo = extSeqNo;
- mFirstFailedAttemptUs = -1ll;
- mRequestedRetransmission = false;
-
- mPackets.erase(mPackets.begin());
-
- mTotalBytesQueued -= buffer->size();
-
- return buffer;
- }
-
- if (mFirstFailedAttemptUs < 0ll) {
- mFirstFailedAttemptUs = ALooper::GetNowUs();
-
- ALOGI("failed to get the correct packet the first time.");
- return NULL;
- }
-
- if (mFirstFailedAttemptUs + 50000ll > ALooper::GetNowUs()) {
- // We're willing to wait a little while to get the right packet.
-
- if (!mRequestedRetransmission) {
- ALOGI("requesting retransmission of seqNo %d",
- (mLastDequeuedExtSeqNo + 1) & 0xffff);
-
- sp<AMessage> notify = mNotifyLost->dup();
- notify->setInt32("seqNo", (mLastDequeuedExtSeqNo + 1) & 0xffff);
- notify->post();
-
- mRequestedRetransmission = true;
- } else {
- ALOGI("still waiting for the correct packet to arrive.");
- }
-
- return NULL;
- }
-
- ALOGI("dropping packet. extSeqNo %d didn't arrive in time",
- mLastDequeuedExtSeqNo + 1);
-
- // Permanent failure, we never received the packet.
- mLastDequeuedExtSeqNo = extSeqNo;
- mFirstFailedAttemptUs = -1ll;
- mRequestedRetransmission = false;
-
- mTotalBytesQueued -= buffer->size();
-
- mPackets.erase(mPackets.begin());
-
- return buffer;
-}
-
-void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatQueueBuffer:
- {
- sp<ABuffer> buffer;
- CHECK(msg->findBuffer("buffer", &buffer));
-
- queueBuffer(buffer);
-
- if (mStreamSource == NULL) {
- if (mTotalBytesQueued > 0ll) {
- initPlayer();
- } else {
- ALOGI("Have %lld bytes queued...", mTotalBytesQueued);
- }
- } else {
- mStreamSource->doSomeWork();
- }
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void TunnelRenderer::initPlayer() {
- if (mSurfaceTex == NULL) {
- mComposerClient = new SurfaceComposerClient;
- CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
-
- DisplayInfo info;
- SurfaceComposerClient::getDisplayInfo(0, &info);
- ssize_t displayWidth = info.w;
- ssize_t displayHeight = info.h;
-
- mSurfaceControl =
- mComposerClient->createSurface(
- String8("A Surface"),
- displayWidth,
- displayHeight,
- PIXEL_FORMAT_RGB_565,
- 0);
-
- CHECK(mSurfaceControl != NULL);
- CHECK(mSurfaceControl->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(mSurfaceControl->setLayer(INT_MAX), (status_t)OK);
- CHECK_EQ(mSurfaceControl->show(), (status_t)OK);
- SurfaceComposerClient::closeGlobalTransaction();
-
- mSurface = mSurfaceControl->getSurface();
- CHECK(mSurface != NULL);
- }
-
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.player"));
- sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- CHECK(service.get() != NULL);
-
- mStreamSource = new StreamSource(this);
-
- mPlayerClient = new PlayerClient;
-
- mPlayer = service->create(getpid(), mPlayerClient, 0);
- CHECK(mPlayer != NULL);
- CHECK_EQ(mPlayer->setDataSource(mStreamSource), (status_t)OK);
-
- mPlayer->setVideoSurfaceTexture(
- mSurfaceTex != NULL ? mSurfaceTex : mSurface->getSurfaceTexture());
-
- mPlayer->start();
-}
-
-void TunnelRenderer::destroyPlayer() {
- mStreamSource.clear();
-
- mPlayer->stop();
- mPlayer.clear();
-
- if (mSurfaceTex == NULL) {
- mSurface.clear();
- mSurfaceControl.clear();
-
- mComposerClient->dispose();
- mComposerClient.clear();
- }
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/wifi-display/sink/TunnelRenderer.h b/media/libstagefright/wifi-display/sink/TunnelRenderer.h
deleted file mode 100644
index c9597e0..0000000
--- a/media/libstagefright/wifi-display/sink/TunnelRenderer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TUNNEL_RENDERER_H_
-
-#define TUNNEL_RENDERER_H_
-
-#include <gui/Surface.h>
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ABuffer;
-struct SurfaceComposerClient;
-struct SurfaceControl;
-struct Surface;
-struct IMediaPlayer;
-struct IStreamListener;
-
-// This class reassembles incoming RTP packets into the correct order
-// and sends the resulting transport stream to a mediaplayer instance
-// for playback.
-struct TunnelRenderer : public AHandler {
- TunnelRenderer(
- const sp<AMessage> &notifyLost,
- const sp<ISurfaceTexture> &surfaceTex);
-
- sp<ABuffer> dequeueBuffer();
-
- enum {
- kWhatQueueBuffer,
- };
-
-protected:
- virtual void onMessageReceived(const sp<AMessage> &msg);
- virtual ~TunnelRenderer();
-
-private:
- struct PlayerClient;
- struct StreamSource;
-
- mutable Mutex mLock;
-
- sp<AMessage> mNotifyLost;
- sp<ISurfaceTexture> mSurfaceTex;
-
- List<sp<ABuffer> > mPackets;
- int64_t mTotalBytesQueued;
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
- sp<Surface> mSurface;
- sp<PlayerClient> mPlayerClient;
- sp<IMediaPlayer> mPlayer;
- sp<StreamSource> mStreamSource;
-
- int32_t mLastDequeuedExtSeqNo;
- int64_t mFirstFailedAttemptUs;
- bool mRequestedRetransmission;
-
- void initPlayer();
- void destroyPlayer();
-
- void queueBuffer(const sp<ABuffer> &buffer);
-
- DISALLOW_EVIL_CONSTRUCTORS(TunnelRenderer);
-};
-
-} // namespace android
-
-#endif // TUNNEL_RENDERER_H_
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp b/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
deleted file mode 100644
index fcd20d4..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.cpp
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "WifiDisplaySink"
-#include <utils/Log.h>
-
-#include "WifiDisplaySink.h"
-#include "ParsedMessage.h"
-#include "RTPSink.h"
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaErrors.h>
-
-namespace android {
-
-WifiDisplaySink::WifiDisplaySink(
- const sp<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex)
- : mState(UNDEFINED),
- mNetSession(netSession),
- mSurfaceTex(surfaceTex),
- mSessionID(0),
- mNextCSeq(1) {
-}
-
-WifiDisplaySink::~WifiDisplaySink() {
-}
-
-void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("sourceHost", sourceHost);
- msg->setInt32("sourcePort", sourcePort);
- msg->post();
-}
-
-void WifiDisplaySink::start(const char *uri) {
- sp<AMessage> msg = new AMessage(kWhatStart, id());
- msg->setString("setupURI", uri);
- msg->post();
-}
-
-// static
-bool WifiDisplaySink::ParseURL(
- const char *url, AString *host, int32_t *port, AString *path,
- AString *user, AString *pass) {
- host->clear();
- *port = 0;
- path->clear();
- user->clear();
- pass->clear();
-
- if (strncasecmp("rtsp://", url, 7)) {
- return false;
- }
-
- const char *slashPos = strchr(&url[7], '/');
-
- if (slashPos == NULL) {
- host->setTo(&url[7]);
- path->setTo("/");
- } else {
- host->setTo(&url[7], slashPos - &url[7]);
- path->setTo(slashPos);
- }
-
- ssize_t atPos = host->find("@");
-
- if (atPos >= 0) {
- // Split of user:pass@ from hostname.
-
- AString userPass(*host, 0, atPos);
- host->erase(0, atPos + 1);
-
- ssize_t colonPos = userPass.find(":");
-
- if (colonPos < 0) {
- *user = userPass;
- } else {
- user->setTo(userPass, 0, colonPos);
- pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
- }
- }
-
- const char *colonPos = strchr(host->c_str(), ':');
-
- if (colonPos != NULL) {
- char *end;
- unsigned long x = strtoul(colonPos + 1, &end, 10);
-
- if (end == colonPos + 1 || *end != '\0' || x >= 65536) {
- return false;
- }
-
- *port = x;
-
- size_t colonOffset = colonPos - host->c_str();
- size_t trailing = host->size() - colonOffset;
- host->erase(colonOffset, trailing);
- } else {
- *port = 554;
- }
-
- return true;
-}
-
-void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatStart:
- {
- int32_t sourcePort;
-
- if (msg->findString("setupURI", &mSetupURI)) {
- AString path, user, pass;
- CHECK(ParseURL(
- mSetupURI.c_str(),
- &mRTSPHost, &sourcePort, &path, &user, &pass)
- && user.empty() && pass.empty());
- } else {
- CHECK(msg->findString("sourceHost", &mRTSPHost));
- CHECK(msg->findInt32("sourcePort", &sourcePort));
- }
-
- sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
-
- status_t err = mNetSession->createRTSPClient(
- mRTSPHost.c_str(), sourcePort, notify, &mSessionID);
- CHECK_EQ(err, (status_t)OK);
-
- mState = CONNECTING;
- break;
- }
-
- case kWhatRTSPNotify:
- {
- int32_t reason;
- CHECK(msg->findInt32("reason", &reason));
-
- switch (reason) {
- case ANetworkSession::kWhatError:
- {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- int32_t err;
- CHECK(msg->findInt32("err", &err));
-
- AString detail;
- CHECK(msg->findString("detail", &detail));
-
- ALOGE("An error occurred in session %d (%d, '%s/%s').",
- sessionID,
- err,
- detail.c_str(),
- strerror(-err));
-
- if (sessionID == mSessionID) {
- ALOGI("Lost control connection.");
-
- // The control connection is dead now.
- mNetSession->destroySession(mSessionID);
- mSessionID = 0;
-
- looper()->stop();
- }
- break;
- }
-
- case ANetworkSession::kWhatConnected:
- {
- ALOGI("We're now connected.");
- mState = CONNECTED;
-
- if (!mSetupURI.empty()) {
- status_t err =
- sendDescribe(mSessionID, mSetupURI.c_str());
-
- CHECK_EQ(err, (status_t)OK);
- }
- break;
- }
-
- case ANetworkSession::kWhatData:
- {
- onReceiveClientData(msg);
- break;
- }
-
- case ANetworkSession::kWhatBinaryData:
- {
- CHECK(sUseTCPInterleaving);
-
- int32_t channel;
- CHECK(msg->findInt32("channel", &channel));
-
- sp<ABuffer> data;
- CHECK(msg->findBuffer("data", &data));
-
- mRTPSink->injectPacket(channel == 0 /* isRTP */, data);
- break;
- }
-
- default:
- TRESPASS();
- }
- break;
- }
-
- case kWhatStop:
- {
- looper()->stop();
- break;
- }
-
- default:
- TRESPASS();
- }
-}
-
-void WifiDisplaySink::registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) {
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
- mResponseHandlers.add(id, func);
-}
-
-status_t WifiDisplaySink::sendM2(int32_t sessionID) {
- AString request = "OPTIONS * RTSP/1.0\r\n";
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(
- "Require: org.wfa.wfd1.0\r\n"
- "\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveM2Response(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- return OK;
-}
-
-status_t WifiDisplaySink::onReceiveDescribeResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- return sendSetup(sessionID, mSetupURI.c_str());
-}
-
-status_t WifiDisplaySink::onReceiveSetupResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- if (!msg->findString("session", &mPlaybackSessionID)) {
- return ERROR_MALFORMED;
- }
-
- if (!ParsedMessage::GetInt32Attribute(
- mPlaybackSessionID.c_str(),
- "timeout",
- &mPlaybackSessionTimeoutSecs)) {
- mPlaybackSessionTimeoutSecs = -1;
- }
-
- ssize_t colonPos = mPlaybackSessionID.find(";");
- if (colonPos >= 0) {
- // Strip any options from the returned session id.
- mPlaybackSessionID.erase(
- colonPos, mPlaybackSessionID.size() - colonPos);
- }
-
- status_t err = configureTransport(msg);
-
- if (err != OK) {
- return err;
- }
-
- mState = PAUSED;
-
- return sendPlay(
- sessionID,
- !mSetupURI.empty()
- ? mSetupURI.c_str() : "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-}
-
-status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg) {
- if (sUseTCPInterleaving) {
- return OK;
- }
-
- AString transport;
- if (!msg->findString("transport", &transport)) {
- ALOGE("Missing 'transport' field in SETUP response.");
- return ERROR_MALFORMED;
- }
-
- AString sourceHost;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "source", &sourceHost)) {
- sourceHost = mRTSPHost;
- }
-
- AString serverPortStr;
- if (!ParsedMessage::GetAttribute(
- transport.c_str(), "server_port", &serverPortStr)) {
- ALOGE("Missing 'server_port' in Transport field.");
- return ERROR_MALFORMED;
- }
-
- int rtpPort, rtcpPort;
- if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
- || rtpPort <= 0 || rtpPort > 65535
- || rtcpPort <=0 || rtcpPort > 65535
- || rtcpPort != rtpPort + 1) {
- ALOGE("Invalid server_port description '%s'.",
- serverPortStr.c_str());
-
- return ERROR_MALFORMED;
- }
-
- if (rtpPort & 1) {
- ALOGW("Server picked an odd numbered RTP port.");
- }
-
- return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
-}
-
-status_t WifiDisplaySink::onReceivePlayResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg) {
- int32_t statusCode;
- if (!msg->getStatusCode(&statusCode)) {
- return ERROR_MALFORMED;
- }
-
- if (statusCode != 200) {
- return ERROR_UNSUPPORTED;
- }
-
- mState = PLAYING;
-
- return OK;
-}
-
-void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {
- int32_t sessionID;
- CHECK(msg->findInt32("sessionID", &sessionID));
-
- sp<RefBase> obj;
- CHECK(msg->findObject("data", &obj));
-
- sp<ParsedMessage> data =
- static_cast<ParsedMessage *>(obj.get());
-
- ALOGV("session %d received '%s'",
- sessionID, data->debugString().c_str());
-
- AString method;
- AString uri;
- data->getRequestField(0, &method);
-
- int32_t cseq;
- if (!data->findInt32("cseq", &cseq)) {
- sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
- return;
- }
-
- if (method.startsWith("RTSP/")) {
- // This is a response.
-
- ResponseID id;
- id.mSessionID = sessionID;
- id.mCSeq = cseq;
-
- ssize_t index = mResponseHandlers.indexOfKey(id);
-
- if (index < 0) {
- ALOGW("Received unsolicited server response, cseq %d", cseq);
- return;
- }
-
- HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
- mResponseHandlers.removeItemsAt(index);
-
- status_t err = (this->*func)(sessionID, data);
- CHECK_EQ(err, (status_t)OK);
- } else {
- AString version;
- data->getRequestField(2, &version);
- if (!(version == AString("RTSP/1.0"))) {
- sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
- return;
- }
-
- if (method == "OPTIONS") {
- onOptionsRequest(sessionID, cseq, data);
- } else if (method == "GET_PARAMETER") {
- onGetParameterRequest(sessionID, cseq, data);
- } else if (method == "SET_PARAMETER") {
- onSetParameterRequest(sessionID, cseq, data);
- } else {
- sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
- }
- }
-}
-
-void WifiDisplaySink::onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n");
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-
- err = sendM2(sessionID);
- CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySink::onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- AString body =
- "wfd_video_formats: xxx\r\n"
- "wfd_audio_codecs: xxx\r\n"
- "wfd_client_rtp_ports: RTP/AVP/UDP;unicast xxx 0 mode=play\r\n";
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("Content-Type: text/parameters\r\n");
- response.append(StringPrintf("Content-Length: %d\r\n", body.size()));
- response.append("\r\n");
- response.append(body);
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri) {
- uri = "rtsp://xwgntvx.is.livestream-api.com/livestreamiphone/wgntv";
- uri = "rtsp://v2.cache6.c.youtube.com/video.3gp?cid=e101d4bf280055f9&fmt=18";
-
- AString request = StringPrintf("DESCRIBE %s RTSP/1.0\r\n", uri);
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append("Accept: application/sdp\r\n");
- request.append("\r\n");
-
- status_t err = mNetSession->sendRequest(
- sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveDescribeResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
- mRTPSink = new RTPSink(mNetSession, mSurfaceTex);
- looper()->registerHandler(mRTPSink);
-
- status_t err = mRTPSink->init(sUseTCPInterleaving);
-
- if (err != OK) {
- looper()->unregisterHandler(mRTPSink->id());
- mRTPSink.clear();
- return err;
- }
-
- AString request = StringPrintf("SETUP %s RTSP/1.0\r\n", uri);
-
- AppendCommonResponse(&request, mNextCSeq);
-
- if (sUseTCPInterleaving) {
- request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
- } else {
- int32_t rtpPort = mRTPSink->getRTPPort();
-
- request.append(
- StringPrintf(
- "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
- rtpPort, rtpPort + 1));
- }
-
- request.append("\r\n");
-
- ALOGV("request = '%s'", request.c_str());
-
- err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) {
- AString request = StringPrintf("PLAY %s RTSP/1.0\r\n", uri);
-
- AppendCommonResponse(&request, mNextCSeq);
-
- request.append(StringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
- request.append("\r\n");
-
- status_t err =
- mNetSession->sendRequest(sessionID, request.c_str(), request.size());
-
- if (err != OK) {
- return err;
- }
-
- registerResponseHandler(
- sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);
-
- ++mNextCSeq;
-
- return OK;
-}
-
-void WifiDisplaySink::onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data) {
- const char *content = data->getContent();
-
- if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {
- status_t err =
- sendSetup(
- sessionID,
- "rtsp://x.x.x.x:x/wfd1.0/streamid=0");
-
- CHECK_EQ(err, (status_t)OK);
- }
-
- AString response = "RTSP/1.0 200 OK\r\n";
- AppendCommonResponse(&response, cseq);
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySink::sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq) {
- AString response;
- response.append("RTSP/1.0 ");
- response.append(errorDetail);
- response.append("\r\n");
-
- AppendCommonResponse(&response, cseq);
-
- response.append("\r\n");
-
- status_t err = mNetSession->sendRequest(sessionID, response.c_str());
- CHECK_EQ(err, (status_t)OK);
-}
-
-// static
-void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) {
- time_t now = time(NULL);
- struct tm *now2 = gmtime(&now);
- char buf[128];
- strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2);
-
- response->append("Date: ");
- response->append(buf);
- response->append("\r\n");
-
- response->append("User-Agent: stagefright/1.1 (Linux;Android 4.1)\r\n");
-
- if (cseq >= 0) {
- response->append(StringPrintf("CSeq: %d\r\n", cseq));
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h b/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
deleted file mode 100644
index f886ee5..0000000
--- a/media/libstagefright/wifi-display/sink/WifiDisplaySink.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef WIFI_DISPLAY_SINK_H_
-
-#define WIFI_DISPLAY_SINK_H_
-
-#include "ANetworkSession.h"
-
-#include <gui/Surface.h>
-#include <media/stagefright/foundation/AHandler.h>
-
-namespace android {
-
-struct ParsedMessage;
-struct RTPSink;
-
-// Represents the RTSP client acting as a wifi display sink.
-// Connects to a wifi display source and renders the incoming
-// transport stream using a MediaPlayer instance.
-struct WifiDisplaySink : public AHandler {
- WifiDisplaySink(
- const sp<ANetworkSession> &netSession,
- const sp<ISurfaceTexture> &surfaceTex = NULL);
-
- void start(const char *sourceHost, int32_t sourcePort);
- void start(const char *uri);
-
-protected:
- virtual ~WifiDisplaySink();
- virtual void onMessageReceived(const sp<AMessage> &msg);
-
-private:
- enum State {
- UNDEFINED,
- CONNECTING,
- CONNECTED,
- PAUSED,
- PLAYING,
- };
-
- enum {
- kWhatStart,
- kWhatRTSPNotify,
- kWhatStop,
- };
-
- struct ResponseID {
- int32_t mSessionID;
- int32_t mCSeq;
-
- bool operator<(const ResponseID &other) const {
- return mSessionID < other.mSessionID
- || (mSessionID == other.mSessionID
- && mCSeq < other.mCSeq);
- }
- };
-
- typedef status_t (WifiDisplaySink::*HandleRTSPResponseFunc)(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- static const bool sUseTCPInterleaving = false;
-
- State mState;
- sp<ANetworkSession> mNetSession;
- sp<ISurfaceTexture> mSurfaceTex;
- AString mSetupURI;
- AString mRTSPHost;
- int32_t mSessionID;
-
- int32_t mNextCSeq;
-
- KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
-
- sp<RTPSink> mRTPSink;
- AString mPlaybackSessionID;
- int32_t mPlaybackSessionTimeoutSecs;
-
- status_t sendM2(int32_t sessionID);
- status_t sendDescribe(int32_t sessionID, const char *uri);
- status_t sendSetup(int32_t sessionID, const char *uri);
- status_t sendPlay(int32_t sessionID, const char *uri);
-
- status_t onReceiveM2Response(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveDescribeResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t onReceiveSetupResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- status_t configureTransport(const sp<ParsedMessage> &msg);
-
- status_t onReceivePlayResponse(
- int32_t sessionID, const sp<ParsedMessage> &msg);
-
- void registerResponseHandler(
- int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
-
- void onReceiveClientData(const sp<AMessage> &msg);
-
- void onOptionsRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void onGetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void onSetParameterRequest(
- int32_t sessionID,
- int32_t cseq,
- const sp<ParsedMessage> &data);
-
- void sendErrorResponse(
- int32_t sessionID,
- const char *errorDetail,
- int32_t cseq);
-
- static void AppendCommonResponse(AString *response, int32_t cseq);
-
- bool ParseURL(
- const char *url, AString *host, int32_t *port, AString *path,
- AString *user, AString *pass);
-
- DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySink);
-};
-
-} // namespace android
-
-#endif // WIFI_DISPLAY_SINK_H_
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 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> &notify, const sp<AMessage> &format);
+
void setRepeaterSource(const sp<RepeaterSource> &source);
sp<AMessage> getFormat();
bool isAudio() const;
const sp<Converter> &converter() const;
- ssize_t packetizerTrackIndex() const;
+ const sp<RepeaterSource> &repeaterSource() const;
- void setPacketizerTrackIndex(size_t index);
+ ssize_t mediaSenderTrackIndex() const;
+ void setMediaSenderTrackIndex(size_t index);
status_t start();
void stopAsync();
@@ -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> &notify, 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> &notify,
const in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp)
+ const sp<IHDCP> &hdcp,
+ const char *path)
: mNetSession(netSession),
mNotify(notify),
mInterfaceAddr(interfaceAddr),
mHDCP(hdcp),
+ mLocalRTPPort(-1),
mWeAreDead(false),
mPaused(false),
mLastLifesignUs(),
mVideoTrackIndex(-1),
mPrevTimeUs(-1ll),
- mAllTracksHavePacketizerIndex(false) {
+ mPullExtractorPending(false),
+ mPullExtractorGeneration(0),
+ mFirstSampleTimeRealUs(-1ll),
+ mFirstSampleTimeUs(-1ll) {
+ if (path != NULL) {
+ mMediaPath.setTo(path);
+ }
}
status_t WifiDisplaySource::PlaybackSession::init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- Sender::TransportMode transportMode,
- bool usePCMAudio) {
- status_t err = setupPacketizer(usePCMAudio);
+ const char *clientIP,
+ int32_t clientRtp,
+ RTPSender::TransportMode rtpMode,
+ int32_t clientRtcp,
+ RTPSender::TransportMode rtcpMode,
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex) {
+ 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> &notify,
const struct in_addr &interfaceAddr,
- const sp<IHDCP> &hdcp);
+ const sp<IHDCP> &hdcp,
+ const char *path = NULL);
status_t init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- Sender::TransportMode transportMode,
- bool usePCMAudio);
+ const char *clientIP,
+ int32_t clientRtp,
+ RTPSender::TransportMode rtpMode,
+ int32_t clientRtcp,
+ RTPSender::TransportMode rtcpMode,
+ bool enableAudio,
+ bool usePCMAudio,
+ bool enableVideo,
+ VideoFormats::ResolutionType videoResolutionType,
+ size_t videoResolutionIndex);
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> &notify)
- : 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> &notify);
-
- enum {
- kWhatInitDone,
- kWhatSessionDead,
- kWhatBinaryData,
- };
-
- enum TransportMode {
- TRANSPORT_UDP,
- TRANSPORT_TCP_INTERLEAVED,
- TRANSPORT_TCP,
- };
- status_t init(
- const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
- TransportMode transportMode);
-
- status_t finishInit();
-
- int32_t getRTPPort() const;
-
- void queuePackets(int64_t timeUs, const sp<ABuffer> &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;
}