summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorSteve Kondik <shade@chemlab.org>2013-02-12 22:30:27 -0800
committerSteve Kondik <shade@chemlab.org>2013-02-12 22:30:27 -0800
commit001a7b82e04646682170d4b946529ebc0b956c16 (patch)
treefb3784437f93380f21b6d69e9c819fc1305104da /media
parentd852697190e617c43930723bbb446375394b4194 (diff)
parentf0ff908da019a44115109f1b4d1b6864b35a8a29 (diff)
downloadframeworks_av-001a7b82e04646682170d4b946529ebc0b956c16.zip
frameworks_av-001a7b82e04646682170d4b946529ebc0b956c16.tar.gz
frameworks_av-001a7b82e04646682170d4b946529ebc0b956c16.tar.bz2
Merge tag 'android-4.2.2_r1' of https://android.googlesource.com/platform/frameworks/av into 1.1
Android 4.2.2 release 1
Diffstat (limited to 'media')
-rw-r--r--media/libmedia/IRemoteDisplay.cpp31
-rw-r--r--media/libmedia/IStreamSource.cpp3
-rw-r--r--media/libmedia/mediaplayer.cpp36
-rw-r--r--media/libmediaplayerservice/RemoteDisplay.cpp8
-rw-r--r--media/libmediaplayerservice/RemoteDisplay.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp28
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp49
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h9
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp20
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.cpp4
-rw-r--r--media/libmediaplayerservice/nuplayer/mp4/MP4Source.h2
-rw-r--r--media/libstagefright/ACodec.cpp57
-rw-r--r--media/libstagefright/MP3Extractor.cpp6
-rw-r--r--media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp7
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp92
-rw-r--r--media/libstagefright/httplive/M3UParser.cpp9
-rw-r--r--media/libstagefright/id3/ID3.cpp4
-rw-r--r--media/libstagefright/include/ID3.h2
-rw-r--r--media/libstagefright/include/LiveSession.h14
-rw-r--r--media/libstagefright/include/M3UParser.h2
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp16
-rw-r--r--media/libstagefright/wifi-display/ANetworkSession.cpp21
-rw-r--r--media/libstagefright/wifi-display/Android.mk1
-rw-r--r--media/libstagefright/wifi-display/TimeSeries.cpp67
-rw-r--r--media/libstagefright/wifi-display/TimeSeries.h46
-rw-r--r--media/libstagefright/wifi-display/source/Converter.cpp65
-rw-r--r--media/libstagefright/wifi-display/source/Converter.h2
-rw-r--r--media/libstagefright/wifi-display/source/MediaPuller.cpp34
-rw-r--r--media/libstagefright/wifi-display/source/MediaPuller.h6
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.cpp112
-rw-r--r--media/libstagefright/wifi-display/source/PlaybackSession.h5
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.cpp5
-rw-r--r--media/libstagefright/wifi-display/source/RepeaterSource.h2
-rw-r--r--media/libstagefright/wifi-display/source/Sender.cpp415
-rw-r--r--media/libstagefright/wifi-display/source/Sender.h43
-rw-r--r--media/libstagefright/wifi-display/source/TSPacketizer.cpp19
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.cpp158
-rw-r--r--media/libstagefright/wifi-display/source/WifiDisplaySource.h22
-rw-r--r--media/libstagefright/wifi-display/wfd.cpp184
-rw-r--r--media/mediaserver/main_mediaserver.cpp1
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 e86bf63..19bf28a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -978,7 +978,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);
@@ -1027,6 +1028,10 @@ status_t ACodec::configureCodec(
}
}
+ if (err != OK) {
+ return err;
+ }
+
if (!msg->findInt32("encoder-delay", &mEncoderDelay)) {
mEncoderDelay = 0;
}
@@ -1666,6 +1671,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(&params);
+ 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,
+ &params, sizeof(params));
+ return err;
+}
+
static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
if (iFramesInterval < 0) {
return 0xFFFFFFFF;
@@ -1861,11 +1903,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 07160f2..b3ec4d6 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> &notify) {
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> &notify);
+ 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> &notify)
: mNetSession(netSession),
mNotify(notify),
- mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
mTransportMode(TRANSPORT_UDP),
mRTPChannel(0),
mRTCPChannel(0),
mRTPPort(0),
mRTPSessionID(0),
mRTCPSessionID(0),
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
mRTPRetransmissionSessionID(0),
mRTCPRetransmissionSessionID(0),
#endif
@@ -128,7 +57,7 @@ Sender::Sender(
mFirstOutputBufferReadyTimeUs(-1ll),
mFirstOutputBufferSentTimeUs(-1ll),
mRTPSeqNo(0),
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
mRTPRetransmissionSeqNo(0),
#endif
mLastNTPTime(0),
@@ -148,15 +77,13 @@ Sender::Sender(
,mLogFile(NULL)
#endif
{
- mTSQueue->setRange(0, 12);
-
#if LOG_TRANSPORT_STREAM
mLogFile = fopen("/system/etc/log.ts", "wb");
#endif
}
Sender::~Sender() {
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
if (mRTCPRetransmissionSessionID != 0) {
mNetSession->destroySession(mRTCPRetransmissionSessionID);
}
@@ -217,7 +144,7 @@ status_t Sender::init(
sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
sp<AMessage> rtpRetransmissionNotify =
new AMessage(kWhatRTPRetransmissionNotify, id());
@@ -264,7 +191,7 @@ status_t Sender::init(
}
}
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
if (mTransportMode == TRANSPORT_UDP) {
int32_t rtpRetransmissionSession;
@@ -358,44 +285,67 @@ int32_t Sender::getRTPPort() const {
}
void Sender::queuePackets(
- int64_t timeUs, const sp<ABuffer> &packets) {
- bool isVideo = false;
+ int64_t timeUs, const sp<ABuffer> &tsPackets) {
+ const size_t numTSPackets = tsPackets->size() / 188;
+
+ const size_t numRTPPackets =
+ (numTSPackets + kMaxNumTSPacketsPerRTPPacket - 1)
+ / kMaxNumTSPacketsPerRTPPacket;
+
+ sp<ABuffer> udpPackets = new ABuffer(
+ numRTPPackets * (12 + kMaxNumTSPacketsPerRTPPacket * 188));
+
+ udpPackets->meta()->setInt64("timeUs", timeUs);
+
+ size_t dstOffset = 0;
+ for (size_t i = 0; i < numTSPackets; ++i) {
+ if ((i % kMaxNumTSPacketsPerRTPPacket) == 0) {
+ static const bool kMarkerBit = false;
+
+ uint8_t *rtp = udpPackets->data() + dstOffset;
+ rtp[0] = 0x80;
+ rtp[1] = 33 | (kMarkerBit ? (1 << 7) : 0); // M-bit
+ rtp[2] = (mRTPSeqNo >> 8) & 0xff;
+ rtp[3] = mRTPSeqNo & 0xff;
+ rtp[4] = 0x00; // rtp time to be filled in later.
+ rtp[5] = 0x00;
+ rtp[6] = 0x00;
+ rtp[7] = 0x00;
+ rtp[8] = kSourceID >> 24;
+ rtp[9] = (kSourceID >> 16) & 0xff;
+ rtp[10] = (kSourceID >> 8) & 0xff;
+ rtp[11] = kSourceID & 0xff;
+
+ ++mRTPSeqNo;
+
+ dstOffset += 12;
+ }
+
+ memcpy(udpPackets->data() + dstOffset,
+ tsPackets->data() + 188 * i,
+ 188);
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
+ dstOffset += 188;
}
- int64_t delayUs;
- int64_t whenUs;
+ udpPackets->setRange(0, dstOffset);
- if (mFirstOutputBufferReadyTimeUs < 0ll) {
- mFirstOutputBufferReadyTimeUs = timeUs;
- mFirstOutputBufferSentTimeUs = whenUs = ALooper::GetNowUs();
- delayUs = 0ll;
- } else {
- int64_t nowUs = ALooper::GetNowUs();
-
- whenUs = (timeUs - mFirstOutputBufferReadyTimeUs)
- + mFirstOutputBufferSentTimeUs;
+ sp<AMessage> msg = new AMessage(kWhatDrainQueue, id());
+ msg->setBuffer("udpPackets", udpPackets);
+ msg->post();
- delayUs = whenUs - nowUs;
+#if LOG_TRANSPORT_STREAM
+ if (mLogFile != NULL) {
+ fwrite(tsPackets->data(), 1, tsPackets->size(), mLogFile);
}
-
- sp<AMessage> msg = new AMessage(kWhatQueuePackets, id());
- msg->setBuffer("packets", packets);
-
- packets->meta()->setInt64("timeUs", timeUs);
- packets->meta()->setInt64("whenUs", whenUs);
- packets->meta()->setInt64("delayUs", delayUs);
- msg->post(delayUs > 0 ? delayUs : 0);
+#endif
}
void Sender::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatRTPNotify:
case kWhatRTCPNotify:
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
case kWhatRTPRetransmissionNotify:
case kWhatRTCPRetransmissionNotify:
#endif
@@ -419,7 +369,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findString("detail", &detail));
if ((msg->what() == kWhatRTPNotify
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
|| msg->what() == kWhatRTPRetransmissionNotify
#endif
) && !errorOccuredDuringSend) {
@@ -443,7 +393,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
} else if (sessionID == mRTCPSessionID) {
mRTCPSessionID = 0;
}
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
else if (sessionID == mRTPRetransmissionSessionID) {
mRTPRetransmissionSessionID = 0;
} else if (sessionID == mRTCPRetransmissionSessionID) {
@@ -465,7 +415,7 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
status_t err;
if (msg->what() == kWhatRTCPNotify
-#if ENABLE_RETRANSMISSION
+#if ENABLE_RETRANSMISSION && RETRANSMISSION_ACCORDING_TO_RFC_XXXX
|| msg->what() == kWhatRTCPRetransmissionNotify
#endif
)
@@ -507,12 +457,12 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case kWhatQueuePackets:
+ case kWhatDrainQueue:
{
- sp<ABuffer> packets;
- CHECK(msg->findBuffer("packets", &packets));
+ sp<ABuffer> udpPackets;
+ CHECK(msg->findBuffer("udpPackets", &udpPackets));
- onQueuePackets(packets);
+ onDrainQueue(udpPackets);
break;
}
@@ -532,156 +482,6 @@ void Sender::onMessageReceived(const sp<AMessage> &msg) {
}
}
-void Sender::onQueuePackets(const sp<ABuffer> &packets) {
-#if DEBUG_JITTER
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- static int64_t lastTimeUs = 0ll;
- int64_t nowUs = ALooper::GetNowUs();
-
- static TimeSeries series;
- series.add((double)(nowUs - lastTimeUs));
-
- ALOGI("deltaTimeUs = %lld us, mean %.2f, sdev %.2f",
- nowUs - lastTimeUs, series.mean(), series.sdev());
-
- lastTimeUs = nowUs;
- }
-#endif
-
- int64_t startTimeUs = ALooper::GetNowUs();
-
- for (size_t offset = 0;
- offset < packets->size(); offset += 188) {
- bool lastTSPacket = (offset + 188 >= packets->size());
-
- appendTSData(
- packets->data() + offset,
- 188,
- true /* timeDiscontinuity */,
- lastTSPacket /* flush */);
- }
-
-#if 0
- int64_t netTimeUs = ALooper::GetNowUs() - startTimeUs;
-
- int64_t whenUs;
- CHECK(packets->meta()->findInt64("whenUs", &whenUs));
-
- int64_t delayUs;
- CHECK(packets->meta()->findInt64("delayUs", &delayUs));
-
- bool isVideo = false;
- int32_t dummy;
- if (packets->meta()->findInt32("isVideo", &dummy)) {
- isVideo = true;
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- if (nowUs - whenUs > 2000) {
- ALOGI("[%s] delayUs = %lld us, delta = %lld us",
- isVideo ? "video" : "audio", delayUs, nowUs - netTimeUs - whenUs);
- }
-#endif
-
-#if LOG_TRANSPORT_STREAM
- if (mLogFile != NULL) {
- fwrite(packets->data(), 1, packets->size(), mLogFile);
- }
-#endif
-}
-
-ssize_t Sender::appendTSData(
- const void *data, size_t size, bool timeDiscontinuity, bool flush) {
- CHECK_EQ(size, 188);
-
- CHECK_LE(mTSQueue->size() + size, mTSQueue->capacity());
-
- memcpy(mTSQueue->data() + mTSQueue->size(), data, size);
- mTSQueue->setRange(0, mTSQueue->size() + size);
-
- if (flush || mTSQueue->size() == mTSQueue->capacity()) {
- // flush
-
- int64_t nowUs = ALooper::GetNowUs();
-
-#if TRACK_BANDWIDTH
- if (mFirstPacketTimeUs < 0ll) {
- mFirstPacketTimeUs = nowUs;
- }
-#endif
-
- // 90kHz time scale
- uint32_t rtpTime = (nowUs * 9ll) / 100ll;
-
- uint8_t *rtp = mTSQueue->data();
- rtp[0] = 0x80;
- rtp[1] = 33 | (timeDiscontinuity ? (1 << 7) : 0); // M-bit
- rtp[2] = (mRTPSeqNo >> 8) & 0xff;
- rtp[3] = mRTPSeqNo & 0xff;
- rtp[4] = rtpTime >> 24;
- rtp[5] = (rtpTime >> 16) & 0xff;
- rtp[6] = (rtpTime >> 8) & 0xff;
- rtp[7] = rtpTime & 0xff;
- rtp[8] = kSourceID >> 24;
- rtp[9] = (kSourceID >> 16) & 0xff;
- rtp[10] = (kSourceID >> 8) & 0xff;
- rtp[11] = kSourceID & 0xff;
-
- ++mRTPSeqNo;
- ++mNumRTPSent;
- mNumRTPOctetsSent += mTSQueue->size() - 12;
-
- mLastRTPTime = rtpTime;
- mLastNTPTime = GetNowNTP();
-
- if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatBinaryData);
-
- sp<ABuffer> data = new ABuffer(mTSQueue->size());
- memcpy(data->data(), rtp, mTSQueue->size());
-
- notify->setInt32("channel", mRTPChannel);
- notify->setBuffer("data", data);
- notify->post();
- } else {
- sendPacket(mRTPSessionID, rtp, mTSQueue->size());
-
-#if TRACK_BANDWIDTH
- mTotalBytesSent += mTSQueue->size();
- int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
-
- if (delayUs > 0ll) {
- ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
- mTotalBytesSent * 8.0 / delayUs);
- }
-#endif
- }
-
-#if ENABLE_RETRANSMISSION
- mTSQueue->setInt32Data(mRTPSeqNo - 1);
-
- mHistory.push_back(mTSQueue);
- ++mHistoryLength;
-
- if (mHistoryLength > kMaxHistoryLength) {
- mTSQueue = *mHistory.begin();
- mHistory.erase(mHistory.begin());
-
- --mHistoryLength;
- } else {
- mTSQueue = new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188);
- }
-#endif
-
- mTSQueue->setRange(0, 12);
- }
-
- return size;
-}
-
void Sender::scheduleSendSR() {
if (mSendSRPending || mRTCPSessionID == 0) {
return;
@@ -851,6 +651,7 @@ status_t Sender::parseTSFB(
if (retransmit) {
ALOGI("retransmitting seqNo %d", bufferSeqNo);
+#if RETRANSMISSION_ACCORDING_TO_RFC_XXXX
sp<ABuffer> retransRTP = new ABuffer(2 + buffer->size());
uint8_t *rtp = retransRTP->data();
memcpy(rtp, buffer->data(), 12);
@@ -865,6 +666,10 @@ status_t Sender::parseTSFB(
sendPacket(
mRTPRetransmissionSessionID,
retransRTP->data(), retransRTP->size());
+#else
+ sendPacket(
+ mRTPSessionID, buffer->data(), buffer->size());
+#endif
if (bufferSeqNo == seqNo) {
foundSeqNo = true;
@@ -975,5 +780,91 @@ void Sender::notifySessionDead() {
notify->post();
}
+void Sender::onDrainQueue(const sp<ABuffer> &udpPackets) {
+ static const size_t kFullRTPPacketSize =
+ 12 + 188 * kMaxNumTSPacketsPerRTPPacket;
+
+ size_t srcOffset = 0;
+ while (srcOffset < udpPackets->size()) {
+ uint8_t *rtp = udpPackets->data() + srcOffset;
+
+ size_t rtpPacketSize = udpPackets->size() - srcOffset;
+ if (rtpPacketSize > kFullRTPPacketSize) {
+ rtpPacketSize = kFullRTPPacketSize;
+ }
+
+ int64_t nowUs = ALooper::GetNowUs();
+ mLastNTPTime = GetNowNTP();
+
+ // 90kHz time scale
+ uint32_t rtpTime = (nowUs * 9ll) / 100ll;
+
+ rtp[4] = rtpTime >> 24;
+ rtp[5] = (rtpTime >> 16) & 0xff;
+ rtp[6] = (rtpTime >> 8) & 0xff;
+ rtp[7] = rtpTime & 0xff;
+
+ ++mNumRTPSent;
+ mNumRTPOctetsSent += rtpPacketSize - 12;
+
+ mLastRTPTime = rtpTime;
+
+ if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatBinaryData);
+
+ sp<ABuffer> data = new ABuffer(rtpPacketSize);
+ memcpy(data->data(), rtp, rtpPacketSize);
+
+ notify->setInt32("channel", mRTPChannel);
+ notify->setBuffer("data", data);
+ notify->post();
+ } else {
+ sendPacket(mRTPSessionID, rtp, rtpPacketSize);
+
+#if TRACK_BANDWIDTH
+ mTotalBytesSent += rtpPacketSize->size();
+ int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
+
+ if (delayUs > 0ll) {
+ ALOGI("approx. net bandwidth used: %.2f Mbit/sec",
+ mTotalBytesSent * 8.0 / delayUs);
+ }
+#endif
+ }
+
+#if ENABLE_RETRANSMISSION
+ addToHistory(rtp, rtpPacketSize);
+#endif
+
+ srcOffset += rtpPacketSize;
+ }
+
+#if 0
+ int64_t timeUs;
+ CHECK(udpPackets->meta()->findInt64("timeUs", &timeUs));
+
+ ALOGI("dTimeUs = %lld us", ALooper::GetNowUs() - timeUs);
+#endif
+}
+
+#if ENABLE_RETRANSMISSION
+void Sender::addToHistory(const uint8_t *rtp, size_t rtpPacketSize) {
+ sp<ABuffer> packet = new ABuffer(rtpPacketSize);
+ memcpy(packet->data(), rtp, rtpPacketSize);
+
+ unsigned rtpSeqNo = U16_AT(&rtp[2]);
+ packet->setInt32Data(rtpSeqNo);
+
+ mHistory.push_back(packet);
+ ++mHistoryLength;
+
+ if (mHistoryLength > kMaxHistoryLength) {
+ mHistory.erase(mHistory.begin());
+ --mHistoryLength;
+ }
+}
+#endif
+
} // namespace android
diff --git a/media/libstagefright/wifi-display/source/Sender.h b/media/libstagefright/wifi-display/source/Sender.h
index e476e84..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 29e9e3d..eecc59b 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");
@@ -776,8 +857,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;
}
}
@@ -802,7 +885,7 @@ status_t WifiDisplaySource::onReceiveM4Response(
return OK;
}
- return sendM5(sessionID, false /* requestShutdown */);
+ return sendTrigger(sessionID, TRIGGER_SETUP);
}
status_t WifiDisplaySource::onReceiveM5Response(
@@ -1179,6 +1262,11 @@ status_t WifiDisplaySource::onPlayRequest(
return err;
}
+ if (mState == PAUSED_TO_PLAYING) {
+ mState = PLAYING;
+ return OK;
+ }
+
playbackSession->finishPlay();
CHECK_EQ(mState, AWAITING_CLIENT_PLAY);
@@ -1200,6 +1288,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);
@@ -1209,6 +1303,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 b8fddc4..993715e 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -39,6 +39,7 @@ void instantiate(void);
int main(int argc, char** argv)
{
+ signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());