From 1c6833afd6cfdbc6c696ed0719ceedc37ec0b534 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 17 Oct 2012 12:16:50 -0700 Subject: Ignore SIGPIPE during write() to broken pipe This happens occasionally when taking a bugreport. Bug: 6447319 Change-Id: Ia6531a4a3658461f8fd3f7106e7996da7cc5933a --- media/mediaserver/main_mediaserver.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'media') diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index 6b1abb1..ddd5b84 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -33,6 +33,7 @@ using namespace android; int main(int argc, char** argv) { + signal(SIGPIPE, SIG_IGN); sp proc(ProcessState::self()); sp sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); -- cgit v1.1 From 0224bf170a3904576bba81593eaab113c5d3a4e7 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 12 Nov 2012 13:08:44 -0800 Subject: 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 Date: Fri Nov 9 11:32:23 2012 -0800 Better shutdown of wfd -l sessions. Change-Id: Id898a14ae21efd3b065b00a729830063d39195a7 commit 0e7d106dfe4eb6e2640b0b66c65deaba265f7ff0 Author: Andreas Huber 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 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 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 Date: Thu Nov 8 08:50:30 2012 -0800 fixed bitrate traffic simulation Change-Id: Ic5efb7cbb0b5d3b4917bc77b8ba73d447249e695 commit 016cdff18e74bdc631a5679e97192645ed095aa2 Author: Andreas Huber Date: Wed Nov 7 14:00:03 2012 -0800 resurrected "our" style of retransmission. Change-Id: I34d757aba67428437cb39b8293a9651750ad20d9 commit 384cf1a3c8fb4ec410bdf8fa5722c298e6028f3e Author: Andreas Huber Date: Tue Nov 6 09:38:55 2012 -0800 Changes to make wfd work on manta. Change-Id: I7a4e00cf16581fe2146edd1b359af195774090e4 commit 9628f24b22b35f28630d99dda3614babf51bc07e Author: Andreas Huber 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 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 --- .../wifi-display/ANetworkSession.cpp | 21 ++ media/libstagefright/wifi-display/Android.mk | 1 + media/libstagefright/wifi-display/TimeSeries.cpp | 67 ++++ media/libstagefright/wifi-display/TimeSeries.h | 46 +++ .../wifi-display/source/Converter.cpp | 46 ++- .../libstagefright/wifi-display/source/Converter.h | 2 + .../wifi-display/source/PlaybackSession.cpp | 68 +++- .../wifi-display/source/PlaybackSession.h | 2 +- .../wifi-display/source/RepeaterSource.cpp | 5 +- .../wifi-display/source/RepeaterSource.h | 2 + .../libstagefright/wifi-display/source/Sender.cpp | 415 ++++++++------------- media/libstagefright/wifi-display/source/Sender.h | 43 ++- .../wifi-display/source/TSPacketizer.cpp | 19 + .../wifi-display/source/WifiDisplaySource.cpp | 12 +- .../wifi-display/source/WifiDisplaySource.h | 2 + media/libstagefright/wifi-display/wfd.cpp | 184 ++++++++- 16 files changed, 633 insertions(+), 302 deletions(-) create mode 100644 media/libstagefright/wifi-display/TimeSeries.cpp create mode 100644 media/libstagefright/wifi-display/TimeSeries.h (limited to 'media') 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 &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 +#include + +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 + +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 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 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 getOutputFormat() const; + bool needToManuallyPrependSPSPPS() const; void feedAccessUnit(const sp &accessUnit); void signalEOS(); @@ -78,6 +79,7 @@ private: bool mIsVideo; bool mIsPCMAudio; sp mOutputFormat; + bool mNeedToManuallyPrependSPSPPS; sp mEncoder; sp 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 #include @@ -81,7 +82,10 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { bool hasOutputBuffer(int64_t *timeUs) const; void queueOutputBuffer(const sp &accessUnit); sp 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 &accessUnit) { mQueuedOutputBuffers.push_back(accessUnit); - mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs(); } @@ -292,6 +295,7 @@ sp 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 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 = 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 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 &accessUnit, + size_t trackIndex, sp accessUnit, sp *packets) { const sp &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 &accessUnit, + size_t trackIndex, sp accessUnit, sp *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 #include +#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 #include @@ -29,79 +30,8 @@ #include #include -#include - -#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 ¬ify) : 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 rtpNotify = new AMessage(kWhatRTPNotify, id()); sp rtcpNotify = new AMessage(kWhatRTCPNotify, id()); -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX sp 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 &packets) { - bool isVideo = false; + int64_t timeUs, const sp &tsPackets) { + const size_t numTSPackets = tsPackets->size() / 188; + + const size_t numRTPPackets = + (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1) + / kMaxNumTSPacketsPerRTPPacket; + + sp 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 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 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 &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 &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 &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 &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 &msg) { break; } - case kWhatQueuePackets: + case kWhatDrainQueue: { - sp packets; - CHECK(msg->findBuffer("packets", &packets)); + sp udpPackets; + CHECK(msg->findBuffer("udpPackets", &udpPackets)); - onQueuePackets(packets); + onDrainQueue(udpPackets); break; } @@ -532,156 +482,6 @@ void Sender::onMessageReceived(const sp &msg) { } } -void Sender::onQueuePackets(const sp &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 notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - - sp 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 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 &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 notify = mNotify->dup(); + notify->setInt32("what", kWhatBinaryData); + + sp 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 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 &packets); + void queuePackets(int64_t timeUs, const sp &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 mNetSession; sp mNotify; - sp 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 &buffer); - void addSDES(const sp &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 &packets); + void onSendSR(); + void addSR(const sp &buffer); + void addSDES(const sp &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 &buffer); @@ -158,6 +159,8 @@ private: void notifyInitDone(); void notifySessionDead(); + void onDrainQueue(const sp &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 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 #include +#include +#include #include +#include +#include #include #include 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 &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 mComposerClient; + sp mSurfaceTexture; + sp 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 &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 sm = defaultServiceManager(); + sp binder = sm->getService(String16("media.player")); + sp service = + interface_cast(binder); + + CHECK(service.get() != NULL); + + enableAudioSubmix(true /* enable */); + + String8 iface; + iface.append(addr.c_str()); + iface.append(StringPrintf(":%d", port).c_str()); + + sp client = new RemoteDisplayClient; + sp 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"); -- cgit v1.1 From 4471e47ece77be5b4159cc9cbbcef4d43900d36f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 14 Nov 2012 09:06:33 -0800 Subject: Properly signal an error if codec configuration goes wrong. previously any error signaled by setupXXX inside ACodec::configureCodec would be overwritten with the result of setMinBufferSize at the end of the function. Change-Id: Id4beb571ca52ea4646239d0af006e09ce1130268 related-to-bug: 7542181 --- media/libstagefright/ACodec.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 0ca027b..84b4962 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -937,7 +937,8 @@ status_t ACodec::configureCodec( } err = setupAACCodec( - encoder, numChannels, sampleRate, bitRate, aacProfile, isADTS != 0); + encoder, numChannels, sampleRate, bitRate, aacProfile, + isADTS != 0); } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { err = setupAMRCodec(encoder, false /* isWAMR */, bitRate); @@ -986,6 +987,10 @@ status_t ACodec::configureCodec( } } + if (err != OK) { + return err; + } + if (!msg->findInt32("encoder-delay", &mEncoderDelay)) { mEncoderDelay = 0; } -- cgit v1.1 From b7c8e91880463ff4981e3e53e98e45d68e2fe374 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 27 Nov 2012 15:02:53 -0800 Subject: Add support for HLS playlists of type 'event'. related-to-bug: 6870049 Squashed commit of the following: commit eee2f3ba6bb7335f4e285632726db85645669929 Author: Andreas Huber Date: Tue Nov 27 15:02:01 2012 -0800 Make everything a lot less verbose by default. Change-Id: I884d7a7901aa1e7d4ff590f065ca57a79d2af8b3 commit 6bbdb837ed5bd88008e45efb8faf595e4051ba26 Author: Andreas Huber Date: Tue Nov 27 14:34:46 2012 -0800 HLS now properly signals media time changes at discontinuities including the start of playback (which may not necessarily be at time 0 if the playlist is of type 'event' and hasn't completed yet). Change-Id: I5ab747d024f9b8d0df72a4e06a12ebb29f62802e commit 1555589832b1878a144a976a643e1af4d61f877c Author: Andreas Huber Date: Tue Nov 27 14:32:28 2012 -0800 As part of a time discontinuity, clients of IStreamListener can now signal the corresponding media time after the discontinuity, i.e. the first PTS timestamp following the discontinuity will be considered equivalent to the specified media time and media buffers timestamped accordingly. Change-Id: Id7db7679b7faa6efd6270620ff52e34e884f3e92 commit 5c24c605c073a11c426d025b1e7478fc1ad8365a Author: Andreas Huber Date: Tue Nov 27 13:00:56 2012 -0800 NuPlayer sources now expose flags() and can announce that duration may change (increase) dynamically, in which case duration will be polled at 1 second intervals and communicated to the upper layers. Change-Id: I45102909b7a19eed0dda576747e3814d742a0eea commit ecb71de8e281e61971a2cd73e7161a97540bc357 Author: Andreas Huber Date: Tue Nov 27 12:57:47 2012 -0800 Stop caching duration in MediaPlayer, duration could increase dynamically. Change-Id: I7bb2f16c0abe49debdf45c776d2266aa069d7791 commit 544aec5823e6d7a3e97e15b6b23546616bcd343e Author: Andreas Huber Date: Tue Nov 27 08:46:28 2012 -0800 An attempt to add support for "event" style HLS playlists. Change-Id: I3dfb2e801ecaff8f5d8bdb3a4fca1b18aeeb2c60 Change-Id: I48cf7f65a654d33f2f49ded74f8be22aed9e3b98 --- media/libmedia/IStreamSource.cpp | 3 + media/libmedia/mediaplayer.cpp | 36 ++++++--- .../nuplayer/GenericSource.cpp | 4 +- .../libmediaplayerservice/nuplayer/GenericSource.h | 3 +- .../nuplayer/HTTPLiveSource.cpp | 28 ++++++- .../nuplayer/HTTPLiveSource.h | 3 +- media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 49 ++++++++++++ media/libmediaplayerservice/nuplayer/NuPlayer.h | 6 ++ .../nuplayer/NuPlayerSource.h | 9 ++- .../libmediaplayerservice/nuplayer/RTSPSource.cpp | 4 +- media/libmediaplayerservice/nuplayer/RTSPSource.h | 3 +- .../nuplayer/StreamingSource.cpp | 20 ++++- .../nuplayer/StreamingSource.h | 2 + .../nuplayer/mp4/MP4Source.cpp | 4 + .../libmediaplayerservice/nuplayer/mp4/MP4Source.h | 2 + .../chromium_http/ChromiumHTTPDataSource.cpp | 7 +- media/libstagefright/httplive/LiveSession.cpp | 92 +++++++++++++++++++--- media/libstagefright/httplive/M3UParser.cpp | 9 ++- media/libstagefright/include/LiveSession.h | 14 +++- media/libstagefright/include/M3UParser.h | 2 + media/libstagefright/mpeg2ts/ATSParser.cpp | 16 +++- 21 files changed, 271 insertions(+), 45 deletions(-) (limited to 'media') diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index 78d810d..68ffca8 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -32,6 +32,9 @@ const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS"; // static const char *const IStreamListener::kKeyDiscontinuityMask = "discontinuity-mask"; +// static +const char *const IStreamListener::kKeyMediaTimeUs = "media-time-us"; + enum { // IStreamSource SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION, diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index b52a37d..bbbf4b6 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -47,7 +47,6 @@ MediaPlayer::MediaPlayer() ALOGV("constructor"); mListener = NULL; mCookie = NULL; - mDuration = -1; mStreamType = AUDIO_STREAM_MUSIC; mCurrentPosition = -1; mSeekPosition = -1; @@ -90,7 +89,6 @@ void MediaPlayer::disconnect() // always call with lock held void MediaPlayer::clear_l() { - mDuration = -1; mCurrentPosition = -1; mSeekPosition = -1; mVideoWidth = mVideoHeight = 0; @@ -395,14 +393,14 @@ status_t MediaPlayer::getCurrentPosition(int *msec) status_t MediaPlayer::getDuration_l(int *msec) { - ALOGV("getDuration"); + ALOGV("getDuration_l"); bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); if (mPlayer != 0 && isValidState) { - status_t ret = NO_ERROR; - if (mDuration <= 0) - ret = mPlayer->getDuration(&mDuration); - if (msec) - *msec = mDuration; + int durationMs; + status_t ret = mPlayer->getDuration(&durationMs); + if (msec) { + *msec = durationMs; + } return ret; } ALOGE("Attempt to call getDuration without a valid mediaplayer"); @@ -422,14 +420,28 @@ status_t MediaPlayer::seekTo_l(int msec) if ( msec < 0 ) { ALOGW("Attempt to seek to invalid position: %d", msec); msec = 0; - } else if ((mDuration > 0) && (msec > mDuration)) { - ALOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration); - msec = mDuration; } + + int durationMs; + status_t err = mPlayer->getDuration(&durationMs); + + if (err != OK) { + ALOGW("Stream has no duration and is therefore not seekable."); + return err; + } + + if (msec > durationMs) { + ALOGW("Attempt to seek to past end of file: request = %d, " + "durationMs = %d", + msec, + durationMs); + + msec = durationMs; + } + // cache duration mCurrentPosition = msec; if (mSeekPosition < 0) { - getDuration_l(NULL); mSeekPosition = msec; return mPlayer->seekTo(msec); } diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp index f0c3240..f281879 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp +++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp @@ -258,8 +258,8 @@ void NuPlayer::GenericSource::readBuffer( } } -bool NuPlayer::GenericSource::isSeekable() { - return true; +uint32_t NuPlayer::GenericSource::flags() const { + return FLAG_SEEKABLE; } } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h index e50b855..e1ce2c1 100644 --- a/media/libmediaplayerservice/nuplayer/GenericSource.h +++ b/media/libmediaplayerservice/nuplayer/GenericSource.h @@ -47,7 +47,8 @@ struct NuPlayer::GenericSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual bool isSeekable(); + + virtual uint32_t flags() const; protected: virtual ~GenericSource(); diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp index 1e98f35..5dcca12 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp @@ -121,9 +121,20 @@ status_t NuPlayer::HTTPLiveSource::feedMoreTSData() { } else { if (buffer[0] == 0x00) { // XXX legacy - sp extra; + + uint8_t type = buffer[1]; + + sp extra = new AMessage; + + if (type & 2) { + int64_t mediaTimeUs; + memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs)); + + extra->setInt64(IStreamListener::kKeyMediaTimeUs, mediaTimeUs); + } + mTSParser->signalDiscontinuity( - buffer[1] == 0x00 + ((type & 1) == 0) ? ATSParser::DISCONTINUITY_SEEK : ATSParser::DISCONTINUITY_FORMATCHANGE, extra); @@ -181,8 +192,17 @@ status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) { return OK; } -bool NuPlayer::HTTPLiveSource::isSeekable() { - return mLiveSession->isSeekable(); +uint32_t NuPlayer::HTTPLiveSource::flags() const { + uint32_t flags = 0; + if (mLiveSession->isSeekable()) { + flags |= FLAG_SEEKABLE; + } + + if (mLiveSession->hasDynamicDuration()) { + flags |= FLAG_DYNAMIC_DURATION; + } + + return flags; } } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h index 9950a9e..79f4ab8 100644 --- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h +++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h @@ -41,7 +41,8 @@ struct NuPlayer::HTTPLiveSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual bool isSeekable(); + + virtual uint32_t flags() const; protected: virtual ~HTTPLiveSource(); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 1ddf775..ff27873 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -59,6 +59,7 @@ NuPlayer::NuPlayer() mVideoEOS(false), mScanSourcesPending(false), mScanSourcesGeneration(0), + mPollDurationGeneration(0), mTimeDiscontinuityPending(false), mFlushingAudio(NONE), mFlushingVideo(NONE), @@ -210,6 +211,28 @@ void NuPlayer::onMessageReceived(const sp &msg) { break; } + case kWhatPollDuration: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mPollDurationGeneration) { + // stale + break; + } + + int64_t durationUs; + if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { + sp driver = mDriver.promote(); + if (driver != NULL) { + driver->notifyDuration(durationUs); + } + } + + msg->post(1000000ll); // poll again in a second. + break; + } + case kWhatSetVideoNativeWindow: { ALOGV("kWhatSetVideoNativeWindow"); @@ -274,6 +297,9 @@ void NuPlayer::onMessageReceived(const sp &msg) { ALOGV("scanning sources haveAudio=%d, haveVideo=%d", mAudioDecoder != NULL, mVideoDecoder != NULL); + bool mHadAnySourcesBefore = + (mAudioDecoder != NULL) || (mVideoDecoder != NULL); + if (mNativeWindow != NULL) { instantiateDecoder(false, &mVideoDecoder); } @@ -282,6 +308,17 @@ void NuPlayer::onMessageReceived(const sp &msg) { instantiateDecoder(true, &mAudioDecoder); } + if (!mHadAnySourcesBefore + && (mAudioDecoder != NULL || mVideoDecoder != NULL)) { + // This is the first time we've found anything playable. + + uint32_t flags = mSource->flags(); + + if (flags & Source::FLAG_DYNAMIC_DURATION) { + schedulePollDuration(); + } + } + status_t err; if ((err = mSource->feedMoreTSData()) != OK) { if (mAudioDecoder == NULL && mVideoDecoder == NULL) { @@ -534,6 +571,8 @@ void NuPlayer::onMessageReceived(const sp &msg) { { ALOGV("kWhatReset"); + cancelPollDuration(); + if (mRenderer != NULL) { // There's an edge case where the renderer owns all output // buffers and is paused, therefore the decoder will not read @@ -976,4 +1015,14 @@ status_t NuPlayer::setVideoScalingMode(int32_t mode) { return OK; } +void NuPlayer::schedulePollDuration() { + sp msg = new AMessage(kWhatPollDuration, id()); + msg->setInt32("generation", mPollDurationGeneration); + msg->post(); +} + +void NuPlayer::cancelPollDuration() { + ++mPollDurationGeneration; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index 36d3a9c..31efb2e 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -88,6 +88,7 @@ private: kWhatSeek = 'seek', kWhatPause = 'paus', kWhatResume = 'rsme', + kWhatPollDuration = 'polD', }; wp mDriver; @@ -107,6 +108,8 @@ private: bool mScanSourcesPending; int32_t mScanSourcesGeneration; + int32_t mPollDurationGeneration; + enum FlushStatus { NONE, AWAITING_DISCONTINUITY, @@ -150,6 +153,9 @@ private: void finishReset(); void postScanSources(); + void schedulePollDuration(); + void cancelPollDuration(); + DISALLOW_EVIL_CONSTRUCTORS(NuPlayer); }; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h index 66aeff3..a635340 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h @@ -25,6 +25,11 @@ namespace android { struct ABuffer; struct NuPlayer::Source : public RefBase { + enum Flags { + FLAG_SEEKABLE = 1, + FLAG_DYNAMIC_DURATION = 2, + }; + Source() {} virtual void start() = 0; @@ -47,9 +52,7 @@ struct NuPlayer::Source : public RefBase { return INVALID_OPERATION; } - virtual bool isSeekable() { - return false; - } + virtual uint32_t flags() const = 0; protected: virtual ~Source() {} diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 5a7a785..cf455bd 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -210,8 +210,8 @@ void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { mHandler->seek(seekTimeUs); } -bool NuPlayer::RTSPSource::isSeekable() { - return true; +uint32_t NuPlayer::RTSPSource::flags() const { + return FLAG_SEEKABLE; } void NuPlayer::RTSPSource::onMessageReceived(const sp &msg) { diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index f07c724..779d791 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -46,7 +46,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source { virtual status_t getDuration(int64_t *durationUs); virtual status_t seekTo(int64_t seekTimeUs); - virtual bool isSeekable(); + + virtual uint32_t flags() const; void onMessageReceived(const sp &msg); diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index a1fd2ed..7159404 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -93,8 +93,22 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { } else { if (buffer[0] == 0x00) { // XXX legacy + + if (extra == NULL) { + extra = new AMessage; + } + + uint8_t type = buffer[1]; + + if (type & 2) { + int64_t mediaTimeUs; + memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs)); + + extra->setInt64(IStreamListener::kKeyMediaTimeUs, mediaTimeUs); + } + mTSParser->signalDiscontinuity( - buffer[1] == 0x00 + ((type & 1) == 0) ? ATSParser::DISCONTINUITY_SEEK : ATSParser::DISCONTINUITY_FORMATCHANGE, extra); @@ -159,5 +173,9 @@ status_t NuPlayer::StreamingSource::dequeueAccessUnit( return err; } +uint32_t NuPlayer::StreamingSource::flags() const { + return 0; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.h b/media/libmediaplayerservice/nuplayer/StreamingSource.h index 3971e2a..a27b58a 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.h +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.h @@ -35,6 +35,8 @@ struct NuPlayer::StreamingSource : public NuPlayer::Source { virtual status_t dequeueAccessUnit(bool audio, sp *accessUnit); + virtual uint32_t flags() const; + protected: virtual ~StreamingSource(); diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp index ffb3a65..a62d5a2 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp @@ -133,4 +133,8 @@ status_t MP4Source::dequeueAccessUnit( return mParser->dequeueAccessUnit(audio, accessUnit); } +uint32_t MP4Source::flags() const { + return 0; +} + } // namespace android diff --git a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h index 4e927af..abca236 100644 --- a/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h +++ b/media/libmediaplayerservice/nuplayer/mp4/MP4Source.h @@ -35,6 +35,8 @@ struct MP4Source : public NuPlayer::Source { virtual status_t dequeueAccessUnit( bool audio, sp *accessUnit); + virtual uint32_t flags() const; + protected: virtual ~MP4Source(); diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp index 32a0ec8..91ce175 100644 --- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp +++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp @@ -65,7 +65,10 @@ status_t ChromiumHTTPDataSource::connect( if (getUID(&uid)) { mDelegate->setUID(uid); } + +#if defined(LOG_NDEBUG) && !LOG_NDEBUG LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid); +#endif return connect_l(uri, headers, offset); } @@ -78,8 +81,10 @@ status_t ChromiumHTTPDataSource::connect_l( disconnect_l(); } - LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, +#if defined(LOG_NDEBUG) && !LOG_NDEBUG + LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect to @%lld", offset); +#endif mURI = uri; mContentType = String8("application/octet-stream"); diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 93d6429..733753b 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -55,7 +55,9 @@ LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) mSeqNumber(-1), mSeekTimeUs(-1), mNumRetries(0), + mStartOfPlayback(true), mDurationUs(-1), + mDurationFixed(false), mSeekDone(false), mDisconnectPending(false), mMonitorQueueGeneration(0), @@ -311,6 +313,8 @@ status_t LiveSession::fetchFile( } sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { + ALOGV("fetchPlaylist '%s'", url); + *unchanged = false; sp buffer; @@ -364,6 +368,37 @@ sp LiveSession::fetchPlaylist(const char *url, bool *unchanged) { return playlist; } +int64_t LiveSession::getSegmentStartTimeUs(int32_t seqNumber) const { + CHECK(mPlaylist != NULL); + + int32_t firstSeqNumberInPlaylist; + if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( + "media-sequence", &firstSeqNumberInPlaylist)) { + firstSeqNumberInPlaylist = 0; + } + + int32_t lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + + CHECK_GE(seqNumber, firstSeqNumberInPlaylist); + CHECK_LE(seqNumber, lastSeqNumberInPlaylist); + + int64_t segmentStartUs = 0ll; + for (int32_t index = 0; + index < seqNumber - firstSeqNumberInPlaylist; ++index) { + sp itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + segmentStartUs += itemDurationUs; + } + + return segmentStartUs; +} + static double uniformRand() { return (double)rand() / RAND_MAX; } @@ -512,8 +547,6 @@ rinse_repeat: url = mMasterURL; } - bool firstTime = (mPlaylist == NULL); - if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) { // If we switch bandwidths, do not pay any heed to whether // playlists changed since the last time... @@ -535,11 +568,12 @@ rinse_repeat: mPlaylist = playlist; } - if (firstTime) { + if (!mDurationFixed) { Mutex::Autolock autoLock(mLock); - if (!mPlaylist->isComplete()) { + if (!mPlaylist->isComplete() && !mPlaylist->isEvent()) { mDurationUs = -1; + mDurationFixed = true; } else { mDurationUs = 0; for (size_t i = 0; i < mPlaylist->size(); ++i) { @@ -552,6 +586,8 @@ rinse_repeat: mDurationUs += itemDurationUs; } + + mDurationFixed = mPlaylist->isComplete(); } } @@ -569,7 +605,7 @@ rinse_repeat: bool bandwidthChanged = false; if (mSeekTimeUs >= 0) { - if (mPlaylist->isComplete()) { + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { size_t index = 0; int64_t segmentStartUs = 0; while (index < mPlaylist->size()) { @@ -617,13 +653,21 @@ rinse_repeat: mCondition.broadcast(); } + const int32_t lastSeqNumberInPlaylist = + firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; + if (mSeqNumber < 0) { - mSeqNumber = firstSeqNumberInPlaylist; + if (mPlaylist->isComplete()) { + mSeqNumber = firstSeqNumberInPlaylist; + } else { + // If this is a live session, start 3 segments from the end. + mSeqNumber = lastSeqNumberInPlaylist - 3; + if (mSeqNumber < firstSeqNumberInPlaylist) { + mSeqNumber = firstSeqNumberInPlaylist; + } + } } - int32_t lastSeqNumberInPlaylist = - firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; - if (mSeqNumber < firstSeqNumberInPlaylist || mSeqNumber > lastSeqNumberInPlaylist) { if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { @@ -686,6 +730,9 @@ rinse_repeat: range_length = -1; } + ALOGV("fetching segment %d from (%d .. %d)", + mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist); + sp buffer; status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length); if (err != OK) { @@ -737,6 +784,11 @@ rinse_repeat: bandwidthChanged = false; } + if (mStartOfPlayback) { + seekDiscontinuity = true; + mStartOfPlayback = false; + } + if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) { // Signal discontinuity. @@ -747,7 +799,19 @@ rinse_repeat: memset(tmp->data(), 0, tmp->size()); // signal a 'hard' discontinuity for explicit or bandwidthChanged. - tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; + uint8_t type = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; + + if (mPlaylist->isComplete() || mPlaylist->isEvent()) { + // If this was a live event this made no sense since + // we don't have access to all the segment before the current + // one. + int64_t segmentStartTimeUs = getSegmentStartTimeUs(mSeqNumber); + memcpy(tmp->data() + 2, &segmentStartTimeUs, sizeof(segmentStartTimeUs)); + + type |= 2; + } + + tmp->data()[1] = type; mDataSource->queueBuffer(tmp); } @@ -923,17 +987,21 @@ void LiveSession::onSeek(const sp &msg) { postMonitorQueue(); } -status_t LiveSession::getDuration(int64_t *durationUs) { +status_t LiveSession::getDuration(int64_t *durationUs) const { Mutex::Autolock autoLock(mLock); *durationUs = mDurationUs; return OK; } -bool LiveSession::isSeekable() { +bool LiveSession::isSeekable() const { int64_t durationUs; return getDuration(&durationUs) == OK && durationUs >= 0; } +bool LiveSession::hasDynamicDuration() const { + return !mDurationFixed; +} + } // namespace android diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 7d3cf05..44e03dc 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -32,7 +32,8 @@ M3UParser::M3UParser( mBaseURI(baseURI), mIsExtM3U(false), mIsVariantPlaylist(false), - mIsComplete(false) { + mIsComplete(false), + mIsEvent(false) { mInitCheck = parse(data, size); } @@ -55,6 +56,10 @@ bool M3UParser::isComplete() const { return mIsComplete; } +bool M3UParser::isEvent() const { + return mIsEvent; +} + sp M3UParser::meta() { return mMeta; } @@ -200,6 +205,8 @@ status_t M3UParser::parse(const void *_data, size_t size) { err = parseCipherInfo(line, &itemMeta, mBaseURI); } else if (line.startsWith("#EXT-X-ENDLIST")) { mIsComplete = true; + } else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) { + mIsEvent = true; } else if (line.startsWith("#EXTINF")) { if (mIsVariantPlaylist) { return ERROR_MALFORMED; diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h index 3a11612..f329cc9 100644 --- a/media/libstagefright/include/LiveSession.h +++ b/media/libstagefright/include/LiveSession.h @@ -48,8 +48,10 @@ struct LiveSession : public AHandler { // Blocks until seek is complete. void seekTo(int64_t timeUs); - status_t getDuration(int64_t *durationUs); - bool isSeekable(); + status_t getDuration(int64_t *durationUs) const; + + bool isSeekable() const; + bool hasDynamicDuration() const; protected: virtual ~LiveSession(); @@ -95,10 +97,12 @@ private: int32_t mSeqNumber; int64_t mSeekTimeUs; int32_t mNumRetries; + bool mStartOfPlayback; - Mutex mLock; + mutable Mutex mLock; Condition mCondition; int64_t mDurationUs; + bool mDurationFixed; // Duration has been determined once and for all. bool mSeekDone; bool mDisconnectPending; @@ -136,6 +140,10 @@ private: static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *); + // Returns the media time in us of the segment specified by seqNumber. + // This is computed by summing the durations of all segments before it. + int64_t getSegmentStartTimeUs(int32_t seqNumber) const; + DISALLOW_EVIL_CONSTRUCTORS(LiveSession); }; diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h index e30d6fd..2d2f50f 100644 --- a/media/libstagefright/include/M3UParser.h +++ b/media/libstagefright/include/M3UParser.h @@ -33,6 +33,7 @@ struct M3UParser : public RefBase { bool isExtM3U() const; bool isVariantPlaylist() const; bool isComplete() const; + bool isEvent() const; sp meta(); @@ -54,6 +55,7 @@ private: bool mIsExtM3U; bool mIsVariantPlaylist; bool mIsComplete; + bool mIsEvent; sp mMeta; Vector mItems; diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 9faa6bc..4f6c4b2 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -215,6 +215,14 @@ bool ATSParser::Program::parsePID( void ATSParser::Program::signalDiscontinuity( DiscontinuityType type, const sp &extra) { + int64_t mediaTimeUs; + if ((type & DISCONTINUITY_TIME) + && extra != NULL + && extra->findInt64( + IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) { + mFirstPTSValid = false; + } + for (size_t i = 0; i < mStreams.size(); ++i) { mStreams.editValueAt(i)->signalDiscontinuity(type, extra); } @@ -929,7 +937,13 @@ status_t ATSParser::feedTSPacket(const void *data, size_t size) { void ATSParser::signalDiscontinuity( DiscontinuityType type, const sp &extra) { - if (type == DISCONTINUITY_ABSOLUTE_TIME) { + int64_t mediaTimeUs; + if ((type & DISCONTINUITY_TIME) + && extra != NULL + && extra->findInt64( + IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) { + mAbsoluteTimeAnchorUs = mediaTimeUs; + } else if (type == DISCONTINUITY_ABSOLUTE_TIME) { int64_t timeUs; CHECK(extra->findInt64("timeUs", &timeUs)); -- cgit v1.1 From 0dbe5a9321b24b6883fbb2fe97cd9d525128b0b5 Mon Sep 17 00:00:00 2001 From: James Dong Date: Thu, 15 Nov 2012 14:00:26 -0800 Subject: Added optional intra macroblock refresh support for encoding o related-to-bug: 7524791 Change-Id: I95ac4ee925e2dbeb00b3cfb2e29c611698c5cc9f --- media/libstagefright/ACodec.cpp | 50 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 84b4962..a01d03f 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -1630,6 +1630,43 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp &msg) { return err; } +status_t ACodec::setCyclicIntraMacroblockRefresh(const sp &msg, int32_t mode) { + OMX_VIDEO_PARAM_INTRAREFRESHTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + params.eRefreshMode = static_cast(mode); + + if (params.eRefreshMode == OMX_VIDEO_IntraRefreshCyclic || + params.eRefreshMode == OMX_VIDEO_IntraRefreshBoth) { + int32_t mbs; + if (!msg->findInt32("intra-refresh-CIR-mbs", &mbs)) { + return INVALID_OPERATION; + } + params.nCirMBs = mbs; + } + + if (params.eRefreshMode == OMX_VIDEO_IntraRefreshAdaptive || + params.eRefreshMode == OMX_VIDEO_IntraRefreshBoth) { + int32_t mbs; + if (!msg->findInt32("intra-refresh-AIR-mbs", &mbs)) { + return INVALID_OPERATION; + } + params.nAirMBs = mbs; + + int32_t ref; + if (!msg->findInt32("intra-refresh-AIR-ref", &ref)) { + return INVALID_OPERATION; + } + params.nAirRef = ref; + } + + status_t err = mOMX->setParameter( + mNode, OMX_IndexParamVideoIntraRefresh, + ¶ms, sizeof(params)); + return err; +} + static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { if (iFramesInterval < 0) { return 0xFFFFFFFF; @@ -1825,11 +1862,22 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { frameRate = (float)tmp; } + status_t err = OK; + int32_t intraRefreshMode = 0; + if (msg->findInt32("intra-refresh-mode", &intraRefreshMode)) { + err = setCyclicIntraMacroblockRefresh(msg, intraRefreshMode); + if (err != OK) { + ALOGE("Setting intra macroblock refresh mode (%d) failed: 0x%x", + err, intraRefreshMode); + return err; + } + } + OMX_VIDEO_PARAM_AVCTYPE h264type; InitOMXParams(&h264type); h264type.nPortIndex = kPortIndexOutput; - status_t err = mOMX->getParameter( + err = mOMX->getParameter( mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); if (err != OK) { -- cgit v1.1 From 13e8a0e8a14faaf3fe0bcebf4da1f2694d59565d Mon Sep 17 00:00:00 2001 From: James Dong Date: Thu, 15 Nov 2012 18:31:50 -0800 Subject: Reduce the frequency of IDR frames and add intra-fresh mode support for WiFi display The time interval between periodic neighboring IDR frames is increased from 1 second to 15 seconds. o related-to-bug: 7524791 Change-Id: Ic32f37448f952f329549eda5e73637ee3b02f046 --- .../libstagefright/wifi-display/source/Converter.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 82c98b9..7a87444 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -161,7 +161,24 @@ status_t Converter::initEncoder() { mOutputFormat->setInt32("bitrate", videoBitrate); mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant); mOutputFormat->setInt32("frame-rate", 30); - mOutputFormat->setInt32("i-frame-interval", 1); // Iframes every 1 secs + mOutputFormat->setInt32("i-frame-interval", 15); // Iframes every 15 secs + + // Configure encoder to use intra macroblock refresh mode + mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic); + + int width, height, mbs; + if (!mOutputFormat->findInt32("width", &width) + || !mOutputFormat->findInt32("height", &height)) { + return ERROR_UNSUPPORTED; + } + + // Update macroblocks in a cyclic fashion with 10% of all MBs within + // frame gets updated at one time. It takes about 10 frames to + // completely update a whole video frame. If the frame rate is 30, + // it takes about 333 ms in the best case (if next frame is not an IDR) + // to recover from a lost/corrupted packet. + mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100; + mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs); } ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); -- cgit v1.1 From 5131d127a042ee88f903370be88845dc8c9f8578 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 16 Nov 2012 10:38:11 -0800 Subject: [wfd] Support a low(er) power state by triggering PAUSE/RESUME. Change-Id: Ibe42bfa73816bbfeb7e652d435254d0171b89727 related-to-bug: 7638150 --- media/libmedia/IRemoteDisplay.cpp | 31 +++++ media/libmediaplayerservice/RemoteDisplay.cpp | 8 ++ media/libmediaplayerservice/RemoteDisplay.h | 2 + .../wifi-display/source/MediaPuller.cpp | 34 ++++- .../wifi-display/source/MediaPuller.h | 6 + .../wifi-display/source/PlaybackSession.cpp | 44 +++++++ .../wifi-display/source/PlaybackSession.h | 3 + .../wifi-display/source/WifiDisplaySource.cpp | 146 +++++++++++++++++---- .../wifi-display/source/WifiDisplaySource.h | 20 ++- 9 files changed, 264 insertions(+), 30 deletions(-) (limited to 'media') diff --git a/media/libmedia/IRemoteDisplay.cpp b/media/libmedia/IRemoteDisplay.cpp index da25a15..1e15434 100644 --- a/media/libmedia/IRemoteDisplay.cpp +++ b/media/libmedia/IRemoteDisplay.cpp @@ -23,6 +23,8 @@ namespace android { enum { DISPOSE = IBinder::FIRST_CALL_TRANSACTION, + PAUSE, + RESUME, }; class BpRemoteDisplay: public BpInterface @@ -33,6 +35,20 @@ public: { } + virtual status_t pause() { + Parcel data, reply; + data.writeInterfaceToken(IRemoteDisplay::getInterfaceDescriptor()); + remote()->transact(PAUSE, data, &reply); + return reply.readInt32(); + } + + virtual status_t resume() { + Parcel data, reply; + data.writeInterfaceToken(IRemoteDisplay::getInterfaceDescriptor()); + remote()->transact(RESUME, data, &reply); + return reply.readInt32(); + } + status_t dispose() { Parcel data, reply; @@ -55,6 +71,21 @@ status_t BnRemoteDisplay::onTransact( reply->writeInt32(dispose()); return NO_ERROR; } + + case PAUSE: + { + CHECK_INTERFACE(IRemoteDisplay, data, reply); + reply->writeInt32(pause()); + return OK; + } + + case RESUME: + { + CHECK_INTERFACE(IRemoteDisplay, data, reply); + reply->writeInt32(resume()); + return OK; + } + default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp index 5baa3ad..20e6513 100644 --- a/media/libmediaplayerservice/RemoteDisplay.cpp +++ b/media/libmediaplayerservice/RemoteDisplay.cpp @@ -40,6 +40,14 @@ RemoteDisplay::RemoteDisplay( RemoteDisplay::~RemoteDisplay() { } +status_t RemoteDisplay::pause() { + return mSource->pause(); +} + +status_t RemoteDisplay::resume() { + return mSource->resume(); +} + status_t RemoteDisplay::dispose() { mSource->stop(); diff --git a/media/libmediaplayerservice/RemoteDisplay.h b/media/libmediaplayerservice/RemoteDisplay.h index 0d87250..bd8b684 100644 --- a/media/libmediaplayerservice/RemoteDisplay.h +++ b/media/libmediaplayerservice/RemoteDisplay.h @@ -33,6 +33,8 @@ struct WifiDisplaySource; struct RemoteDisplay : public BnRemoteDisplay { RemoteDisplay(const sp &client, const char *iface); + virtual status_t pause(); + virtual status_t resume(); virtual status_t dispose(); protected: diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp index ab69c4a..189bea3 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.cpp +++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp @@ -34,7 +34,8 @@ MediaPuller::MediaPuller( : mSource(source), mNotify(notify), mPullGeneration(0), - mIsAudio(false) { + mIsAudio(false), + mPaused(false) { sp meta = source->getFormat(); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -71,6 +72,14 @@ void MediaPuller::stopAsync(const sp ¬ify) { msg->post(); } +void MediaPuller::pause() { + (new AMessage(kWhatPause, id()))->post(); +} + +void MediaPuller::resume() { + (new AMessage(kWhatResume, id()))->post(); +} + void MediaPuller::onMessageReceived(const sp &msg) { switch (msg->what()) { case kWhatStart: @@ -95,7 +104,6 @@ void MediaPuller::onMessageReceived(const sp &msg) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - response->postReply(replyID); break; } @@ -130,6 +138,16 @@ void MediaPuller::onMessageReceived(const sp &msg) { MediaBuffer *mbuf; status_t err = mSource->read(&mbuf); + if (mPaused) { + if (err == OK) { + mbuf->release(); + mbuf = NULL; + } + + schedulePull(); + break; + } + if (err != OK) { if (err == ERROR_END_OF_STREAM) { ALOGI("stream ended."); @@ -176,6 +194,18 @@ void MediaPuller::onMessageReceived(const sp &msg) { break; } + case kWhatPause: + { + mPaused = true; + break; + } + + case kWhatResume: + { + mPaused = false; + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/MediaPuller.h b/media/libstagefright/wifi-display/source/MediaPuller.h index 728da7b..1291bb3 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.h +++ b/media/libstagefright/wifi-display/source/MediaPuller.h @@ -35,6 +35,9 @@ struct MediaPuller : public AHandler { status_t start(); void stopAsync(const sp ¬ify); + void pause(); + void resume(); + protected: virtual void onMessageReceived(const sp &msg); virtual ~MediaPuller(); @@ -44,12 +47,15 @@ private: kWhatStart, kWhatStop, kWhatPull, + kWhatPause, + kWhatResume, }; sp mSource; sp mNotify; int32_t mPullGeneration; bool mIsAudio; + bool mPaused; status_t postSynchronouslyAndReturnError(const sp &msg); void schedulePull(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index 4e5eb52..916f797 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -76,6 +76,9 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { status_t start(); void stopAsync(); + void pause(); + void resume(); + void queueAccessUnit(const sp &accessUnit); sp dequeueAccessUnit(); @@ -208,6 +211,14 @@ void WifiDisplaySource::PlaybackSession::Track::stopAsync() { } } +void WifiDisplaySource::PlaybackSession::Track::pause() { + mMediaPuller->pause(); +} + +void WifiDisplaySource::PlaybackSession::Track::resume() { + mMediaPuller->resume(); +} + void WifiDisplaySource::PlaybackSession::Track::onMessageReceived( const sp &msg) { switch (msg->what()) { @@ -325,6 +336,7 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( mInterfaceAddr(interfaceAddr), mHDCP(hdcp), mWeAreDead(false), + mPaused(false), mLastLifesignUs(), mVideoTrackIndex(-1), mPrevTimeUs(-1ll), @@ -383,6 +395,8 @@ void WifiDisplaySource::PlaybackSession::updateLiveness() { status_t WifiDisplaySource::PlaybackSession::play() { updateLiveness(); + (new AMessage(kWhatResume, id()))->post(); + return OK; } @@ -413,6 +427,8 @@ status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() { status_t WifiDisplaySource::PlaybackSession::pause() { updateLiveness(); + (new AMessage(kWhatPause, id()))->post(); + return OK; } @@ -590,6 +606,34 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( break; } + case kWhatPause: + { + if (mPaused) { + break; + } + + for (size_t i = 0; i < mTracks.size(); ++i) { + mTracks.editValueAt(i)->pause(); + } + + mPaused = true; + break; + } + + case kWhatResume: + { + if (!mPaused) { + break; + } + + for (size_t i = 0; i < mTracks.size(); ++i) { + mTracks.editValueAt(i)->resume(); + } + + mPaused = false; + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index dabc1c4..b9d193b 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -84,6 +84,8 @@ private: kWhatUpdateSurface, kWhatFinishPlay, kWhatPacketize, + kWhatPause, + kWhatResume, }; sp mNetSession; @@ -93,6 +95,7 @@ private: in_addr mInterfaceAddr; sp mHDCP; bool mWeAreDead; + bool mPaused; int64_t mLastLifesignUs; diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index 78d6e62..08f67f9 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -65,41 +65,50 @@ WifiDisplaySource::WifiDisplaySource( WifiDisplaySource::~WifiDisplaySource() { } -status_t WifiDisplaySource::start(const char *iface) { - CHECK_EQ(mState, INITIALIZED); - - sp msg = new AMessage(kWhatStart, id()); - msg->setString("iface", iface); - - sp response; - status_t err = msg->postAndAwaitResponse(&response); +static status_t PostAndAwaitResponse( + const sp &msg, sp *response) { + status_t err = msg->postAndAwaitResponse(response); if (err != OK) { return err; } - if (!response->findInt32("err", &err)) { + if (response == NULL || !(*response)->findInt32("err", &err)) { err = OK; } return err; } +status_t WifiDisplaySource::start(const char *iface) { + CHECK_EQ(mState, INITIALIZED); + + sp msg = new AMessage(kWhatStart, id()); + msg->setString("iface", iface); + + sp response; + return PostAndAwaitResponse(msg, &response); +} + status_t WifiDisplaySource::stop() { sp msg = new AMessage(kWhatStop, id()); sp response; - status_t err = msg->postAndAwaitResponse(&response); + return PostAndAwaitResponse(msg, &response); +} - if (err != OK) { - return err; - } +status_t WifiDisplaySource::pause() { + sp msg = new AMessage(kWhatPause, id()); - if (!response->findInt32("err", &err)) { - err = OK; - } + sp response; + return PostAndAwaitResponse(msg, &response); +} - return err; +status_t WifiDisplaySource::resume() { + sp msg = new AMessage(kWhatResume, id()); + + sp response; + return PostAndAwaitResponse(msg, &response); } void WifiDisplaySource::onMessageReceived(const sp &msg) { @@ -236,6 +245,20 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { mClient->onDisplayError( IRemoteDisplayClient::kDisplayErrorUnknown); } + +#if 0 + // testing only. + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd.trigger", val, NULL)) { + if (!strcasecmp(val, "pause") && mState == PLAYING) { + mState = PLAYING_TO_PAUSED; + sendTrigger(mClientSessionID, TRIGGER_PAUSE); + } else if (!strcasecmp(val, "play") && mState == PAUSED) { + mState = PAUSED_TO_PLAYING; + sendTrigger(mClientSessionID, TRIGGER_PLAY); + } + } +#endif break; } @@ -254,8 +277,8 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { if (mState >= AWAITING_CLIENT_PLAY) { // We have a session, i.e. a previous SETUP succeeded. - status_t err = sendM5( - mClientSessionID, true /* requestShutdown */); + status_t err = sendTrigger( + mClientSessionID, TRIGGER_TEARDOWN); if (err == OK) { mState = AWAITING_CLIENT_TEARDOWN; @@ -273,6 +296,46 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { break; } + case kWhatPause: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = OK; + + if (mState != PLAYING) { + err = INVALID_OPERATION; + } else { + mState = PLAYING_TO_PAUSED; + sendTrigger(mClientSessionID, TRIGGER_PAUSE); + } + + sp response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatResume: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = OK; + + if (mState != PAUSED) { + err = INVALID_OPERATION; + } else { + mState = PAUSED_TO_PLAYING; + sendTrigger(mClientSessionID, TRIGGER_PLAY); + } + + sp response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + case kWhatReapDeadClients: { mReaperPending = false; @@ -400,7 +463,7 @@ void WifiDisplaySource::onMessageReceived(const sp &msg) { if (mSetupTriggerDeferred) { mSetupTriggerDeferred = false; - sendM5(mClientSessionID, false /* requestShutdown */); + sendTrigger(mClientSessionID, TRIGGER_SETUP); } break; } @@ -574,13 +637,25 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { return OK; } -status_t WifiDisplaySource::sendM5(int32_t sessionID, bool requestShutdown) { +status_t WifiDisplaySource::sendTrigger( + int32_t sessionID, TriggerType triggerType) { AString body = "wfd_trigger_method: "; - if (requestShutdown) { - ALOGI("Sending TEARDOWN trigger."); - body.append("TEARDOWN"); - } else { - body.append("SETUP"); + switch (triggerType) { + case TRIGGER_SETUP: + body.append("SETUP"); + break; + case TRIGGER_TEARDOWN: + ALOGI("Sending TEARDOWN trigger."); + body.append("TEARDOWN"); + break; + case TRIGGER_PAUSE: + body.append("PAUSE"); + break; + case TRIGGER_PLAY: + body.append("PLAY"); + break; + default: + TRESPASS(); } body.append("\r\n"); @@ -807,7 +882,7 @@ status_t WifiDisplaySource::onReceiveM4Response( return OK; } - return sendM5(sessionID, false /* requestShutdown */); + return sendTrigger(sessionID, TRIGGER_SETUP); } status_t WifiDisplaySource::onReceiveM5Response( @@ -1184,6 +1259,11 @@ status_t WifiDisplaySource::onPlayRequest( return err; } + if (mState == PAUSED_TO_PLAYING) { + mState = PLAYING; + return OK; + } + playbackSession->finishPlay(); CHECK_EQ(mState, AWAITING_CLIENT_PLAY); @@ -1205,6 +1285,12 @@ status_t WifiDisplaySource::onPauseRequest( return ERROR_MALFORMED; } + ALOGI("Received PAUSE request."); + + if (mState != PLAYING_TO_PAUSED) { + return INVALID_OPERATION; + } + status_t err = playbackSession->pause(); CHECK_EQ(err, (status_t)OK); @@ -1214,6 +1300,12 @@ status_t WifiDisplaySource::onPauseRequest( err = mNetSession->sendRequest(sessionID, response.c_str()); + if (err != OK) { + return err; + } + + mState = PAUSED; + return err; } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index 1e855e7..974e070 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -44,6 +44,9 @@ struct WifiDisplaySource : public AHandler { status_t start(const char *iface); status_t stop(); + status_t pause(); + status_t resume(); + protected: virtual ~WifiDisplaySource(); virtual void onMessageReceived(const sp &msg); @@ -59,6 +62,9 @@ private: AWAITING_CLIENT_PLAY, ABOUT_TO_PLAY, PLAYING, + PLAYING_TO_PAUSED, + PAUSED, + PAUSED_TO_PLAYING, AWAITING_CLIENT_TEARDOWN, STOPPING, STOPPED, @@ -68,6 +74,8 @@ private: kWhatStart, kWhatRTSPNotify, kWhatStop, + kWhatPause, + kWhatResume, kWhatReapDeadClients, kWhatPlaybackSessionNotify, kWhatKeepAlive, @@ -147,7 +155,17 @@ private: status_t sendM1(int32_t sessionID); status_t sendM3(int32_t sessionID); status_t sendM4(int32_t sessionID); - status_t sendM5(int32_t sessionID, bool requestShutdown); + + enum TriggerType { + TRIGGER_SETUP, + TRIGGER_TEARDOWN, + TRIGGER_PAUSE, + TRIGGER_PLAY, + }; + + // M5 + status_t sendTrigger(int32_t sessionID, TriggerType triggerType); + status_t sendM16(int32_t sessionID); status_t onReceiveM1Response( -- cgit v1.1 From e5aed03d30ea0ce49728873c5b74f89ba05a9541 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 29 Nov 2012 09:54:42 -0800 Subject: Enable retransmission of UDP packets in case we want to use it in our upcoming wfd _sink_ implementation. Change-Id: I4509c30d5a7b992bc841b73d63db902bbcf8f76a related-to-bug: 7638155 --- media/libstagefright/wifi-display/source/Sender.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'media') diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h index 73e3d19..66951f7 100644 --- a/media/libstagefright/wifi-display/source/Sender.h +++ b/media/libstagefright/wifi-display/source/Sender.h @@ -25,7 +25,7 @@ namespace android { #define LOG_TRANSPORT_STREAM 0 #define TRACK_BANDWIDTH 0 -#define ENABLE_RETRANSMISSION 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 -- cgit v1.1 From 47c1a5f7c13d82aa8834fd4543bd1d713b97808e Mon Sep 17 00:00:00 2001 From: Dylan Powers Date: Tue, 27 Nov 2012 16:06:38 -0800 Subject: Bug fix for the MediaPlayer::prepare() api. For an MP3 source, within the prepare command, ID3 tags are checked in search of gapless playback info. This causes problems for streamed sources. If ID3v2 tags aren't present, then a check is done for ID3v1 tags. This results in a read command that asks the cache to jump to the end of the file, and subsequently make an extra http call to request those bytes. For a streamed source, this causes the file to not be downloaded automatically when MediaPlayer::prepare() is called, and causes stuttering and extra buffering time to be needed when start() is finally called. The solution is to ignore the ID3v1 tags as the gapless info would never exist there, and only check for ID3v2 tags. Cherrypicked from external contribution ffd6ffc5429c45577fd8e9f8fa90e79bb91b8a84 b/7638165 Change-Id: I7d1b94cffbfe7c38ca094834dedbc92a58855e20 --- media/libstagefright/MP3Extractor.cpp | 6 ++++-- media/libstagefright/id3/ID3.cpp | 4 ++-- media/libstagefright/include/ID3.h | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'media') diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index d94054b..380dab4 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -350,8 +350,10 @@ MP3Extractor::MP3Extractor( mInitCheck = OK; - // get iTunes-style gapless info if present - ID3 id3(mDataSource); + // Get iTunes-style gapless info if present. + // When getting the id3 tag, skip the V1 tags to prevent the source cache + // from being iterated to the end of the file. + ID3 id3(mDataSource, true); if (id3.isValid()) { ID3::Iterator *com = new ID3::Iterator(id3, "COM"); if (com->done()) { diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 69274ca..22c2f5a 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -30,7 +30,7 @@ namespace android { static const size_t kMaxMetadataSize = 3 * 1024 * 1024; -ID3::ID3(const sp &source) +ID3::ID3(const sp &source, bool ignoreV1) : mIsValid(false), mData(NULL), mSize(0), @@ -38,7 +38,7 @@ ID3::ID3(const sp &source) mVersion(ID3_UNKNOWN) { mIsValid = parseV2(source); - if (!mIsValid) { + if (!mIsValid && !ignoreV1) { mIsValid = parseV1(source); } } diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 8714008..3028f56 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -35,7 +35,7 @@ struct ID3 { ID3_V2_4, }; - ID3(const sp &source); + ID3(const sp &source, bool ignoreV1 = false); ~ID3(); bool isValid() const; -- cgit v1.1