Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7bd24af authored by Andreas Huber's avatar Andreas Huber
Browse files

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
parent d69fd4d2
Loading
Loading
Loading
Loading
+145 −9
Original line number Diff line number Diff line
@@ -38,12 +38,14 @@ namespace android {
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 @@ 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)) {
        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();
    }

    if (!mIsPCMAudio) {
        mEncoder = MediaCodec::CreateByType(
                mCodecLooper, outputMIME.c_str(), true /* encoder */);

        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<AMessage> &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<AMessage> &msg) {
        case kWhatShutdown:
        {
            ALOGI("shutting down encoder");

            if (mEncoder != NULL) {
                mEncoder->release();
                mEncoder.clear();
            }

            AString mime;
            CHECK(mInputFormat->findString("mime", &mime));
@@ -332,6 +350,11 @@ void Converter::onMessageReceived(const sp<AMessage> &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<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();
+12 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@

#define CONVERTER_H_

#include "WifiDisplaySource.h"

#include <media/stagefright/foundation/AHandler.h>

namespace android {
@@ -34,7 +36,8 @@ struct Converter : public AHandler {
    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 @@ private:
    sp<ALooper> mCodecLooper;
    sp<AMessage> mInputFormat;
    bool mIsVideo;
    bool mIsPCMAudio;
    sp<AMessage> mOutputFormat;

    sp<MediaCodec> mEncoder;
@@ -92,6 +96,8 @@ private:
    bool mInSilentMode;
#endif

    sp<ABuffer> 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<ABuffer> &accessUnit);

    DISALLOW_EVIL_CONSTRUCTORS(Converter);
+14 −10
Original line number Diff line number Diff line
@@ -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<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 @@ status_t WifiDisplaySource::PlaybackSession::addSource(
    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 @@ 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> 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");
+5 −3
Original line number Diff line number Diff line
@@ -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<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);
+10 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ struct TSPacketizer::Track : public RefBase {
    bool isVideo() const;

    bool isH264() const;
    bool isAAC() const;
    bool lacksADTSHeader() const;

    sp<ABuffer> prependCSD(const sp<ABuffer> &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<AMessage> &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);
    }
Loading