summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2012-11-12 13:08:44 -0800
committerAndreas Huber <andih@google.com>2012-11-13 13:21:36 -0800
commit0224bf170a3904576bba81593eaab113c5d3a4e7 (patch)
tree36db389aee1c43d3225d07ef4bdbcfdb8e560022
parent78b012f5bc05706d1ce412811ccfaa9385dae0ab (diff)
downloadframeworks_av-0224bf170a3904576bba81593eaab113c5d3a4e7.zip
frameworks_av-0224bf170a3904576bba81593eaab113c5d3a4e7.tar.gz
frameworks_av-0224bf170a3904576bba81593eaab113c5d3a4e7.tar.bz2
Various improvements of wifi display code
- manually prepend SPS/PPS if encoder doesn't support it - latency improvements - support for "our" method of optional RTP retransmission - improvements to the wfd commandline tool for testing - make it easier to turn on/off suspension of the video pipeline on idle - fixes an issue where an error during encryption would cause a SEGV - add HDCP descriptor if necessary Squashed commit of the following: commit 1115be0ebb3b885b4f1b7dba56761ca013d0ec4a Author: Andreas Huber <andih@google.com> Date: Fri Nov 9 11:32:23 2012 -0800 Better shutdown of wfd -l sessions. Change-Id: Id898a14ae21efd3b065b00a729830063d39195a7 commit 0e7d106dfe4eb6e2640b0b66c65deaba265f7ff0 Author: Andreas Huber <andih@google.com> Date: Thu Nov 8 16:38:55 2012 -0800 No more sending delay, create rtp packets upfront. Change-Id: I809a225f664fdb485c7d9a49a27886601a6a26b2 commit d399e8571b77353d59afb57508dfd2a82c1ef93a Author: Andreas Huber <andih@google.com> Date: Thu Nov 8 14:19:43 2012 -0800 Restore AudioSource buffer size, factor out TimeSeries, make suspending video optional. Change-Id: Ifdfe4d447b901e714abf52856b4641d1d55a5d41 commit f8b649f0b8f917d59f4b8a2e8e6d7db61a684a78 Author: Andreas Huber <andih@google.com> Date: Thu Nov 8 09:34:06 2012 -0800 Pull 480 frames at a time from AudioSource/AudioRecord Change-Id: I1e215abd329faec3da026631122c0f4c800c0ac4 commit 1bc13452eb35eebbba00f5da93fa86535be5db59 Author: Andreas Huber <andih@google.com> Date: Thu Nov 8 08:50:30 2012 -0800 fixed bitrate traffic simulation Change-Id: Ic5efb7cbb0b5d3b4917bc77b8ba73d447249e695 commit 016cdff18e74bdc631a5679e97192645ed095aa2 Author: Andreas Huber <andih@google.com> Date: Wed Nov 7 14:00:03 2012 -0800 resurrected "our" style of retransmission. Change-Id: I34d757aba67428437cb39b8293a9651750ad20d9 commit 384cf1a3c8fb4ec410bdf8fa5722c298e6028f3e Author: Andreas Huber <andih@google.com> Date: Tue Nov 6 09:38:55 2012 -0800 Changes to make wfd work on manta. Change-Id: I7a4e00cf16581fe2146edd1b359af195774090e4 commit 9628f24b22b35f28630d99dda3614babf51bc07e Author: Andreas Huber <andih@google.com> Date: Wed Nov 7 09:15:44 2012 -0800 Patch up rtp timestamps to more accurately measure network jitter. Change-Id: I9502a4615575f97f98a215a13131a89a6ae93c6d commit 7c891a1a24f08bbd50f55be13f7d05f43e807eb8 Author: Andreas Huber <andih@google.com> Date: Tue Nov 6 09:37:24 2012 -0800 Additions to the "wfd" tool to create a local wfd source. Change-Id: I99558653a70fdc703f9d13990b3ce1c4d3ae227a Change-Id: Ia94c63fc390f597014531073485f0cfc53b3418a
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.cpp21
-rw-r--r--media/libstagefright/wifi-display/Android.mk1
-rw-r--r--media/libstagefright/wifi-display/TimeSeries.cpp67
-rw-r--r--media/libstagefright/wifi-display/TimeSeries.h46
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp46
-rw-r--r--media/libstagefright/wifi-display/source/Converter.h2
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp68
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.h2
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.cpp5
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.h2
-rw-r--r--media/libstagefright/wifi-display/source/Sender.cpp415
-rw-r--r--media/libstagefright/wifi-display/source/Sender.h43
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.cpp19
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp12
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h2
-rw-r--r--media/libstagefright/wifi-display/wfd.cpp184
16 files changed, 633 insertions, 302 deletions
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 819cd62..62a6e7f 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -407,6 +407,24 @@ status_t ANetworkSession::Session::writeMore() {
do {
const sp<ABuffer> &datagram = *mOutDatagrams.begin();
+ uint8_t *data = datagram->data();
+ if (data[0] == 0x80 && (data[1] & 0x7f) == 33) {
+ int64_t nowUs = ALooper::GetNowUs();
+
+ uint32_t prevRtpTime = U32_AT(&data[4]);
+
+ // 90kHz time scale
+ uint32_t rtpTime = (nowUs * 9ll) / 100ll;
+ int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime;
+
+ ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0);
+
+ data[4] = rtpTime >> 24;
+ data[5] = (rtpTime >> 16) & 0xff;
+ data[6] = (rtpTime >> 8) & 0xff;
+ data[7] = rtpTime & 0xff;
+ }
+
int n;
do {
n = send(mSocket, datagram->data(), datagram->size(), 0);
@@ -424,6 +442,9 @@ status_t ANetworkSession::Session::writeMore() {
} while (err == OK && !mOutDatagrams.empty());
if (err == -EAGAIN) {
+ if (!mOutDatagrams.empty()) {
+ ALOGI("%d datagrams remain queued.", mOutDatagrams.size());
+ }
err = OK;
}
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 611bfff..75098f1 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
source/Sender.cpp \
source/TSPacketizer.cpp \
source/WifiDisplaySource.cpp \
+ TimeSeries.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/media/libstagefright \
diff --git a/media/libstagefright/wifi-display/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp
new file mode 100644
index 0000000..d882d98
--- /dev/null
+++ b/media/libstagefright/wifi-display/TimeSeries.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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/TimeSeries.h b/media/libstagefright/wifi-display/TimeSeries.h
new file mode 100644
index 0000000..c818d51
--- /dev/null
+++ b/media/libstagefright/wifi-display/TimeSeries.h
@@ -0,0 +1,46 @@
+/*
+ * 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 TIME_SERIES_H_
+
+#define TIME_SERIES_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+struct TimeSeries {
+ TimeSeries();
+
+ void add(double val);
+
+ double mean() const;
+ double sdev() const;
+
+private:
+ enum {
+ kHistorySize = 20
+ };
+ double mValues[kHistorySize];
+
+ size_t mCount;
+ double mSum;
+};
+
+} // namespace android
+
+#endif // TIME_SERIES_H_
+
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 01a394f..82c98b9 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -48,6 +48,7 @@ Converter::Converter(
mInputFormat(format),
mIsVideo(false),
mIsPCMAudio(usePCMAudio),
+ mNeedToManuallyPrependSPSPPS(false),
mDoMoreWorkPending(false)
#if ENABLE_SILENCE_DETECTION
,mFirstSilentFrameUs(-1ll)
@@ -94,6 +95,10 @@ sp<AMessage> Converter::getOutputFormat() const {
return mOutputFormat;
}
+bool Converter::needToManuallyPrependSPSPPS() const {
+ return mNeedToManuallyPrependSPSPPS;
+}
+
static int32_t getBitrate(const char *propName, int32_t defaultValue) {
char val[PROPERTY_VALUE_MAX];
if (property_get(propName, val, NULL)) {
@@ -157,16 +162,45 @@ status_t Converter::initEncoder() {
mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant);
mOutputFormat->setInt32("frame-rate", 30);
mOutputFormat->setInt32("i-frame-interval", 1); // Iframes every 1 secs
- mOutputFormat->setInt32("prepend-sps-pps-to-idr-frames", 1);
}
ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
- status_t err = mEncoder->configure(
- mOutputFormat,
- NULL /* nativeWindow */,
- NULL /* crypto */,
- MediaCodec::CONFIGURE_FLAG_ENCODE);
+ mNeedToManuallyPrependSPSPPS = false;
+
+ status_t err = NO_INIT;
+
+ if (!isAudio) {
+ sp<AMessage> tmp = mOutputFormat->dup();
+ tmp->setInt32("prepend-sps-pps-to-idr-frames", 1);
+
+ err = mEncoder->configure(
+ tmp,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+
+ if (err == OK) {
+ // Encoder supported prepending SPS/PPS, we don't need to emulate
+ // it.
+ mOutputFormat = tmp;
+ } else {
+ mNeedToManuallyPrependSPSPPS = true;
+
+ ALOGI("We going to manually prepend SPS and PPS to IDR frames.");
+ }
+ }
+
+ if (err != OK) {
+ // We'll get here for audio or if we failed to configure the encoder
+ // to automatically prepend SPS/PPS in the case of video.
+
+ err = mEncoder->configure(
+ mOutputFormat,
+ NULL /* nativeWindow */,
+ NULL /* crypto */,
+ MediaCodec::CONFIGURE_FLAG_ENCODE);
+ }
if (err != OK) {
return err;
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 2cdeda3..0665eea 100644
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ b/media/libstagefright/wifi-display/source/Converter.h
@@ -44,6 +44,7 @@ struct Converter : public AHandler {
size_t getInputBufferCount() const;
sp<AMessage> getOutputFormat() const;
+ bool needToManuallyPrependSPSPPS() const;
void feedAccessUnit(const sp<ABuffer> &accessUnit);
void signalEOS();
@@ -78,6 +79,7 @@ private:
bool mIsVideo;
bool mIsPCMAudio;
sp<AMessage> mOutputFormat;
+ bool mNeedToManuallyPrependSPSPPS;
sp<MediaCodec> mEncoder;
sp<AMessage> mEncoderActivityNotify;
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index f1e7140..4e5eb52 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -26,6 +26,7 @@
#include "Sender.h"
#include "TSPacketizer.h"
#include "include/avc_utils.h"
+#include "WifiDisplaySource.h"
#include <binder/IServiceManager.h>
#include <gui/ISurfaceComposer.h>
@@ -81,7 +82,10 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler {
bool hasOutputBuffer(int64_t *timeUs) const;
void queueOutputBuffer(const sp<ABuffer> &accessUnit);
sp<ABuffer> dequeueOutputBuffer();
+
+#if SUSPEND_VIDEO_IF_IDLE
bool isSuspended() const;
+#endif
size_t countQueuedOutputBuffers() const {
return mQueuedOutputBuffers.size();
@@ -279,7 +283,6 @@ bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer(
void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer(
const sp<ABuffer> &accessUnit) {
mQueuedOutputBuffers.push_back(accessUnit);
-
mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs();
}
@@ -292,6 +295,7 @@ sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() {
return outputBuffer;
}
+#if SUSPEND_VIDEO_IF_IDLE
bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
if (!mQueuedOutputBuffers.empty()) {
return false;
@@ -307,6 +311,7 @@ bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const {
// this track suspended for the time being.
return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll;
}
+#endif
////////////////////////////////////////////////////////////////////////////////
@@ -443,8 +448,13 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived(
ssize_t packetizerTrackIndex = track->packetizerTrackIndex();
if (packetizerTrackIndex < 0) {
- packetizerTrackIndex =
- mPacketizer->addTrack(track->getFormat());
+ 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);
@@ -642,8 +652,10 @@ status_t WifiDisplaySource::PlaybackSession::addSource(
sp<Converter> converter =
new Converter(notify, codecLooper, format, usePCMAudio);
- if (converter->initCheck() != OK) {
- return converter->initCheck();
+ err = converter->initCheck();
+ if (err != OK) {
+ ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err);
+ return err;
}
looper()->registerHandler(converter);
@@ -735,11 +747,19 @@ sp<ISurfaceTexture> WifiDisplaySource::PlaybackSession::getSurfaceTexture() {
}
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() {
@@ -767,7 +787,7 @@ bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() {
}
status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit,
+ size_t trackIndex, sp<ABuffer> accessUnit,
sp<ABuffer> *packets) {
const sp<Track> &track = mTracks.valueFor(trackIndex);
@@ -776,9 +796,20 @@ status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
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 */,
@@ -858,6 +889,8 @@ status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
#endif
flags |= TSPacketizer::IS_ENCRYPTED;
+ } else if (manuallyPrependSPSPPS) {
+ flags |= TSPacketizer::PREPEND_SPS_PPS_TO_IDR_FRAMES;
}
int64_t timeUs = ALooper::GetNowUs();
@@ -930,12 +963,21 @@ bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
minTrackIndex = mTracks.keyAt(i);
minTimeUs = timeUs;
}
- } else if (!track->isSuspended()) {
+ }
+#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) {
@@ -950,6 +992,7 @@ bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
if (err != OK) {
notifySessionDead();
+ return false;
}
if ((ssize_t)minTrackIndex == mVideoTrackIndex) {
@@ -957,6 +1000,17 @@ bool WifiDisplaySource::PlaybackSession::drainAccessUnit() {
}
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;
}
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index cc8b244..dabc1c4 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -127,7 +127,7 @@ private:
bool allTracksHavePacketizerIndex();
status_t packetizeAccessUnit(
- size_t trackIndex, const sp<ABuffer> &accessUnit,
+ size_t trackIndex, sp<ABuffer> accessUnit,
sp<ABuffer> *packets);
status_t packetizeQueuedAccessUnits();
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
index 641e63f..72be927 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp
@@ -125,11 +125,14 @@ status_t RepeaterSource::read(
return mResult;
}
+#if SUSPEND_VIDEO_IF_IDLE
int64_t nowUs = ALooper::GetNowUs();
if (nowUs - mLastBufferUpdateUs > 1000000ll) {
mLastBufferUpdateUs = -1ll;
stale = true;
- } else {
+ } else
+#endif
+ {
mBuffer->add_ref();
*buffer = mBuffer;
(*buffer)->meta_data()->setInt64(kKeyTime, bufferTimeUs);
diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h
index e4aa2b6..a13973c 100644
--- a/media/libstagefright/wifi-display/source/RepeaterSource.h
+++ b/media/libstagefright/wifi-display/source/RepeaterSource.h
@@ -6,6 +6,8 @@
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/MediaSource.h>
+#define SUSPEND_VIDEO_IF_IDLE 1
+
namespace android {
// This MediaSource delivers frames at a constant rate by repeating buffers
diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp
index ea12424..9048691 100644
--- a/media/libstagefright/wifi-display/source/Sender.cpp
+++ b/media/libstagefright/wifi-display/source/Sender.cpp
@@ -21,6 +21,7 @@
#include "Sender.h"
#include "ANetworkSession.h"
+#include "TimeSeries.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -29,79 +30,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
-#include <math.h>
-
-#define DEBUG_JITTER 0
-
namespace android {
-////////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_JITTER
-struct TimeSeries {
- TimeSeries();
-
- void add(double val);
-
- double mean() const;
- double sdev() const;
-
-private:
- enum {
- kHistorySize = 20
- };
- double mValues[kHistorySize];
-
- size_t mCount;
- double mSum;
-};
-
-TimeSeries::TimeSeries()
- : mCount(0),
- mSum(0.0) {
-}
-
-void TimeSeries::add(double val) {
- if (mCount < kHistorySize) {
- mValues[mCount++] = val;
- mSum += val;
- } else {
- mSum -= mValues[0];
- memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double));
- mValues[kHistorySize - 1] = val;
- mSum += val;
- }
-}
-
-double TimeSeries::mean() const {
- if (mCount < 1) {
- return 0.0;
- }
-
- return mSum / mCount;
-}
-
-double TimeSeries::sdev() const {
- if (mCount < 1) {
- return 0.0;
- }
-
- double m = mean();
-
- double sum = 0.0;
- for (size_t i = 0; i < mCount; ++i) {
- double tmp = mValues[i] - m;
- tmp *= tmp;
-
- sum += tmp;
- }
-
- return sqrt(sum / mCount);
-}
-#endif // DEBUG_JITTER
-
-////////////////////////////////////////////////////////////////////////////////
-
static size_t kMaxRTPPacketSize = 1500;
static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188;
@@ -110,14 +40,13 @@ Sender::Sender(
const sp<AMessage> &notify)
: mNetSession(netSession),
mNotify(notify),
- mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
mTransportMode(TRANSPORT_UDP),
mRTPChannel(0),
mRTCPChannel(0),
mRTPPort(0),
mRTPSessionID(0),
mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
mRTPRetransmissionSessionID(0),
mRTCPRetransmissionSessionID(0),
#endif
@@ -128,7 +57,7 @@ Sender::Sender(
mFirstOutputBufferReadyTimeUs(-1ll),
mFirstOutputBufferSentTimeUs(-1ll),
mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
mRTPRetransmissionSeqNo(0),
#endif
mLastNTPTime(0),
@@ -148,15 +77,13 @@ Sender::Sender(
,mLogFile(NULL)
#endif
{
- mTSQueue->setRange(0, 12);
-
#if LOG_TRANSPORT_STREAM
mLogFile = fopen("/system/etc/log.ts", "wb");
#endif
}
Sender::~Sender() {
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
if (mRTCPRetransmissionSessionID != 0) {
mNetSession->destroySession(mRTCPRetransmissionSessionID);
}
@@ -217,7 +144,7 @@ status_t Sender::init(
sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
sp<AMessage> rtpRetransmissionNotify =
new AMessage(kWhatRTPRetransmissionNotify, id());
@@ -264,7 +191,7 @@ status_t Sender::init(
}
}
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
if (mTransportMode == TRANSPORT_UDP) {
int32_t rtpRetransmissionSession;
@@ -358,44 +285,67 @@ int32_t Sender::getRTPPort() const {
}
void Sender::queuePackets(
- int64_t timeUs, const sp<ABuffer> &packets) {
- bool isVideo = false;
+ 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);
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
+ dstOffset += 188;
}
- int64_t delayUs;
- int64_t whenUs;
+ udpPackets->setRange(0, dstOffset);
- if (mFirstOutputBufferReadyTimeUs < 0ll) {
- mFirstOutputBufferReadyTimeUs = timeUs;
- mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs();
- delayUs = 0ll;
- } else {
- int64_t nowUs = ALooper::GetNowUs();
-
- whenUs = (timeUs - mFirstOutputBufferReadyTimeUs)
- + mFirstOutputBufferSentTimeUs;
+ sp<AMessage> msg = new AMessage(kWhatDrainQueue, id());
+ msg->setBuffer("udpPackets", udpPackets);
+ msg->post();
- delayUs = whenUs - nowUs;
+#if LOG_TRANSPORT_STREAM
+ if (mLogFile != NULL) {
+ fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
}
-
- sp<AMessage> msg = new AMessage(kWhatQueuePackets, id());
- msg->setBuffer("packets", packets);
-
- packets->meta()->setInt64("timeUs", timeUs);
- packets->meta()->setInt64("whenUs", whenUs);
- packets->meta()->setInt64("delayUs", delayUs);
- msg->post(delayUs > 0 ? delayUs : 0);
+#endif
}
void Sender::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatRTPNotify:
case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
case kWhatRTPRetransmissionNotify:
case kWhatRTCPRetransmissionNotify:
#endif
@@ -419,7 +369,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findString("detail", &detail));
if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
|| msg->what() == kWhatRTPRetransmissionNotify
#endif
) && !errorOccuredDuringSend) {
@@ -443,7 +393,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
} else if (sessionID == mRTCPSessionID) {
mRTCPSessionID = 0;
}
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
else if (sessionID == mRTPRetransmissionSessionID) {
mRTPRetransmissionSessionID = 0;
} else if (sessionID == mRTCPRetransmissionSessionID) {
@@ -465,7 +415,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
status_t err;
if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
|| msg->what() == kWhatRTCPRetransmissionNotify
#endif
)
@@ -507,12 +457,12 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case kWhatQueuePackets:
+ case kWhatDrainQueue:
{
- sp<ABuffer> packets;
- CHECK(msg->findBuffer("packets", &packets));
+ sp<ABuffer> udpPackets;
+ CHECK(msg->findBuffer("udpPackets", &udpPackets));
- onQueuePackets(packets);
+ onDrainQueue(udpPackets);
break;
}
@@ -532,156 +482,6 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
}
}
-void Sender::onQueuePackets(const sp<ABuffer> &packets) {
-#if DEBUG_JITTER
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- static int64_t lastTimeUs = 0ll;
- int64_t nowUs = ALooper::GetNowUs();
-
- static TimeSeries series;
- series.add((double)(nowUs - lastTimeUs));
-
- ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f",
- nowUs - lastTimeUs, series.mean(), series.sdev());
-
- lastTimeUs = nowUs;
- }
-#endif
-
- int64_t startTimeUs = ALooper::GetNowUs();
-
- for (size_t offset = 0;
- offset < packets->size(); offset += 188) {
- bool lastTSPacket = (offset + 188 >= packets->size());
-
- appendTSData(
- packets->data() + offset,
- 188,
- true /* timeDiscontinuity */,
- lastTSPacket /* flush */);
- }
-
-#if 0
- int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs;
-
- int64_t whenUs;
- CHECK(packets->meta()->findInt64("whenUs", &whenUs));
-
- int64_t delayUs;
- CHECK(packets->meta()->findInt64("delayUs", &delayUs));
-
- bool isVideo = false;
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- if (nowUs - whenUs > 2000) {
- ALOGI("[%s] delayUs = %lld us, delta = %lld us",
- isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs);
- }
-#endif
-
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fwrite(packets->data(), 1, packets->size(), mLogFile);
- }
-#endif
-}
-
-ssize_t Sender::appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush) {
- CHECK_EQ(size, 188);
-
- CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
-
- memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
- mTSQueue->setRange(0, mTSQueue->size() + size);
-
- if (flush || mTSQueue->size() == mTSQueue->capacity()) {
- // flush
-
- int64_t nowUs = ALooper::GetNowUs();
-
-#if TRACK_BANDWIDTH
- if (mFirstPacketTimeUs < 0ll) {
- mFirstPacketTimeUs = nowUs;
- }
-#endif
-
- // 90kHz time scale
- uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
- uint8_t *rtp = mTSQueue->data();
- rtp[0] = 0x80;
- rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- ++mRTPSeqNo;
- ++mNumRTPSent;
- mNumRTPOctetsSent += mTSQueue->size() - 12;
-
- mLastRTPTime = rtpTime;
- mLastNTPTime = GetNowNTP();
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
-
- sp<ABuffer> data = new ABuffer(mTSQueue->size());
- memcpy(data->data(), rtp, mTSQueue->size());
-
- notify->setInt32("channel", mRTPChannel);
- notify->setBuffer("data", data);
- notify->post();
- } else {
- sendPacket(mRTPSessionID, rtp, mTSQueue->size());
-
-#if TRACK_BANDWIDTH
- mTotalBytesSent += mTSQueue->size();
- int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
- if (delayUs > 0ll) {
- ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
- mTotalBytesSent * 8.0 / delayUs);
- }
-#endif
- }
-
-#if ENABLE_RETRANSMISSION
- mTSQueue->setInt32Data(mRTPSeqNo - 1);
-
- mHistory.push_back(mTSQueue);
- ++mHistoryLength;
-
- if (mHistoryLength > kMaxHistoryLength) {
- mTSQueue = *mHistory.begin();
- mHistory.erase(mHistory.begin());
-
- --mHistoryLength;
- } else {
- mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
- }
-#endif
-
- mTSQueue->setRange(0, 12);
- }
-
- return size;
-}
-
void Sender::scheduleSendSR() {
if (mSendSRPending || mRTCPSessionID == 0) {
return;
@@ -851,6 +651,7 @@ status_t Sender::parseTSFB(
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);
@@ -865,6 +666,10 @@ status_t Sender::parseTSFB(
sendPacket(
mRTPRetransmissionSessionID,
retransRTP->data(), retransRTP->size());
+#else
+ sendPacket(
+ mRTPSessionID, buffer->data(), buffer->size());
+#endif
if (bufferSeqNo == seqNo) {
foundSeqNo = true;
@@ -975,5 +780,91 @@ void Sender::notifySessionDead() {
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
index e476e84..73e3d19 100644
--- a/media/libstagefright/wifi-display/source/Sender.h
+++ b/media/libstagefright/wifi-display/source/Sender.h
@@ -23,9 +23,17 @@
namespace android {
#define LOG_TRANSPORT_STREAM 0
-#define ENABLE_RETRANSMISSION 0
#define TRACK_BANDWIDTH 0
+#define ENABLE_RETRANSMISSION 0
+
+// 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;
@@ -51,7 +59,7 @@ struct Sender : public AHandler {
int32_t getRTPPort() const;
- void queuePackets(int64_t timeUs, const sp<ABuffer> &packets);
+ void queuePackets(int64_t timeUs, const sp<ABuffer> &tsPackets);
void scheduleSendSR();
protected:
@@ -60,13 +68,13 @@ protected:
private:
enum {
- kWhatQueuePackets,
+ kWhatDrainQueue,
kWhatSendSR,
kWhatRTPNotify,
kWhatRTCPNotify,
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
kWhatRTPRetransmissionNotify,
- kWhatRTCPRetransmissionNotify
+ kWhatRTCPRetransmissionNotify,
#endif
};
@@ -75,15 +83,13 @@ private:
static const uint32_t kSourceID = 0xdeadbeef;
static const size_t kMaxHistoryLength = 128;
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
static const size_t kRetransmissionPortOffset = 120;
#endif
sp<ANetworkSession> mNetSession;
sp<AMessage> mNotify;
- sp<ABuffer> mTSQueue;
-
TransportMode mTransportMode;
AString mClientIP;
@@ -96,7 +102,7 @@ private:
int32_t mRTPSessionID;
int32_t mRTCPSessionID;
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
int32_t mRTPRetransmissionSessionID;
int32_t mRTCPRetransmissionSessionID;
#endif
@@ -106,12 +112,11 @@ private:
bool mRTPConnected;
bool mRTCPConnected;
-
int64_t mFirstOutputBufferReadyTimeUs;
int64_t mFirstOutputBufferSentTimeUs;
uint32_t mRTPSeqNo;
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
uint32_t mRTPRetransmissionSeqNo;
#endif
@@ -133,22 +138,18 @@ private:
uint64_t mTotalBytesSent;
#endif
- void onSendSR();
- void addSR(const sp<ABuffer> &buffer);
- void addSDES(const sp<ABuffer> &buffer);
- static uint64_t GetNowNTP();
-
#if LOG_TRANSPORT_STREAM
FILE *mLogFile;
#endif
- ssize_t appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush);
-
- void onQueuePackets(const sp<ABuffer> &packets);
+ 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);
@@ -158,6 +159,8 @@ private:
void notifyInitDone();
void notifySessionDead();
+ void onDrainQueue(const sp<ABuffer> &udpPackets);
+
DISALLOW_EVIL_CONSTRUCTORS(Sender);
};
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index a5679ad..ef57a4d 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -314,6 +314,25 @@ void TSPacketizer::Track::finalize() {
mDescriptors.push_back(descriptor);
}
+ int32_t hdcpVersion;
+ if (mFormat->findInt32("hdcp-version", &hdcpVersion)) {
+ // HDCP descriptor
+
+ CHECK(hdcpVersion == 0x20 || hdcpVersion == 0x21);
+
+ sp<ABuffer> descriptor = new ABuffer(7);
+ uint8_t *data = descriptor->data();
+ data[0] = 0x05; // descriptor_tag
+ data[1] = 5; // descriptor_length
+ data[2] = 'H';
+ data[3] = 'D';
+ data[4] = 'C';
+ data[5] = 'P';
+ data[6] = hdcpVersion;
+
+ mDescriptors.push_back(descriptor);
+ }
+
mFinalized = true;
}
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index b16c5d0..78d6e62 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -534,9 +534,15 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) {
// 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",
@@ -773,8 +779,10 @@ status_t WifiDisplaySource::onReceiveM3Response(
status_t err = makeHDCP();
if (err != OK) {
- ALOGE("Unable to instantiate HDCP component.");
- return err;
+ ALOGE("Unable to instantiate HDCP component. "
+ "Not using HDCP after all.");
+
+ mUsingHDCP = false;
}
}
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 02fa0a6..1e855e7 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -26,6 +26,8 @@
namespace android {
+#define USE_1080P 0
+
struct IHDCP;
struct IRemoteDisplayClient;
struct ParsedMessage;
diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp
index 011edab..03a1123 100644
--- a/media/libstagefright/wifi-display/wfd.cpp
+++ b/media/libstagefright/wifi-display/wfd.cpp
@@ -23,22 +23,163 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <gui/SurfaceComposerClient.h>
+#include <media/AudioSystem.h>
#include <media/IMediaPlayerService.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/foundation/ADebug.h>
namespace android {
-} // namespace android
-
static void usage(const char *me) {
fprintf(stderr,
"usage:\n"
" %s -c host[:port]\tconnect to wifi source\n"
- " -u uri \tconnect to an rtsp uri\n",
+ " -u uri \tconnect to an rtsp uri\n"
+ " -l ip[:port] \tlisten on the specified port "
+ "(create a sink)\n",
me);
}
+struct RemoteDisplayClient : public BnRemoteDisplayClient {
+ RemoteDisplayClient();
+
+ virtual void onDisplayConnected(
+ const sp<ISurfaceTexture> &surfaceTexture,
+ uint32_t width,
+ uint32_t height,
+ uint32_t flags);
+
+ virtual void onDisplayDisconnected();
+ virtual void onDisplayError(int32_t error);
+
+ void waitUntilDone();
+
+protected:
+ virtual ~RemoteDisplayClient();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+
+ bool mDone;
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<ISurfaceTexture> mSurfaceTexture;
+ sp<IBinder> mDisplayBinder;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient);
+};
+
+RemoteDisplayClient::RemoteDisplayClient()
+ : mDone(false) {
+ mComposerClient = new SurfaceComposerClient;
+ CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
+}
+
+RemoteDisplayClient::~RemoteDisplayClient() {
+}
+
+void RemoteDisplayClient::onDisplayConnected(
+ const sp<ISurfaceTexture> &surfaceTexture,
+ 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 */);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);
+
+ Rect layerStackRect(1280, 720); // XXX fix this.
+ Rect displayRect(1280, 720);
+
+ mComposerClient->setDisplayProjection(
+ mDisplayBinder, 0 /* 0 degree rotation */,
+ layerStackRect,
+ displayRect);
+
+ SurfaceComposerClient::closeGlobalTransaction();
+}
+
+void RemoteDisplayClient::onDisplayDisconnected() {
+ ALOGI("onDisplayDisconnected");
+
+ Mutex::Autolock autoLock(mLock);
+ mDone = true;
+ mCondition.broadcast();
+}
+
+void RemoteDisplayClient::onDisplayError(int32_t error) {
+ ALOGI("onDisplayError error=%d", error);
+
+ Mutex::Autolock autoLock(mLock);
+ mDone = true;
+ mCondition.broadcast();
+}
+
+void RemoteDisplayClient::waitUntilDone() {
+ Mutex::Autolock autoLock(mLock);
+ while (!mDone) {
+ mCondition.wait(mLock);
+ }
+}
+
+static status_t enableAudioSubmix(bool enable) {
+ status_t err = AudioSystem::setDeviceConnectionState(
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ enable
+ ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE
+ : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ NULL /* device_address */);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = AudioSystem::setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ enable
+ ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE
+ : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ NULL /* device_address */);
+
+ return err;
+}
+
+static void createSource(const AString &addr, int32_t port) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ enableAudioSubmix(true /* enable */);
+
+ String8 iface;
+ iface.append(addr.c_str());
+ iface.append(StringPrintf(":%d", port).c_str());
+
+ sp<RemoteDisplayClient> client = new RemoteDisplayClient;
+ sp<IRemoteDisplay> display = service->listenForRemoteDisplay(client, iface);
+
+ client->waitUntilDone();
+
+ display->dispose();
+ display.clear();
+
+ enableAudioSubmix(false /* enable */);
+}
+
+} // namespace android
+
int main(int argc, char **argv) {
using namespace android;
@@ -50,6 +191,9 @@ int main(int argc, char **argv) {
int32_t connectToPort = -1;
AString uri;
+ AString listenOnAddr;
+ int32_t listenOnPort = -1;
+
int res;
while ((res = getopt(argc, argv, "hc:l:u:")) >= 0) {
switch (res) {
@@ -81,6 +225,28 @@ int main(int argc, char **argv) {
break;
}
+ case 'l':
+ {
+ const char *colonPos = strrchr(optarg, ':');
+
+ if (colonPos == NULL) {
+ listenOnAddr = optarg;
+ listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort;
+ } else {
+ listenOnAddr.setTo(optarg, colonPos - optarg);
+
+ char *end;
+ listenOnPort = strtol(colonPos + 1, &end, 10);
+
+ if (*end != '\0' || end == colonPos + 1
+ || listenOnPort < 1 || listenOnPort > 65535) {
+ fprintf(stderr, "Illegal port specified.\n");
+ exit(1);
+ }
+ }
+ break;
+ }
+
case '?':
case 'h':
default:
@@ -89,6 +255,18 @@ 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");