From 20a5a98667df7ed83e5d9f37e88b1bbdb876d718 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 4 Oct 2012 11:46:29 -0700 Subject: Optionally emit LPCM audio instead of using AAC in wifi display code related-to-bug: 7248248 May decrease power usage at the cost of significantly increasing audio bitrate. Use "adb shell setprop media.wfd.use-pcm-audio true" to turn it on (must be done before connecting). Change-Id: I7ebeadf3209e01522a2644948287b23d7c383c7e --- .../wifi-display/source/Converter.cpp | 154 +++++++++++++++++++-- .../libstagefright/wifi-display/source/Converter.h | 13 +- .../wifi-display/source/PlaybackSession.cpp | 24 ++-- .../wifi-display/source/PlaybackSession.h | 8 +- .../wifi-display/source/TSPacketizer.cpp | 11 +- .../wifi-display/source/WifiDisplaySource.cpp | 14 +- .../wifi-display/source/WifiDisplaySource.h | 3 +- 7 files changed, 200 insertions(+), 27 deletions(-) diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp index 0e8c9af..a4a6f07 100644 --- a/media/libstagefright/wifi-display/source/Converter.cpp +++ b/media/libstagefright/wifi-display/source/Converter.cpp @@ -38,12 +38,14 @@ namespace android { Converter::Converter( const sp ¬ify, const sp &codecLooper, - const sp &format) + const sp &format, + bool usePCMAudio) : mInitCheck(NO_INIT), mNotify(notify), mCodecLooper(codecLooper), mInputFormat(format), mIsVideo(false), + mIsPCMAudio(usePCMAudio), mDoMoreWorkPending(false) #if ENABLE_SILENCE_DETECTION ,mFirstSilentFrameUs(-1ll) @@ -57,6 +59,8 @@ Converter::Converter( mIsVideo = true; } + CHECK(!usePCMAudio || !mIsVideo); + mInitCheck = initEncoder(); if (mInitCheck != OK) { @@ -109,7 +113,11 @@ status_t Converter::initEncoder() { AString outputMIME; bool isAudio = false; if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) { - outputMIME = MEDIA_MIMETYPE_AUDIO_AAC; + if (mIsPCMAudio) { + outputMIME = MEDIA_MIMETYPE_AUDIO_RAW; + } else { + outputMIME = MEDIA_MIMETYPE_AUDIO_AAC; + } isAudio = true; } else if (!strcasecmp(inputMIME.c_str(), MEDIA_MIMETYPE_VIDEO_RAW)) { outputMIME = MEDIA_MIMETYPE_VIDEO_AVC; @@ -117,14 +125,21 @@ status_t Converter::initEncoder() { TRESPASS(); } - mEncoder = MediaCodec::CreateByType( - mCodecLooper, outputMIME.c_str(), true /* encoder */); + if (!mIsPCMAudio) { + mEncoder = MediaCodec::CreateByType( + mCodecLooper, outputMIME.c_str(), true /* encoder */); - if (mEncoder == NULL) { - return ERROR_UNSUPPORTED; + if (mEncoder == NULL) { + return ERROR_UNSUPPORTED; + } } mOutputFormat = mInputFormat->dup(); + + if (mIsPCMAudio) { + return OK; + } + mOutputFormat->setString("mime", outputMIME.c_str()); int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000); @@ -197,7 +212,7 @@ void Converter::onMessageReceived(const sp &msg) { int32_t what; CHECK(msg->findInt32("what", &what)); - if (mEncoder == NULL) { + if (!mIsPCMAudio && mEncoder == NULL) { ALOGV("got msg '%s' after encoder shutdown.", msg->debugString().c_str()); @@ -317,8 +332,11 @@ void Converter::onMessageReceived(const sp &msg) { case kWhatShutdown: { ALOGI("shutting down encoder"); - mEncoder->release(); - mEncoder.clear(); + + if (mEncoder != NULL) { + mEncoder->release(); + mEncoder.clear(); + } AString mime; CHECK(mInputFormat->findString("mime", &mime)); @@ -332,6 +350,11 @@ void Converter::onMessageReceived(const sp &msg) { } void Converter::scheduleDoMoreWork() { + if (mIsPCMAudio) { + // There's no encoder involved in this case. + return; + } + if (mDoMoreWorkPending) { return; } @@ -350,7 +373,120 @@ void Converter::scheduleDoMoreWork() { #endif } +status_t Converter::feedRawAudioInputBuffers() { + // Split incoming PCM audio into buffers of 6 AUs of 80 audio frames each + // and add a 4 byte header according to the wifi display specs. + + while (!mInputBufferQueue.empty()) { + sp buffer = *mInputBufferQueue.begin(); + mInputBufferQueue.erase(mInputBufferQueue.begin()); + + int16_t *ptr = (int16_t *)buffer->data(); + int16_t *stop = (int16_t *)(buffer->data() + buffer->size()); + while (ptr < stop) { + *ptr = htons(*ptr); + ++ptr; + } + + static const size_t kFrameSize = 2 * sizeof(int16_t); // stereo + static const size_t kFramesPerAU = 80; + static const size_t kNumAUsPerPESPacket = 6; + + if (mPartialAudioAU != NULL) { + size_t bytesMissingForFullAU = + kNumAUsPerPESPacket * kFramesPerAU * kFrameSize + - mPartialAudioAU->size() + 4; + + size_t copy = buffer->size(); + if(copy > bytesMissingForFullAU) { + copy = bytesMissingForFullAU; + } + + memcpy(mPartialAudioAU->data() + mPartialAudioAU->size(), + buffer->data(), + copy); + + mPartialAudioAU->setRange(0, mPartialAudioAU->size() + copy); + + buffer->setRange(buffer->offset() + copy, buffer->size() - copy); + + int64_t timeUs; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0); + timeUs += copyUs; + buffer->meta()->setInt64("timeUs", timeUs); + + if (bytesMissingForFullAU == copy) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatAccessUnit); + notify->setBuffer("accessUnit", mPartialAudioAU); + notify->post(); + + mPartialAudioAU.clear(); + } + } + + while (buffer->size() > 0) { + sp partialAudioAU = + new ABuffer( + 4 + + kNumAUsPerPESPacket * kFrameSize * kFramesPerAU); + + uint8_t *ptr = partialAudioAU->data(); + ptr[0] = 0xa0; // 10100000b + ptr[1] = kNumAUsPerPESPacket; + ptr[2] = 0; // reserved, audio _emphasis_flag = 0 + + static const unsigned kQuantizationWordLength = 0; // 16-bit + static const unsigned kAudioSamplingFrequency = 2; // 48Khz + static const unsigned kNumberOfAudioChannels = 1; // stereo + + ptr[3] = (kQuantizationWordLength << 6) + | (kAudioSamplingFrequency << 3) + | kNumberOfAudioChannels; + + size_t copy = buffer->size(); + if (copy > partialAudioAU->size() - 4) { + copy = partialAudioAU->size() - 4; + } + + memcpy(&ptr[4], buffer->data(), copy); + + partialAudioAU->setRange(0, 4 + copy); + buffer->setRange(buffer->offset() + copy, buffer->size() - copy); + + int64_t timeUs; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + partialAudioAU->meta()->setInt64("timeUs", timeUs); + + int64_t copyUs = (int64_t)((copy / kFrameSize) * 1E6 / 48000.0); + timeUs += copyUs; + buffer->meta()->setInt64("timeUs", timeUs); + + if (copy == partialAudioAU->size() - 4) { + sp notify = mNotify->dup(); + notify->setInt32("what", kWhatAccessUnit); + notify->setBuffer("accessUnit", partialAudioAU); + notify->post(); + + partialAudioAU.clear(); + continue; + } + + mPartialAudioAU = partialAudioAU; + } + } + + return OK; +} + status_t Converter::feedEncoderInputBuffers() { + if (mIsPCMAudio) { + return feedRawAudioInputBuffers(); + } + while (!mInputBufferQueue.empty() && !mAvailEncoderInputIndices.empty()) { sp buffer = *mInputBufferQueue.begin(); diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h index 8d45395..8dfff3d 100644 --- a/media/libstagefright/wifi-display/source/Converter.h +++ b/media/libstagefright/wifi-display/source/Converter.h @@ -18,6 +18,8 @@ #define CONVERTER_H_ +#include "WifiDisplaySource.h" + #include namespace android { @@ -34,7 +36,8 @@ struct Converter : public AHandler { Converter( const sp ¬ify, const sp &codecLooper, - const sp &format); + const sp &format, + bool usePCMAudio); status_t initCheck() const; @@ -73,6 +76,7 @@ private: sp mCodecLooper; sp mInputFormat; bool mIsVideo; + bool mIsPCMAudio; sp mOutputFormat; sp mEncoder; @@ -92,6 +96,8 @@ private: bool mInSilentMode; #endif + sp mPartialAudioAU; + status_t initEncoder(); status_t feedEncoderInputBuffers(); @@ -101,6 +107,11 @@ private: void notifyError(status_t err); + // Packetizes raw PCM audio data available in mInputBufferQueue + // into a format suitable for transport stream inclusion and + // notifies the observer. + status_t feedRawAudioInputBuffers(); + static bool IsSilence(const sp &accessUnit); DISALLOW_EVIL_CONSTRUCTORS(Converter); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp index ffdafed..6ef5e40 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp +++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp @@ -312,10 +312,11 @@ WifiDisplaySource::PlaybackSession::PlaybackSession( status_t WifiDisplaySource::PlaybackSession::init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - TransportMode transportMode) { + TransportMode transportMode, + bool usePCMAudio) { mClientIP = clientIP; - status_t err = setupPacketizer(); + status_t err = setupPacketizer(usePCMAudio); if (err != OK) { return err; @@ -823,7 +824,7 @@ void WifiDisplaySource::PlaybackSession::onMessageReceived( } } -status_t WifiDisplaySource::PlaybackSession::setupPacketizer() { +status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) { mPacketizer = new TSPacketizer; status_t err = addVideoSource(); @@ -832,12 +833,15 @@ status_t WifiDisplaySource::PlaybackSession::setupPacketizer() { return err; } - return addAudioSource(); + return addAudioSource(usePCMAudio); } status_t WifiDisplaySource::PlaybackSession::addSource( bool isVideo, const sp &source, bool isRepeaterSource, - size_t *numInputBuffers) { + bool usePCMAudio, size_t *numInputBuffers) { + CHECK(!usePCMAudio || !isVideo); + CHECK(!isRepeaterSource || isVideo); + sp pullLooper = new ALooper; pullLooper->setName("pull_looper"); @@ -875,7 +879,7 @@ status_t WifiDisplaySource::PlaybackSession::addSource( notify->setSize("trackIndex", trackIndex); sp converter = - new Converter(notify, codecLooper, format); + new Converter(notify, codecLooper, format, usePCMAudio); if (converter->initCheck() != OK) { return converter->initCheck(); @@ -928,12 +932,12 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource() { size_t numInputBuffers; status_t err = addSource( true /* isVideo */, videoSource, true /* isRepeaterSource */, - &numInputBuffers); + false /* usePCMAudio */, &numInputBuffers); #else size_t numInputBuffers; status_t err = addSource( true /* isVideo */, source, false /* isRepeaterSource */, - &numInputBuffers); + false /* usePCMAudio */, &numInputBuffers); #endif if (err != OK) { @@ -948,7 +952,7 @@ status_t WifiDisplaySource::PlaybackSession::addVideoSource() { return OK; } -status_t WifiDisplaySource::PlaybackSession::addAudioSource() { +status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) { sp audioSource = new AudioSource( AUDIO_SOURCE_REMOTE_SUBMIX, 48000 /* sampleRate */, @@ -957,7 +961,7 @@ status_t WifiDisplaySource::PlaybackSession::addAudioSource() { if (audioSource->initCheck() == OK) { return addSource( false /* isVideo */, audioSource, false /* isRepeaterSource */, - NULL /* numInputBuffers */); + usePCMAudio, NULL /* numInputBuffers */); } ALOGW("Unable to instantiate audio source"); diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h index 5d4bde8..4bbc3f0 100644 --- a/media/libstagefright/wifi-display/source/PlaybackSession.h +++ b/media/libstagefright/wifi-display/source/PlaybackSession.h @@ -50,7 +50,8 @@ struct WifiDisplaySource::PlaybackSession : public AHandler { }; status_t init( const char *clientIP, int32_t clientRtp, int32_t clientRtcp, - TransportMode transportMode); + TransportMode transportMode, + bool usePCMAudio); void destroyAsync(); @@ -180,16 +181,17 @@ private: void addSDES(const sp &buffer); static uint64_t GetNowNTP(); - status_t setupPacketizer(); + status_t setupPacketizer(bool usePCMAudio); status_t addSource( bool isVideo, const sp &source, bool isRepeaterSource, + bool usePCMAudio, size_t *numInputBuffers); status_t addVideoSource(); - status_t addAudioSource(); + status_t addAudioSource(bool usePCMAudio); ssize_t appendTSData( const void *data, size_t size, bool timeDiscontinuity, bool flush); diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp index e5abd57..7e66072 100644 --- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp +++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp @@ -47,6 +47,7 @@ struct TSPacketizer::Track : public RefBase { bool isVideo() const; bool isH264() const; + bool isAAC() const; bool lacksADTSHeader() const; sp prependCSD(const sp &accessUnit) const; @@ -139,6 +140,10 @@ bool TSPacketizer::Track::isH264() const { return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC); } +bool TSPacketizer::Track::isAAC() const { + return !strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC); +} + bool TSPacketizer::Track::lacksADTSHeader() const { return mAudioLacksATDSHeaders; } @@ -247,6 +252,10 @@ ssize_t TSPacketizer::addTrack(const sp &format) { streamType = 0x0f; streamIDStart = 0xc0; streamIDStop = 0xdf; + } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_RAW)) { + streamType = 0x83; + streamIDStart = 0xbd; + streamIDStop = 0xbd; } else { return ERROR_UNSUPPORTED; } @@ -298,7 +307,7 @@ status_t TSPacketizer::packetize( && IsIDR(accessUnit)) { // prepend codec specific data, i.e. SPS and PPS. accessUnit = track->prependCSD(accessUnit); - } else if (track->isAudio() && track->lacksADTSHeader()) { + } else if (track->isAAC() && track->lacksADTSHeader()) { CHECK(!(flags & IS_ENCRYPTED)); accessUnit = track->prependADTSHeader(accessUnit); } diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp index b0aaf3b..d856bac 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp @@ -46,6 +46,7 @@ WifiDisplaySource::WifiDisplaySource( mClient(client), mSessionID(0), mStopReplyID(0), + mUsingPCMAudio(false), mClientSessionID(0), mReaperPending(false), mNextCSeq(1) @@ -531,6 +532,11 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { transportString = "TCP"; } + if (property_get("media.wfd.use-pcm-audio", val, NULL) + && (!strcasecmp("true", val) || !strcmp("1", val))) { + ALOGI("Using PCM audio."); + mUsingPCMAudio = true; + } // For 720p60: // use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n" // For 720p30: @@ -540,9 +546,12 @@ status_t WifiDisplaySource::sendM4(int32_t sessionID) { AString body = StringPrintf( "wfd_video_formats: " "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n" - "wfd_audio_codecs: AAC 00000001 00\r\n" // 2 ch AAC 48kHz + "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 19000 0 mode=play\r\n", + (mUsingPCMAudio + ? "LPCM 00000002 00" // 2 ch PCM 48kHz + : "AAC 00000001 00"), // 2 ch AAC 48kHz mClientInfo.mLocalIP.c_str(), transportString.c_str()); AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n"; @@ -1000,7 +1009,8 @@ status_t WifiDisplaySource::onSetupRequest( mClientInfo.mRemoteIP.c_str(), clientRtp, clientRtcp, - transportMode); + transportMode, + mUsingPCMAudio); if (err != OK) { looper()->unregisterHandler(playbackSession->id()); diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h index c30e3cb..0692cde 100644 --- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h +++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h @@ -26,7 +26,7 @@ namespace android { -#define REQUIRE_HDCP 1 +#define REQUIRE_HDCP 1 struct IHDCP; struct IRemoteDisplayClient; @@ -114,6 +114,7 @@ private: uint32_t mStopReplyID; + bool mUsingPCMAudio; int32_t mClientSessionID; struct ClientInfo { -- cgit v1.1