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
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 @@
 Converter::Converter(
         const sp<AMessage> &notify,
         const sp<ALooper> &codecLooper,
-        const sp<AMessage> &format)
+        const sp<AMessage> &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 @@
         mIsVideo = true;
     }
 
+    CHECK(!usePCMAudio || !mIsVideo);
+
     mInitCheck = initEncoder();
 
     if (mInitCheck != OK) {
@@ -109,7 +113,11 @@
     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 @@
         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 @@
             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 @@
         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::scheduleDoMoreWork() {
+    if (mIsPCMAudio) {
+        // There's no encoder involved in this case.
+        return;
+    }
+
     if (mDoMoreWorkPending) {
         return;
     }
@@ -350,7 +373,120 @@
 #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<ABuffer> 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<AMessage> notify = mNotify->dup();
+                notify->setInt32("what", kWhatAccessUnit);
+                notify->setBuffer("accessUnit", mPartialAudioAU);
+                notify->post();
+
+                mPartialAudioAU.clear();
+            }
+        }
+
+        while (buffer->size() > 0) {
+            sp<ABuffer> 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<AMessage> 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<ABuffer> 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 <media/stagefright/foundation/AHandler.h>
 
 namespace android {
@@ -34,7 +36,8 @@
     Converter(
             const sp<AMessage> &notify,
             const sp<ALooper> &codecLooper,
-            const sp<AMessage> &format);
+            const sp<AMessage> &format,
+            bool usePCMAudio);
 
     status_t initCheck() const;
 
@@ -73,6 +76,7 @@
     sp<ALooper> mCodecLooper;
     sp<AMessage> mInputFormat;
     bool mIsVideo;
+    bool mIsPCMAudio;
     sp<AMessage> mOutputFormat;
 
     sp<MediaCodec> mEncoder;
@@ -92,6 +96,8 @@
     bool mInSilentMode;
 #endif
 
+    sp<ABuffer> mPartialAudioAU;
+
     status_t initEncoder();
 
     status_t feedEncoderInputBuffers();
@@ -101,6 +107,11 @@
 
     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<ABuffer> &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 @@
 
 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 @@
     }
 }
 
-status_t WifiDisplaySource::PlaybackSession::setupPacketizer() {
+status_t WifiDisplaySource::PlaybackSession::setupPacketizer(bool usePCMAudio) {
     mPacketizer = new TSPacketizer;
 
     status_t err = addVideoSource();
@@ -832,12 +833,15 @@
         return err;
     }
 
-    return addAudioSource();
+    return addAudioSource(usePCMAudio);
 }
 
 status_t WifiDisplaySource::PlaybackSession::addSource(
         bool isVideo, const sp<MediaSource> &source, bool isRepeaterSource,
-        size_t *numInputBuffers) {
+        bool usePCMAudio, size_t *numInputBuffers) {
+    CHECK(!usePCMAudio || !isVideo);
+    CHECK(!isRepeaterSource || isVideo);
+
     sp<ALooper> pullLooper = new ALooper;
     pullLooper->setName("pull_looper");
 
@@ -875,7 +879,7 @@
     notify->setSize("trackIndex", trackIndex);
 
     sp<Converter> converter =
-        new Converter(notify, codecLooper, format);
+        new Converter(notify, codecLooper, format, usePCMAudio);
 
     if (converter->initCheck() != OK) {
         return converter->initCheck();
@@ -928,12 +932,12 @@
     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 @@
     return OK;
 }
 
-status_t WifiDisplaySource::PlaybackSession::addAudioSource() {
+status_t WifiDisplaySource::PlaybackSession::addAudioSource(bool usePCMAudio) {
     sp<AudioSource> audioSource = new AudioSource(
             AUDIO_SOURCE_REMOTE_SUBMIX,
             48000 /* sampleRate */,
@@ -957,7 +961,7 @@
     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 @@
     };
     status_t init(
             const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            TransportMode transportMode);
+            TransportMode transportMode,
+            bool usePCMAudio);
 
     void destroyAsync();
 
@@ -180,16 +181,17 @@
     void addSDES(const sp<ABuffer> &buffer);
     static uint64_t GetNowNTP();
 
-    status_t setupPacketizer();
+    status_t setupPacketizer(bool usePCMAudio);
 
     status_t addSource(
             bool isVideo,
             const sp<MediaSource> &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 @@
     bool isVideo() const;
 
     bool isH264() const;
+    bool isAAC() const;
     bool lacksADTSHeader() const;
 
     sp<ABuffer> prependCSD(const sp<ABuffer> &accessUnit) const;
@@ -139,6 +140,10 @@
     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 @@
         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 @@
             && 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 @@
       mClient(client),
       mSessionID(0),
       mStopReplyID(0),
+      mUsingPCMAudio(false),
       mClientSessionID(0),
       mReaperPending(false),
       mNextCSeq(1)
@@ -531,6 +532,11 @@
         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 @@
     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 @@
             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 @@
 
     uint32_t mStopReplyID;
 
+    bool mUsingPCMAudio;
     int32_t mClientSessionID;
 
     struct ClientInfo {