diff options
Diffstat (limited to 'media')
47 files changed, 1249 insertions, 385 deletions
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<IRemoteDisplay> @@ -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/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/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<IRemoteDisplayClient> &client, const char *iface); + virtual status_t pause(); + virtual status_t resume(); virtual status_t dispose(); protected: 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<AMessage> extra; + + uint8_t type = buffer[1]; + + sp<AMessage> 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<AMessage> &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<NuPlayerDriver> 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<AMessage> &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<AMessage> &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<AMessage> &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<AMessage> 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<NuPlayerDriver> 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<AMessage> &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<AMessage> &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<ABuffer> *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<ABuffer> *accessUnit); + virtual uint32_t flags() const; + protected: virtual ~MP4Source(); diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 0ca027b..a01d03f 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; } @@ -1625,6 +1630,43 @@ status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { return err; } +status_t ACodec::setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode) { + OMX_VIDEO_PARAM_INTRAREFRESHTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + params.eRefreshMode = static_cast<OMX_VIDEO_INTRAREFRESHTYPE>(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; @@ -1820,11 +1862,22 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &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) { 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/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 <URL suppressed> @%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<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) { + ALOGV("fetchPlaylist '%s'", url); + *unchanged = false; sp<ABuffer> buffer; @@ -364,6 +368,37 @@ sp<M3UParser> 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<AMessage> 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<ABuffer> 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<AMessage> &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<AMessage> 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/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<DataSource> &source) +ID3::ID3(const sp<DataSource> &source, bool ignoreV1) : mIsValid(false), mData(NULL), mSize(0), @@ -38,7 +38,7 @@ ID3::ID3(const sp<DataSource> &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<DataSource> &source); + ID3(const sp<DataSource> &source, bool ignoreV1 = false); ~ID3(); bool isValid() const; 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<AMessage> meta(); @@ -54,6 +55,7 @@ private: bool mIsExtM3U; bool mIsVariantPlaylist; bool mIsComplete; + bool mIsEvent; sp<AMessage> mMeta; Vector<Item> 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<AMessage> &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<AMessage> &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)); diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp index 819cd62..62a6e7f 100644 --- a/media/libstagefright/wifi-display/ANetworkSession.cpp +++ b/media/libstagefright/wifi-display/ANetworkSession.cpp @@ -407,6 +407,24 @@ status_t ANetworkSession::Session::writeMore() { do { const sp<ABuffer> &datagram = *mOutDatagrams.begin(); + uint8_t *data = datagram->data(); + if (data[0] == 0x80 && (data[1] & 0x7f) == 33) { + int64_t nowUs = ALooper::GetNowUs(); + + uint32_t prevRtpTime = U32_AT(&data[4]); + + // 90kHz time scale + uint32_t rtpTime = (nowUs * 9ll) / 100ll; + int32_t diffTime = (int32_t)rtpTime - (int32_t)prevRtpTime; + + ALOGV("correcting rtpTime by %.0f ms", diffTime / 90.0); + + data[4] = rtpTime >> 24; + data[5] = (rtpTime >> 16) & 0xff; + data[6] = (rtpTime >> 8) & 0xff; + data[7] = rtpTime & 0xff; + } + int n; do { n = send(mSocket, datagram->data(), datagram->size(), 0); @@ -424,6 +442,9 @@ status_t ANetworkSession::Session::writeMore() { } while (err == OK && !mOutDatagrams.empty()); if (err == -EAGAIN) { + if (!mOutDatagrams.empty()) { + ALOGI("%d datagrams remain queued.", mOutDatagrams.size()); + } err = OK; } diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk index 611bfff..75098f1 100644 --- a/media/libstagefright/wifi-display/Android.mk +++ b/media/libstagefright/wifi-display/Android.mk @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ source/Sender.cpp \ source/TSPacketizer.cpp \ source/WifiDisplaySource.cpp \ + TimeSeries.cpp \ LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ diff --git a/media/libstagefright/wifi-display/TimeSeries.cpp b/media/libstagefright/wifi-display/TimeSeries.cpp new file mode 100644 index 0000000..d882d98 --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSeries.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TimeSeries.h" + +#include <math.h> +#include <string.h> + +namespace android { + +TimeSeries::TimeSeries() + : mCount(0), + mSum(0.0) { +} + +void TimeSeries::add(double val) { + if (mCount < kHistorySize) { + mValues[mCount++] = val; + mSum += val; + } else { + mSum -= mValues[0]; + memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double)); + mValues[kHistorySize - 1] = val; + mSum += val; + } +} + +double TimeSeries::mean() const { + if (mCount < 1) { + return 0.0; + } + + return mSum / mCount; +} + +double TimeSeries::sdev() const { + if (mCount < 1) { + return 0.0; + } + + double m = mean(); + + double sum = 0.0; + for (size_t i = 0; i < mCount; ++i) { + double tmp = mValues[i] - m; + tmp *= tmp; + + sum += tmp; + } + + return sqrt(sum / mCount); +} + +} // namespace android diff --git a/media/libstagefright/wifi-display/TimeSeries.h b/media/libstagefright/wifi-display/TimeSeries.h new file mode 100644 index 0000000..c818d51 --- /dev/null +++ b/media/libstagefright/wifi-display/TimeSeries.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIME_SERIES_H_ + +#define TIME_SERIES_H_ + +#include <sys/types.h> + +namespace android { + +struct TimeSeries { + TimeSeries(); + + void add(double val); + + double mean() const; + double sdev() const; + +private: + enum { + kHistorySize = 20 + }; + double mValues[kHistorySize]; + + size_t mCount; + double mSum; +}; + +} // namespace android + +#endif // TIME_SERIES_H_ + diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 01a394f..7a87444 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -48,6 +48,7 @@ Converter::Converter( mInputFormat(format), mIsVideo(false), mIsPCMAudio(usePCMAudio), + mNeedToManuallyPrependSPSPPS(false), mDoMoreWorkPending(false) #if ENABLE_SILENCE_DETECTION ,mFirstSilentFrameUs(-1ll) @@ -94,6 +95,10 @@ sp<AMessage> Converter::getOutputFormat() const { return mOutputFormat; } +bool Converter::needToManuallyPrependSPSPPS() const { + return mNeedToManuallyPrependSPSPPS; +} + static int32_t getBitrate(const char *propName, int32_t defaultValue) { char val[PROPERTY_VALUE_MAX]; if (property_get(propName, val, NULL)) { @@ -156,17 +161,63 @@ status_t Converter::initEncoder() { mOutputFormat->setInt32("bitrate", videoBitrate); mOutputFormat->setInt32("bitrate-mode", OMX_Video_ControlRateConstant); mOutputFormat->setInt32("frame-rate", 30); - mOutputFormat->setInt32("i-frame-interval", 1); // Iframes every 1 secs - mOutputFormat->setInt32("prepend-sps-pps-to-idr-frames", 1); + mOutputFormat->setInt32("i-frame-interval", 15); // Iframes every 15 secs + + // Configure encoder to use intra macroblock refresh mode + mOutputFormat->setInt32("intra-refresh-mode", OMX_VIDEO_IntraRefreshCyclic); + + int width, height, mbs; + if (!mOutputFormat->findInt32("width", &width) + || !mOutputFormat->findInt32("height", &height)) { + return ERROR_UNSUPPORTED; + } + + // Update macroblocks in a cyclic fashion with 10% of all MBs within + // frame gets updated at one time. It takes about 10 frames to + // completely update a whole video frame. If the frame rate is 30, + // it takes about 333 ms in the best case (if next frame is not an IDR) + // to recover from a lost/corrupted packet. + mbs = (((width + 15) / 16) * ((height + 15) / 16) * 10) / 100; + mOutputFormat->setInt32("intra-refresh-CIR-mbs", mbs); } ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str()); - status_t err = mEncoder->configure( - mOutputFormat, - NULL /* nativeWindow */, - NULL /* crypto */, - MediaCodec::CONFIGURE_FLAG_ENCODE); + mNeedToManuallyPrependSPSPPS = false; + + status_t err = NO_INIT; + + if (!isAudio) { + sp<AMessage> tmp = mOutputFormat->dup(); + tmp->setInt32("prepend-sps-pps-to-idr-frames", 1); + + err = mEncoder->configure( + tmp, + NULL /* nativeWindow */, + NULL /* crypto */, + MediaCodec::CONFIGURE_FLAG_ENCODE); + + if (err == OK) { + // Encoder supported prepending SPS/PPS, we don't need to emulate + // it. + mOutputFormat = tmp; + } else { + mNeedToManuallyPrependSPSPPS = true; + + ALOGI("We going to manually prepend SPS and PPS to IDR frames."); + } + } + + if (err != OK) { + // We'll get here for audio or if we failed to configure the encoder + // to automatically prepend SPS/PPS in the case of video. + + err = mEncoder->configure( + mOutputFormat, + NULL /* nativeWindow */, + NULL /* crypto */, + MediaCodec::CONFIGURE_FLAG_ENCODE); + } if (err != OK) { return err; diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 2cdeda3..0665eea 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -44,6 +44,7 @@ struct Converter : public AHandler { size_t getInputBufferCount() const; sp<AMessage> getOutputFormat() const; + bool needToManuallyPrependSPSPPS() const; void feedAccessUnit(const sp<ABuffer> &accessUnit); void signalEOS(); @@ -78,6 +79,7 @@ private: bool mIsVideo; bool mIsPCMAudio; sp<AMessage> mOutputFormat; + bool mNeedToManuallyPrependSPSPPS; sp<MediaCodec> mEncoder; sp<AMessage> mEncoderActivityNotify; diff --git a/media/libstagefright/wifi-display/source/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<MetaData> meta = source->getFormat(); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); @@ -71,6 +72,14 @@ void MediaPuller::stopAsync(const sp<AMessage> ¬ify) { msg->post(); } +void MediaPuller::pause() { + (new AMessage(kWhatPause, id()))->post(); +} + +void MediaPuller::resume() { + (new AMessage(kWhatResume, id()))->post(); +} + void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatStart: @@ -95,7 +104,6 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); - response->postReply(replyID); break; } @@ -130,6 +138,16 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { MediaBuffer *mbuf; status_t err = mSource->read(&mbuf); + if (mPaused) { + if (err == OK) { + mbuf->release(); + mbuf = NULL; + } + + schedulePull(); + break; + } + if (err != OK) { if (err == ERROR_END_OF_STREAM) { ALOGI("stream ended."); @@ -176,6 +194,18 @@ void MediaPuller::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatPause: + { + mPaused = true; + break; + } + + case kWhatResume: + { + mPaused = false; + break; + } + default: TRESPASS(); } diff --git a/media/libstagefright/wifi-display/source/MediaPuller.h b/media/libstagefright/wifi-display/source/MediaPuller.h index 728da7b..1291bb3 100644 --- a/media/libstagefright/wifi-display/source/MediaPuller.h +++ b/media/libstagefright/wifi-display/source/MediaPuller.h @@ -35,6 +35,9 @@ struct MediaPuller : public AHandler { status_t start(); void stopAsync(const sp<AMessage> ¬ify); + void pause(); + void resume(); + protected: virtual void onMessageReceived(const sp<AMessage> &msg); virtual ~MediaPuller(); @@ -44,12 +47,15 @@ private: kWhatStart, kWhatStop, kWhatPull, + kWhatPause, + kWhatResume, }; sp<MediaSource> mSource; sp<AMessage> mNotify; int32_t mPullGeneration; bool mIsAudio; + bool mPaused; status_t postSynchronouslyAndReturnError(const sp<AMessage> &msg); void schedulePull(); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index f1e7140..916f797 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -26,6 +26,7 @@ #include "Sender.h" #include "TSPacketizer.h" #include "include/avc_utils.h" +#include "WifiDisplaySource.h" #include <binder/IServiceManager.h> #include <gui/ISurfaceComposer.h> @@ -75,13 +76,19 @@ struct WifiDisplaySource::PlaybackSession::Track : public AHandler { status_t start(); void stopAsync(); + void pause(); + void resume(); + void queueAccessUnit(const sp<ABuffer> &accessUnit); sp<ABuffer> dequeueAccessUnit(); bool hasOutputBuffer(int64_t *timeUs) const; void queueOutputBuffer(const sp<ABuffer> &accessUnit); sp<ABuffer> dequeueOutputBuffer(); + +#if SUSPEND_VIDEO_IF_IDLE bool isSuspended() const; +#endif size_t countQueuedOutputBuffers() const { return mQueuedOutputBuffers.size(); @@ -204,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<AMessage> &msg) { switch (msg->what()) { @@ -279,7 +294,6 @@ bool WifiDisplaySource::PlaybackSession::Track::hasOutputBuffer( void WifiDisplaySource::PlaybackSession::Track::queueOutputBuffer( const sp<ABuffer> &accessUnit) { mQueuedOutputBuffers.push_back(accessUnit); - mLastOutputBufferQueuedTimeUs = ALooper::GetNowUs(); } @@ -292,6 +306,7 @@ sp<ABuffer> WifiDisplaySource::PlaybackSession::Track::dequeueOutputBuffer() { return outputBuffer; } +#if SUSPEND_VIDEO_IF_IDLE bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const { if (!mQueuedOutputBuffers.empty()) { return false; @@ -307,6 +322,7 @@ bool WifiDisplaySource::PlaybackSession::Track::isSuspended() const { // this track suspended for the time being. return (ALooper::GetNowUs() - mLastOutputBufferQueuedTimeUs) > 60000ll; } +#endif //////////////////////////////////////////////////////////////////////////////// @@ -320,6 +336,7 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( mInterfaceAddr(interfaceAddr), mHDCP(hdcp), mWeAreDead(false), + mPaused(false), mLastLifesignUs(), mVideoTrackIndex(-1), mPrevTimeUs(-1ll), @@ -378,6 +395,8 @@ void WifiDisplaySource::PlaybackSession::updateLiveness() { status_t WifiDisplaySource::PlaybackSession::play() { updateLiveness(); + (new AMessage(kWhatResume, id()))->post(); + return OK; } @@ -408,6 +427,8 @@ status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() { status_t WifiDisplaySource::PlaybackSession::pause() { updateLiveness(); + (new AMessage(kWhatPause, id()))->post(); + return OK; } @@ -443,8 +464,13 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( ssize_t packetizerTrackIndex = track->packetizerTrackIndex(); if (packetizerTrackIndex < 0) { - packetizerTrackIndex = - mPacketizer->addTrack(track->getFormat()); + sp<AMessage> trackFormat = track->getFormat()->dup(); + if (mHDCP != NULL && !track->isAudio()) { + // HDCP2.0 _and_ HDCP 2.1 specs say to set the version + // inside the HDCP descriptor to 0x20!!! + trackFormat->setInt32("hdcp-version", 0x20); + } + packetizerTrackIndex = mPacketizer->addTrack(trackFormat); CHECK_GE(packetizerTrackIndex, 0); @@ -580,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(); } @@ -642,8 +696,10 @@ status_t WifiDisplaySource::PlaybackSession::addSource( sp<Converter> converter = new Converter(notify, codecLooper, format, usePCMAudio); - if (converter->initCheck() != OK) { - return converter->initCheck(); + err = converter->initCheck(); + if (err != OK) { + ALOGE("%s converter returned err %d", isVideo ? "video" : "audio", err); + return err; } looper()->registerHandler(converter); @@ -735,11 +791,19 @@ sp<ISurfaceTexture> WifiDisplaySource::PlaybackSession::getSurfaceTexture() { } int32_t WifiDisplaySource::PlaybackSession::width() const { +#if USE_1080P + return 1920; +#else return 1280; +#endif } int32_t WifiDisplaySource::PlaybackSession::height() const { +#if USE_1080P + return 1080; +#else return 720; +#endif } void WifiDisplaySource::PlaybackSession::requestIDRFrame() { @@ -767,7 +831,7 @@ bool WifiDisplaySource::PlaybackSession::allTracksHavePacketizerIndex() { } status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit( - size_t trackIndex, const sp<ABuffer> &accessUnit, + size_t trackIndex, sp<ABuffer> accessUnit, sp<ABuffer> *packets) { const sp<Track> &track = mTracks.valueFor(trackIndex); @@ -776,9 +840,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 +933,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 +1007,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 +1036,7 @@ bool WifiDisplaySource::PlaybackSession::drainAccessUnit() { if (err != OK) { notifySessionDead(); + return false; } if ((ssize_t)minTrackIndex == mVideoTrackIndex) { @@ -957,6 +1044,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..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<ANetworkSession> mNetSession; @@ -93,6 +95,7 @@ private: in_addr mInterfaceAddr; sp<IHDCP> mHDCP; bool mWeAreDead; + bool mPaused; int64_t mLastLifesignUs; @@ -127,7 +130,7 @@ private: bool allTracksHavePacketizerIndex(); status_t packetizeAccessUnit( - size_t trackIndex, const sp<ABuffer> &accessUnit, + size_t trackIndex, sp<ABuffer> accessUnit, sp<ABuffer> *packets); status_t packetizeQueuedAccessUnits(); diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.cpp b/media/libstagefright/wifi-display/source/RepeaterSource.cpp index 641e63f..72be927 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.cpp +++ b/media/libstagefright/wifi-display/source/RepeaterSource.cpp @@ -125,11 +125,14 @@ status_t RepeaterSource::read( return mResult; } +#if SUSPEND_VIDEO_IF_IDLE int64_t nowUs = ALooper::GetNowUs(); if (nowUs - mLastBufferUpdateUs > 1000000ll) { mLastBufferUpdateUs = -1ll; stale = true; - } else { + } else +#endif + { mBuffer->add_ref(); *buffer = mBuffer; (*buffer)->meta_data()->setInt64(kKeyTime, bufferTimeUs); diff --git a/media/libstagefright/wifi-display/source/RepeaterSource.h b/media/libstagefright/wifi-display/source/RepeaterSource.h index e4aa2b6..a13973c 100644 --- a/media/libstagefright/wifi-display/source/RepeaterSource.h +++ b/media/libstagefright/wifi-display/source/RepeaterSource.h @@ -6,6 +6,8 @@ #include <media/stagefright/foundation/AHandlerReflector.h> #include <media/stagefright/MediaSource.h> +#define SUSPEND_VIDEO_IF_IDLE 1 + namespace android { // This MediaSource delivers frames at a constant rate by repeating buffers diff --git a/media/libstagefright/wifi-display/source/Sender.cpp b/media/libstagefright/wifi-display/source/Sender.cpp index ea12424..9048691 100644 --- a/media/libstagefright/wifi-display/source/Sender.cpp +++ b/media/libstagefright/wifi-display/source/Sender.cpp @@ -21,6 +21,7 @@ #include "Sender.h" #include "ANetworkSession.h" +#include "TimeSeries.h" #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -29,79 +30,8 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/Utils.h> -#include <math.h> - -#define DEBUG_JITTER 0 - namespace android { -//////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_JITTER -struct TimeSeries { - TimeSeries(); - - void add(double val); - - double mean() const; - double sdev() const; - -private: - enum { - kHistorySize = 20 - }; - double mValues[kHistorySize]; - - size_t mCount; - double mSum; -}; - -TimeSeries::TimeSeries() - : mCount(0), - mSum(0.0) { -} - -void TimeSeries::add(double val) { - if (mCount < kHistorySize) { - mValues[mCount++] = val; - mSum += val; - } else { - mSum -= mValues[0]; - memmove(&mValues[0], &mValues[1], (kHistorySize - 1) * sizeof(double)); - mValues[kHistorySize - 1] = val; - mSum += val; - } -} - -double TimeSeries::mean() const { - if (mCount < 1) { - return 0.0; - } - - return mSum / mCount; -} - -double TimeSeries::sdev() const { - if (mCount < 1) { - return 0.0; - } - - double m = mean(); - - double sum = 0.0; - for (size_t i = 0; i < mCount; ++i) { - double tmp = mValues[i] - m; - tmp *= tmp; - - sum += tmp; - } - - return sqrt(sum / mCount); -} -#endif // DEBUG_JITTER - -//////////////////////////////////////////////////////////////////////////////// - static size_t kMaxRTPPacketSize = 1500; static size_t kMaxNumTSPacketsPerRTPPacket = (kMaxRTPPacketSize - 12) / 188; @@ -110,14 +40,13 @@ Sender::Sender( const sp<AMessage> ¬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<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id()); sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id()); -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX sp<AMessage> rtpRetransmissionNotify = new AMessage(kWhatRTPRetransmissionNotify, id()); @@ -264,7 +191,7 @@ status_t Sender::init( } } -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX if (mTransportMode == TRANSPORT_UDP) { int32_t rtpRetransmissionSession; @@ -358,44 +285,67 @@ int32_t Sender::getRTPPort() const { } void Sender::queuePackets( - int64_t timeUs, const sp<ABuffer> &packets) { - bool isVideo = false; + int64_t timeUs, const sp<ABuffer> &tsPackets) { + const size_t numTSPackets = tsPackets->size() / 188; + + const size_t numRTPPackets = + (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1) + / kMaxNumTSPacketsPerRTPPacket; + + sp<ABuffer> udpPackets = new ABuffer( + numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188)); + + udpPackets->meta()->setInt64("timeUs", timeUs); + + size_t dstOffset = 0; + for (size_t i = 0; i < numTSPackets; ++i) { + if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) { + static const bool kMarkerBit = false; + + uint8_t *rtp = udpPackets->data() + dstOffset; + rtp[0] = 0x80; + rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0); // M-bit + rtp[2] = (mRTPSeqNo >> 8) & 0xff; + rtp[3] = mRTPSeqNo & 0xff; + rtp[4] = 0x00; // rtp time to be filled in later. + rtp[5] = 0x00; + rtp[6] = 0x00; + rtp[7] = 0x00; + rtp[8] = kSourceID >> 24; + rtp[9] = (kSourceID >> 16) & 0xff; + rtp[10] = (kSourceID >> 8) & 0xff; + rtp[11] = kSourceID & 0xff; + + ++mRTPSeqNo; + + dstOffset += 12; + } + + memcpy(udpPackets->data() + dstOffset, + tsPackets->data() + 188 * i, + 188); - int32_t dummy; - if (packets->meta()->findInt32("isVideo", &dummy)) { - isVideo = true; + dstOffset += 188; } - int64_t delayUs; - int64_t whenUs; + udpPackets->setRange(0, dstOffset); - if (mFirstOutputBufferReadyTimeUs < 0ll) { - mFirstOutputBufferReadyTimeUs = timeUs; - mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs(); - delayUs = 0ll; - } else { - int64_t nowUs = ALooper::GetNowUs(); - - whenUs = (timeUs - mFirstOutputBufferReadyTimeUs) - + mFirstOutputBufferSentTimeUs; + sp<AMessage> msg = new AMessage(kWhatDrainQueue, id()); + msg->setBuffer("udpPackets", udpPackets); + msg->post(); - delayUs = whenUs - nowUs; +#if LOG_TRANSPORT_STREAM + if (mLogFile != NULL) { + fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile); } - - sp<AMessage> msg = new AMessage(kWhatQueuePackets, id()); - msg->setBuffer("packets", packets); - - packets->meta()->setInt64("timeUs", timeUs); - packets->meta()->setInt64("whenUs", whenUs); - packets->meta()->setInt64("delayUs", delayUs); - msg->post(delayUs > 0 ? delayUs : 0); +#endif } void Sender::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatRTPNotify: case kWhatRTCPNotify: -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX case kWhatRTPRetransmissionNotify: case kWhatRTCPRetransmissionNotify: #endif @@ -419,7 +369,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findString("detail", &detail)); if ((msg->what() == kWhatRTPNotify -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX || msg->what() == kWhatRTPRetransmissionNotify #endif ) && !errorOccuredDuringSend) { @@ -443,7 +393,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) { } else if (sessionID == mRTCPSessionID) { mRTCPSessionID = 0; } -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX else if (sessionID == mRTPRetransmissionSessionID) { mRTPRetransmissionSessionID = 0; } else if (sessionID == mRTCPRetransmissionSessionID) { @@ -465,7 +415,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) { status_t err; if (msg->what() == kWhatRTCPNotify -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX || msg->what() == kWhatRTCPRetransmissionNotify #endif ) @@ -507,12 +457,12 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) { break; } - case kWhatQueuePackets: + case kWhatDrainQueue: { - sp<ABuffer> packets; - CHECK(msg->findBuffer("packets", &packets)); + sp<ABuffer> udpPackets; + CHECK(msg->findBuffer("udpPackets", &udpPackets)); - onQueuePackets(packets); + onDrainQueue(udpPackets); break; } @@ -532,156 +482,6 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) { } } -void Sender::onQueuePackets(const sp<ABuffer> &packets) { -#if DEBUG_JITTER - int32_t dummy; - if (packets->meta()->findInt32("isVideo", &dummy)) { - static int64_t lastTimeUs = 0ll; - int64_t nowUs = ALooper::GetNowUs(); - - static TimeSeries series; - series.add((double)(nowUs - lastTimeUs)); - - ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f", - nowUs - lastTimeUs, series.mean(), series.sdev()); - - lastTimeUs = nowUs; - } -#endif - - int64_t startTimeUs = ALooper::GetNowUs(); - - for (size_t offset = 0; - offset < packets->size(); offset += 188) { - bool lastTSPacket = (offset + 188 >= packets->size()); - - appendTSData( - packets->data() + offset, - 188, - true /* timeDiscontinuity */, - lastTSPacket /* flush */); - } - -#if 0 - int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs; - - int64_t whenUs; - CHECK(packets->meta()->findInt64("whenUs", &whenUs)); - - int64_t delayUs; - CHECK(packets->meta()->findInt64("delayUs", &delayUs)); - - bool isVideo = false; - int32_t dummy; - if (packets->meta()->findInt32("isVideo", &dummy)) { - isVideo = true; - } - - int64_t nowUs = ALooper::GetNowUs(); - - if (nowUs - whenUs > 2000) { - ALOGI("[%s] delayUs = %lld us, delta = %lld us", - isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs); - } -#endif - -#if LOG_TRANSPORT_STREAM - if (mLogFile != NULL) { - fwrite(packets->data(), 1, packets->size(), mLogFile); - } -#endif -} - -ssize_t Sender::appendTSData( - const void *data, size_t size, bool timeDiscontinuity, bool flush) { - CHECK_EQ(size, 188); - - CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity()); - - memcpy(mTSQueue->data() + mTSQueue->size(), data, size); - mTSQueue->setRange(0, mTSQueue->size() + size); - - if (flush || mTSQueue->size() == mTSQueue->capacity()) { - // flush - - int64_t nowUs = ALooper::GetNowUs(); - -#if TRACK_BANDWIDTH - if (mFirstPacketTimeUs < 0ll) { - mFirstPacketTimeUs = nowUs; - } -#endif - - // 90kHz time scale - uint32_t rtpTime = (nowUs * 9ll) / 100ll; - - uint8_t *rtp = mTSQueue->data(); - rtp[0] = 0x80; - rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit - rtp[2] = (mRTPSeqNo >> 8) & 0xff; - rtp[3] = mRTPSeqNo & 0xff; - rtp[4] = rtpTime >> 24; - rtp[5] = (rtpTime >> 16) & 0xff; - rtp[6] = (rtpTime >> 8) & 0xff; - rtp[7] = rtpTime & 0xff; - rtp[8] = kSourceID >> 24; - rtp[9] = (kSourceID >> 16) & 0xff; - rtp[10] = (kSourceID >> 8) & 0xff; - rtp[11] = kSourceID & 0xff; - - ++mRTPSeqNo; - ++mNumRTPSent; - mNumRTPOctetsSent += mTSQueue->size() - 12; - - mLastRTPTime = rtpTime; - mLastNTPTime = GetNowNTP(); - - if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) { - sp<AMessage> notify = mNotify->dup(); - notify->setInt32("what", kWhatBinaryData); - - sp<ABuffer> data = new ABuffer(mTSQueue->size()); - memcpy(data->data(), rtp, mTSQueue->size()); - - notify->setInt32("channel", mRTPChannel); - notify->setBuffer("data", data); - notify->post(); - } else { - sendPacket(mRTPSessionID, rtp, mTSQueue->size()); - -#if TRACK_BANDWIDTH - mTotalBytesSent += mTSQueue->size(); - int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs; - - if (delayUs > 0ll) { - ALOGI("approx. net bandwidth used: %.2f Mbit/sec", - mTotalBytesSent * 8.0 / delayUs); - } -#endif - } - -#if ENABLE_RETRANSMISSION - mTSQueue->setInt32Data(mRTPSeqNo - 1); - - mHistory.push_back(mTSQueue); - ++mHistoryLength; - - if (mHistoryLength > kMaxHistoryLength) { - mTSQueue = *mHistory.begin(); - mHistory.erase(mHistory.begin()); - - --mHistoryLength; - } else { - mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188); - } -#endif - - mTSQueue->setRange(0, 12); - } - - return size; -} - void Sender::scheduleSendSR() { if (mSendSRPending || mRTCPSessionID == 0) { return; @@ -851,6 +651,7 @@ status_t Sender::parseTSFB( if (retransmit) { ALOGI("retransmitting seqNo %d", bufferSeqNo); +#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size()); uint8_t *rtp = retransRTP->data(); memcpy(rtp, buffer->data(), 12); @@ -865,6 +666,10 @@ status_t Sender::parseTSFB( sendPacket( mRTPRetransmissionSessionID, retransRTP->data(), retransRTP->size()); +#else + sendPacket( + mRTPSessionID, buffer->data(), buffer->size()); +#endif if (bufferSeqNo == seqNo) { foundSeqNo = true; @@ -975,5 +780,91 @@ void Sender::notifySessionDead() { notify->post(); } +void Sender::onDrainQueue(const sp<ABuffer> &udpPackets) { + static const size_t kFullRTPPacketSize = + 12 + 188 * kMaxNumTSPacketsPerRTPPacket; + + size_t srcOffset = 0; + while (srcOffset < udpPackets->size()) { + uint8_t *rtp = udpPackets->data() + srcOffset; + + size_t rtpPacketSize = udpPackets->size() - srcOffset; + if (rtpPacketSize > kFullRTPPacketSize) { + rtpPacketSize = kFullRTPPacketSize; + } + + int64_t nowUs = ALooper::GetNowUs(); + mLastNTPTime = GetNowNTP(); + + // 90kHz time scale + uint32_t rtpTime = (nowUs * 9ll) / 100ll; + + rtp[4] = rtpTime >> 24; + rtp[5] = (rtpTime >> 16) & 0xff; + rtp[6] = (rtpTime >> 8) & 0xff; + rtp[7] = rtpTime & 0xff; + + ++mNumRTPSent; + mNumRTPOctetsSent += rtpPacketSize - 12; + + mLastRTPTime = rtpTime; + + if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kWhatBinaryData); + + sp<ABuffer> data = new ABuffer(rtpPacketSize); + memcpy(data->data(), rtp, rtpPacketSize); + + notify->setInt32("channel", mRTPChannel); + notify->setBuffer("data", data); + notify->post(); + } else { + sendPacket(mRTPSessionID, rtp, rtpPacketSize); + +#if TRACK_BANDWIDTH + mTotalBytesSent += rtpPacketSize->size(); + int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs; + + if (delayUs > 0ll) { + ALOGI("approx. net bandwidth used: %.2f Mbit/sec", + mTotalBytesSent * 8.0 / delayUs); + } +#endif + } + +#if ENABLE_RETRANSMISSION + addToHistory(rtp, rtpPacketSize); +#endif + + srcOffset += rtpPacketSize; + } + +#if 0 + int64_t timeUs; + CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs)); + + ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs); +#endif +} + +#if ENABLE_RETRANSMISSION +void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) { + sp<ABuffer> packet = new ABuffer(rtpPacketSize); + memcpy(packet->data(), rtp, rtpPacketSize); + + unsigned rtpSeqNo = U16_AT(&rtp[2]); + packet->setInt32Data(rtpSeqNo); + + mHistory.push_back(packet); + ++mHistoryLength; + + if (mHistoryLength > kMaxHistoryLength) { + mHistory.erase(mHistory.begin()); + --mHistoryLength; + } +} +#endif + } // namespace android diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h index e476e84..66951f7 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 1 + +// If retransmission is enabled the following define determines what +// kind we support, if RETRANSMISSION_ACCORDING_TO_RFC_XXXX is 0 +// we'll send NACKs on the original RTCP channel and retransmit packets +// on the original RTP channel, otherwise a separate channel pair is used +// for this purpose. +#define RETRANSMISSION_ACCORDING_TO_RFC_XXXX 0 + struct ABuffer; struct ANetworkSession; @@ -51,7 +59,7 @@ struct Sender : public AHandler { int32_t getRTPPort() const; - void queuePackets(int64_t timeUs, const sp<ABuffer> &packets); + void queuePackets(int64_t timeUs, const sp<ABuffer> &tsPackets); void scheduleSendSR(); protected: @@ -60,13 +68,13 @@ protected: private: enum { - kWhatQueuePackets, + kWhatDrainQueue, kWhatSendSR, kWhatRTPNotify, kWhatRTCPNotify, -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX kWhatRTPRetransmissionNotify, - kWhatRTCPRetransmissionNotify + kWhatRTCPRetransmissionNotify, #endif }; @@ -75,15 +83,13 @@ private: static const uint32_t kSourceID = 0xdeadbeef; static const size_t kMaxHistoryLength = 128; -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX static const size_t kRetransmissionPortOffset = 120; #endif sp<ANetworkSession> mNetSession; sp<AMessage> mNotify; - sp<ABuffer> mTSQueue; - TransportMode mTransportMode; AString mClientIP; @@ -96,7 +102,7 @@ private: int32_t mRTPSessionID; int32_t mRTCPSessionID; -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX int32_t mRTPRetransmissionSessionID; int32_t mRTCPRetransmissionSessionID; #endif @@ -106,12 +112,11 @@ private: bool mRTPConnected; bool mRTCPConnected; - int64_t mFirstOutputBufferReadyTimeUs; int64_t mFirstOutputBufferSentTimeUs; uint32_t mRTPSeqNo; -#if ENABLE_RETRANSMISSION +#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX uint32_t mRTPRetransmissionSeqNo; #endif @@ -133,22 +138,18 @@ private: uint64_t mTotalBytesSent; #endif - void onSendSR(); - void addSR(const sp<ABuffer> &buffer); - void addSDES(const sp<ABuffer> &buffer); - static uint64_t GetNowNTP(); - #if LOG_TRANSPORT_STREAM FILE *mLogFile; #endif - ssize_t appendTSData( - const void *data, size_t size, bool timeDiscontinuity, bool flush); - - void onQueuePackets(const sp<ABuffer> &packets); + void onSendSR(); + void addSR(const sp<ABuffer> &buffer); + void addSDES(const sp<ABuffer> &buffer); + static uint64_t GetNowNTP(); #if ENABLE_RETRANSMISSION status_t parseTSFB(const uint8_t *data, size_t size); + void addToHistory(const uint8_t *rtp, size_t rtpPacketSize); #endif status_t parseRTCP(const sp<ABuffer> &buffer); @@ -158,6 +159,8 @@ private: void notifyInitDone(); void notifySessionDead(); + void onDrainQueue(const sp<ABuffer> &udpPackets); + DISALLOW_EVIL_CONSTRUCTORS(Sender); }; diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index a5679ad..ef57a4d 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -314,6 +314,25 @@ void TSPacketizer::Track::finalize() { mDescriptors.push_back(descriptor); } + int32_t hdcpVersion; + if (mFormat->findInt32("hdcp-version", &hdcpVersion)) { + // HDCP descriptor + + CHECK(hdcpVersion == 0x20 || hdcpVersion == 0x21); + + sp<ABuffer> descriptor = new ABuffer(7); + uint8_t *data = descriptor->data(); + data[0] = 0x05; // descriptor_tag + data[1] = 5; // descriptor_length + data[2] = 'H'; + data[3] = 'D'; + data[4] = 'C'; + data[5] = 'P'; + data[6] = hdcpVersion; + + mDescriptors.push_back(descriptor); + } + mFinalized = true; } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b16c5d0..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<AMessage> msg = new AMessage(kWhatStart, id()); - msg->setString("iface", iface); - - sp<AMessage> response; - status_t err = msg->postAndAwaitResponse(&response); +static status_t PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response) { + status_t err = msg->postAndAwaitResponse(response); if (err != OK) { return err; } - if (!response->findInt32("err", &err)) { + if (response == NULL || !(*response)->findInt32("err", &err)) { err = OK; } return err; } +status_t WifiDisplaySource::start(const char *iface) { + CHECK_EQ(mState, INITIALIZED); + + sp<AMessage> msg = new AMessage(kWhatStart, id()); + msg->setString("iface", iface); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + status_t WifiDisplaySource::stop() { sp<AMessage> msg = new AMessage(kWhatStop, id()); sp<AMessage> response; - status_t err = msg->postAndAwaitResponse(&response); + return PostAndAwaitResponse(msg, &response); +} - if (err != OK) { - return err; - } +status_t WifiDisplaySource::pause() { + sp<AMessage> msg = new AMessage(kWhatPause, id()); - if (!response->findInt32("err", &err)) { - err = OK; - } + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} - return err; +status_t WifiDisplaySource::resume() { + sp<AMessage> msg = new AMessage(kWhatResume, id()); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); } void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { @@ -236,6 +245,20 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { mClient->onDisplayError( IRemoteDisplayClient::kDisplayErrorUnknown); } + +#if 0 + // testing only. + char val[PROPERTY_VALUE_MAX]; + if (property_get("media.wfd.trigger", val, NULL)) { + if (!strcasecmp(val, "pause") && mState == PLAYING) { + mState = PLAYING_TO_PAUSED; + sendTrigger(mClientSessionID, TRIGGER_PAUSE); + } else if (!strcasecmp(val, "play") && mState == PAUSED) { + mState = PAUSED_TO_PLAYING; + sendTrigger(mClientSessionID, TRIGGER_PLAY); + } + } +#endif break; } @@ -254,8 +277,8 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (mState >= AWAITING_CLIENT_PLAY) { // We have a session, i.e. a previous SETUP succeeded. - status_t err = sendM5( - mClientSessionID, true /* requestShutdown */); + status_t err = sendTrigger( + mClientSessionID, TRIGGER_TEARDOWN); if (err == OK) { mState = AWAITING_CLIENT_TEARDOWN; @@ -273,6 +296,46 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatPause: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = OK; + + if (mState != PLAYING) { + err = INVALID_OPERATION; + } else { + mState = PLAYING_TO_PAUSED; + sendTrigger(mClientSessionID, TRIGGER_PAUSE); + } + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatResume: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + status_t err = OK; + + if (mState != PAUSED) { + err = INVALID_OPERATION; + } else { + mState = PAUSED_TO_PLAYING; + sendTrigger(mClientSessionID, TRIGGER_PLAY); + } + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + case kWhatReapDeadClients: { mReaperPending = false; @@ -400,7 +463,7 @@ void WifiDisplaySource::onMessageReceived(const sp<AMessage> &msg) { if (mSetupTriggerDeferred) { mSetupTriggerDeferred = false; - sendM5(mClientSessionID, false /* requestShutdown */); + sendTrigger(mClientSessionID, TRIGGER_SETUP); } break; } @@ -534,9 +597,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", @@ -568,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"); @@ -773,8 +854,10 @@ status_t WifiDisplaySource::onReceiveM3Response( status_t err = makeHDCP(); if (err != OK) { - ALOGE("Unable to instantiate HDCP component."); - return err; + ALOGE("Unable to instantiate HDCP component. " + "Not using HDCP after all."); + + mUsingHDCP = false; } } @@ -799,7 +882,7 @@ status_t WifiDisplaySource::onReceiveM4Response( return OK; } - return sendM5(sessionID, false /* requestShutdown */); + return sendTrigger(sessionID, TRIGGER_SETUP); } status_t WifiDisplaySource::onReceiveM5Response( @@ -1176,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); @@ -1197,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); @@ -1206,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 02fa0a6..974e070 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; @@ -42,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<AMessage> &msg); @@ -57,6 +62,9 @@ private: AWAITING_CLIENT_PLAY, ABOUT_TO_PLAY, PLAYING, + PLAYING_TO_PAUSED, + PAUSED, + PAUSED_TO_PLAYING, AWAITING_CLIENT_TEARDOWN, STOPPING, STOPPED, @@ -66,6 +74,8 @@ private: kWhatStart, kWhatRTSPNotify, kWhatStop, + kWhatPause, + kWhatResume, kWhatReapDeadClients, kWhatPlaybackSessionNotify, kWhatKeepAlive, @@ -145,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( diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp index 011edab..03a1123 100644 --- a/media/libstagefright/wifi-display/wfd.cpp +++ b/media/libstagefright/wifi-display/wfd.cpp @@ -23,22 +23,163 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <gui/SurfaceComposerClient.h> +#include <media/AudioSystem.h> #include <media/IMediaPlayerService.h> +#include <media/IRemoteDisplay.h> +#include <media/IRemoteDisplayClient.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/foundation/ADebug.h> namespace android { -} // namespace android - static void usage(const char *me) { fprintf(stderr, "usage:\n" " %s -c host[:port]\tconnect to wifi source\n" - " -u uri \tconnect to an rtsp uri\n", + " -u uri \tconnect to an rtsp uri\n" + " -l ip[:port] \tlisten on the specified port " + "(create a sink)\n", me); } +struct RemoteDisplayClient : public BnRemoteDisplayClient { + RemoteDisplayClient(); + + virtual void onDisplayConnected( + const sp<ISurfaceTexture> &surfaceTexture, + uint32_t width, + uint32_t height, + uint32_t flags); + + virtual void onDisplayDisconnected(); + virtual void onDisplayError(int32_t error); + + void waitUntilDone(); + +protected: + virtual ~RemoteDisplayClient(); + +private: + Mutex mLock; + Condition mCondition; + + bool mDone; + + sp<SurfaceComposerClient> mComposerClient; + sp<ISurfaceTexture> mSurfaceTexture; + sp<IBinder> mDisplayBinder; + + DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient); +}; + +RemoteDisplayClient::RemoteDisplayClient() + : mDone(false) { + mComposerClient = new SurfaceComposerClient; + CHECK_EQ(mComposerClient->initCheck(), (status_t)OK); +} + +RemoteDisplayClient::~RemoteDisplayClient() { +} + +void RemoteDisplayClient::onDisplayConnected( + const sp<ISurfaceTexture> &surfaceTexture, + uint32_t width, + uint32_t height, + uint32_t flags) { + ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x", + width, height, flags); + + mSurfaceTexture = surfaceTexture; + mDisplayBinder = mComposerClient->createDisplay( + String8("foo"), false /* secure */); + + SurfaceComposerClient::openGlobalTransaction(); + mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture); + + Rect layerStackRect(1280, 720); // XXX fix this. + Rect displayRect(1280, 720); + + mComposerClient->setDisplayProjection( + mDisplayBinder, 0 /* 0 degree rotation */, + layerStackRect, + displayRect); + + SurfaceComposerClient::closeGlobalTransaction(); +} + +void RemoteDisplayClient::onDisplayDisconnected() { + ALOGI("onDisplayDisconnected"); + + Mutex::Autolock autoLock(mLock); + mDone = true; + mCondition.broadcast(); +} + +void RemoteDisplayClient::onDisplayError(int32_t error) { + ALOGI("onDisplayError error=%d", error); + + Mutex::Autolock autoLock(mLock); + mDone = true; + mCondition.broadcast(); +} + +void RemoteDisplayClient::waitUntilDone() { + Mutex::Autolock autoLock(mLock); + while (!mDone) { + mCondition.wait(mLock); + } +} + +static status_t enableAudioSubmix(bool enable) { + status_t err = AudioSystem::setDeviceConnectionState( + AUDIO_DEVICE_IN_REMOTE_SUBMIX, + enable + ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE + : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + NULL /* device_address */); + + if (err != OK) { + return err; + } + + err = AudioSystem::setDeviceConnectionState( + AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + enable + ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE + : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + NULL /* device_address */); + + return err; +} + +static void createSource(const AString &addr, int32_t port) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("media.player")); + sp<IMediaPlayerService> service = + interface_cast<IMediaPlayerService>(binder); + + CHECK(service.get() != NULL); + + enableAudioSubmix(true /* enable */); + + String8 iface; + iface.append(addr.c_str()); + iface.append(StringPrintf(":%d", port).c_str()); + + sp<RemoteDisplayClient> client = new RemoteDisplayClient; + sp<IRemoteDisplay> display = service->listenForRemoteDisplay(client, iface); + + client->waitUntilDone(); + + display->dispose(); + display.clear(); + + enableAudioSubmix(false /* enable */); +} + +} // namespace android + int main(int argc, char **argv) { using namespace android; @@ -50,6 +191,9 @@ int main(int argc, char **argv) { int32_t connectToPort = -1; AString uri; + AString listenOnAddr; + int32_t listenOnPort = -1; + int res; while ((res = getopt(argc, argv, "hc:l:u:")) >= 0) { switch (res) { @@ -81,6 +225,28 @@ int main(int argc, char **argv) { break; } + case 'l': + { + const char *colonPos = strrchr(optarg, ':'); + + if (colonPos == NULL) { + listenOnAddr = optarg; + listenOnPort = WifiDisplaySource::kWifiDisplayDefaultPort; + } else { + listenOnAddr.setTo(optarg, colonPos - optarg); + + char *end; + listenOnPort = strtol(colonPos + 1, &end, 10); + + if (*end != '\0' || end == colonPos + 1 + || listenOnPort < 1 || listenOnPort > 65535) { + fprintf(stderr, "Illegal port specified.\n"); + exit(1); + } + } + break; + } + case '?': case 'h': default: @@ -89,6 +255,18 @@ int main(int argc, char **argv) { } } + if (connectToPort >= 0 && listenOnPort >= 0) { + fprintf(stderr, + "You can connect to a source or create one, " + "but not both at the same time.\n"); + exit(1); + } + + if (listenOnPort >= 0) { + createSource(listenOnAddr, listenOnPort); + exit(0); + } + if (connectToPort < 0 && uri.empty()) { fprintf(stderr, "You need to select either source host or uri.\n"); 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<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); |