Merge "Camera2: Include JPEG settings in ZSL requests." into jb-mr1-dev
diff --git a/libvideoeditor/lvpp/VideoEditorSRC.cpp b/libvideoeditor/lvpp/VideoEditorSRC.cpp
index 4753dd4..36d0812 100755
--- a/libvideoeditor/lvpp/VideoEditorSRC.cpp
+++ b/libvideoeditor/lvpp/VideoEditorSRC.cpp
@@ -321,8 +321,7 @@
mResampler = AudioResampler::create(
16 /* bit depth */,
mChannelCnt,
- mOutputSampleRate,
- AudioResampler::DEFAULT);
+ mOutputSampleRate);
CHECK(mResampler);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(kUnityGain, kUnityGain);
diff --git a/libvideoeditor/vss/src/VideoEditorResampler.cpp b/libvideoeditor/vss/src/VideoEditorResampler.cpp
index 38dffb7..1129c3c 100755
--- a/libvideoeditor/vss/src/VideoEditorResampler.cpp
+++ b/libvideoeditor/vss/src/VideoEditorResampler.cpp
@@ -80,7 +80,7 @@
VideoEditorResampler *context = new VideoEditorResampler();
context->mResampler = AudioResampler::create(
- bitDepth, inChannelCount, sampleRate, AudioResampler::DEFAULT);
+ bitDepth, inChannelCount, sampleRate);
if (context->mResampler == NULL) {
return NULL;
}
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> ¬ify,
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> ¬ify,
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 {
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7a1c020..85c2dac 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -20,7 +20,9 @@
AudioPolicyService.cpp \
ServiceUtilities.cpp \
AudioResamplerSinc.cpp.arm
-# AudioResamplerCubic.cpp.arm
+
+# uncomment to enable AudioResampler::MED_QUALITY
+# LOCAL_SRC_FILES += AudioResamplerCubic.cpp.arm
LOCAL_SRC_FILES += StateQueue.cpp
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index ab75dd0..af169d5 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -539,11 +539,23 @@
if (sampleRate != value) {
sampleRate = value;
if (resampler == NULL) {
+ ALOGV("creating resampler from track %d Hz to device %d Hz", value, devSampleRate);
+ AudioResampler::src_quality quality;
+ // force lowest quality level resampler if use case isn't music or video
+ // FIXME this is flawed for dynamic sample rates, as we choose the resampler
+ // quality level based on the initial ratio, but that could change later.
+ // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
+ if (!((value == 44100 && devSampleRate == 48000) ||
+ (value == 48000 && devSampleRate == 44100))) {
+ quality = AudioResampler::LOW_QUALITY;
+ } else {
+ quality = AudioResampler::DEFAULT_QUALITY;
+ }
resampler = AudioResampler::create(
format,
// the resampler sees the number of channels after the downmixer, if any
downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
- devSampleRate);
+ devSampleRate, quality);
resampler->setLocalTimeFreq(localTimeFreq);
}
return true;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 5c1c905..96293e3 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -24,9 +24,7 @@
#include <cutils/properties.h>
#include "AudioResampler.h"
#include "AudioResamplerSinc.h"
-#if 0
#include "AudioResamplerCubic.h"
-#endif
#ifdef __arm__
#include <machine/cpu-features.h>
@@ -42,7 +40,7 @@
class AudioResamplerOrder1 : public AudioResampler {
public:
AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) :
- AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) {
+ AudioResampler(bitDepth, inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
}
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
@@ -79,29 +77,120 @@
int mX0R;
};
+bool AudioResampler::qualityIsSupported(src_quality quality)
+{
+ switch (quality) {
+ case DEFAULT_QUALITY:
+ case LOW_QUALITY:
+#if 0 // these have not been qualified recently so are not supported unless explicitly requested
+ case MED_QUALITY:
+ case HIGH_QUALITY:
+#endif
+ case VERY_HIGH_QUALITY:
+ return true;
+ default:
+ return false;
+ }
+}
+
// ----------------------------------------------------------------------------
-AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
- int32_t sampleRate, int quality) {
- // can only create low quality resample now
- AudioResampler* resampler;
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY;
+void AudioResampler::init_routine()
+{
char value[PROPERTY_VALUE_MAX];
- if (property_get("af.resampler.quality", value, 0)) {
- quality = atoi(value);
- ALOGD("forcing AudioResampler quality to %d", quality);
+ if (property_get("af.resampler.quality", value, NULL) > 0) {
+ char *endptr;
+ unsigned long l = strtoul(value, &endptr, 0);
+ if (*endptr == '\0') {
+ defaultQuality = (src_quality) l;
+ ALOGD("forcing AudioResampler quality to %d", defaultQuality);
+ if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) {
+ defaultQuality = DEFAULT_QUALITY;
+ }
+ }
+ }
+}
+
+uint32_t AudioResampler::qualityMHz(src_quality quality)
+{
+ switch (quality) {
+ default:
+ case DEFAULT_QUALITY:
+ case LOW_QUALITY:
+ return 3;
+ case MED_QUALITY:
+ return 6;
+ case HIGH_QUALITY:
+ return 20;
+ case VERY_HIGH_QUALITY:
+ return 34;
+ }
+}
+
+static const uint32_t maxMHz = 75; // an arbitrary number that permits 2 VHQ, should be tunable
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static uint32_t currentMHz = 0;
+
+AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
+ int32_t sampleRate, src_quality quality) {
+
+ bool atFinalQuality;
+ if (quality == DEFAULT_QUALITY) {
+ // read the resampler default quality property the first time it is needed
+ int ok = pthread_once(&once_control, init_routine);
+ if (ok != 0) {
+ ALOGE("%s pthread_once failed: %d", __func__, ok);
+ }
+ quality = defaultQuality;
+ atFinalQuality = false;
+ } else {
+ atFinalQuality = true;
}
- if (quality == DEFAULT)
- quality = LOW_QUALITY;
+ // naive implementation of CPU load throttling doesn't account for whether resampler is active
+ pthread_mutex_lock(&mutex);
+ for (;;) {
+ uint32_t deltaMHz = qualityMHz(quality);
+ uint32_t newMHz = currentMHz + deltaMHz;
+ if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) {
+ ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d",
+ currentMHz, newMHz, deltaMHz, quality);
+ currentMHz = newMHz;
+ break;
+ }
+ // not enough CPU available for proposed quality level, so try next lowest level
+ switch (quality) {
+ default:
+ case DEFAULT_QUALITY:
+ case LOW_QUALITY:
+ atFinalQuality = true;
+ break;
+ case MED_QUALITY:
+ quality = LOW_QUALITY;
+ break;
+ case HIGH_QUALITY:
+ quality = MED_QUALITY;
+ break;
+ case VERY_HIGH_QUALITY:
+ quality = HIGH_QUALITY;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&mutex);
+
+ AudioResampler* resampler;
switch (quality) {
default:
+ case DEFAULT_QUALITY:
case LOW_QUALITY:
ALOGV("Create linear Resampler");
resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
break;
-#if 0
+#if 0 // disabled because it has not been qualified recently, if requested will use default:
case MED_QUALITY:
ALOGV("Create cubic Resampler");
resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
@@ -110,8 +199,9 @@
case HIGH_QUALITY:
ALOGV("Create HIGH_QUALITY sinc Resampler");
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
+ break;
case VERY_HIGH_QUALITY:
- ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d",quality);
+ ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
break;
}
@@ -122,17 +212,20 @@
}
AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
- int32_t sampleRate) :
+ int32_t sampleRate, src_quality quality) :
mBitDepth(bitDepth), mChannelCount(inChannelCount),
mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
mPhaseFraction(0), mLocalTimeFreq(0),
- mPTS(AudioBufferProvider::kInvalidPTS) {
+ mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
// sanity check on format
if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
inChannelCount);
// ALOG_ASSERT(0);
}
+ if (sampleRate <= 0) {
+ ALOGE("Unsupported sample rate %d Hz", sampleRate);
+ }
// initialize common members
mVolume[0] = mVolume[1] = 0;
@@ -141,6 +234,15 @@
}
AudioResampler::~AudioResampler() {
+ pthread_mutex_lock(&mutex);
+ src_quality quality = getQuality();
+ uint32_t deltaMHz = qualityMHz(quality);
+ int32_t newMHz = currentMHz - deltaMHz;
+ ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d",
+ currentMHz, newMHz, deltaMHz, quality);
+ LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz);
+ currentMHz = newMHz;
+ pthread_mutex_unlock(&mutex);
}
void AudioResampler::setSampleRate(int32_t inSampleRate) {
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 71cdfda..2b8694f 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -35,15 +35,15 @@
// certain fixed rate conversions. Sample rate cannot be
// changed dynamically.
enum src_quality {
- DEFAULT=0,
+ DEFAULT_QUALITY=0,
LOW_QUALITY=1,
MED_QUALITY=2,
HIGH_QUALITY=3,
- VERY_HIGH_QUALITY=255
+ VERY_HIGH_QUALITY=4,
};
static AudioResampler* create(int bitDepth, int inChannelCount,
- int32_t sampleRate, int quality=DEFAULT);
+ int32_t sampleRate, src_quality quality=DEFAULT_QUALITY);
virtual ~AudioResampler();
@@ -61,6 +61,9 @@
virtual void reset();
virtual size_t getUnreleasedFrames() const { return mInputIndex; }
+ // called from destructor, so must not be virtual
+ src_quality getQuality() const { return mQuality; }
+
protected:
// number of bits for phase fraction - 30 bits allows nearly 2x downsampling
static const int kNumPhaseBits = 30;
@@ -71,7 +74,7 @@
// multiplier to calculate fixed point phase increment
static const double kPhaseMultiplier = 1L << kNumPhaseBits;
- AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate);
+ AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality);
// prevent copying
AudioResampler(const AudioResampler&);
@@ -94,6 +97,19 @@
uint32_t mPhaseFraction;
uint64_t mLocalTimeFreq;
int64_t mPTS;
+
+private:
+ const src_quality mQuality;
+
+ // Return 'true' if the quality level is supported without explicit request
+ static bool qualityIsSupported(src_quality quality);
+
+ // For pthread_once()
+ static void init_routine();
+
+ // Return the estimated CPU load for specific resampler in MHz.
+ // The absolute number is irrelevant, it's the relative values that matter.
+ static uint32_t qualityMHz(src_quality quality);
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
index 892785a..203b933 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -29,7 +29,7 @@
class AudioResamplerCubic : public AudioResampler {
public:
AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) :
- AudioResampler(bitDepth, inChannelCount, sampleRate) {
+ AudioResampler(bitDepth, inChannelCount, sampleRate, MED_QUALITY) {
}
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 0ae4b64..90651f1 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -64,13 +64,65 @@
0x00000000 // this one is needed for lerping the last coefficient
};
-//Define the static variables
-int AudioResamplerSinc::coefsBits;
-int AudioResamplerSinc::cShift;
-uint32_t AudioResamplerSinc::cMask;
-int AudioResamplerSinc::pShift;
-uint32_t AudioResamplerSinc::pMask;
-unsigned int AudioResamplerSinc::halfNumCoefs;
+// we use 15 bits to interpolate between these samples
+// this cannot change because the mul below rely on it.
+static const int pLerpBits = 15;
+
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+static readCoefficientsFn readResampleCoefficients = NULL;
+
+/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::highQualityConstants;
+/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::veryHighQualityConstants;
+
+void AudioResamplerSinc::init_routine()
+{
+ // for high quality resampler, the parameters for coefficients are compile-time constants
+ Constants *c = &highQualityConstants;
+ c->coefsBits = RESAMPLE_FIR_LERP_INT_BITS;
+ c->cShift = kNumPhaseBits - c->coefsBits;
+ c->cMask = ((1<< c->coefsBits)-1) << c->cShift;
+ c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
+ c->pMask = ((1<< pLerpBits)-1) << c->pShift;
+ c->halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
+
+ // for very high quality resampler, the parameters are load-time constants
+ veryHighQualityConstants = highQualityConstants;
+
+ // Open the dll to get the coefficients for VERY_HIGH_QUALITY
+ void *resampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW);
+ ALOGV("Open libaudio-resampler library = %p", resampleCoeffLib);
+ if (resampleCoeffLib == NULL) {
+ ALOGE("Could not open audio-resampler library: %s", dlerror());
+ return;
+ }
+
+ readResampleCoefficients = (readCoefficientsFn) dlsym(resampleCoeffLib,
+ "readResamplerCoefficients");
+ readResampleFirNumCoeffFn readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
+ dlsym(resampleCoeffLib, "readResampleFirNumCoeff");
+ readResampleFirLerpIntBitsFn readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)
+ dlsym(resampleCoeffLib, "readResampleFirLerpIntBits");
+ if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) {
+ readResampleCoefficients = NULL;
+ dlclose(resampleCoeffLib);
+ resampleCoeffLib = NULL;
+ ALOGE("Could not find symbol: %s", dlerror());
+ return;
+ }
+
+ c = &veryHighQualityConstants;
+ // we have 16 coefs samples per zero-crossing
+ c->coefsBits = readResampleFirLerpIntBits();
+ ALOGV("coefsBits = %d", c->coefsBits);
+ c->cShift = kNumPhaseBits - c->coefsBits;
+ c->cMask = ((1<<c->coefsBits)-1) << c->cShift;
+ c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
+ c->pMask = ((1<<pLerpBits)-1) << c->pShift;
+ // number of zero-crossing on each side
+ c->halfNumCoefs = readResampleFirNumCoeff();
+ ALOGV("halfNumCoefs = %d", c->halfNumCoefs);
+ // note that we "leak" resampleCoeffLib until the process exits
+}
// ----------------------------------------------------------------------------
@@ -148,8 +200,8 @@
// ----------------------------------------------------------------------------
AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
- int inChannelCount, int32_t sampleRate, int32_t quality)
- : AudioResampler(bitDepth, inChannelCount, sampleRate),
+ int inChannelCount, int32_t sampleRate, src_quality quality)
+ : AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
mState(0)
{
/*
@@ -168,74 +220,28 @@
*
*/
- mResampleCoeffLib = NULL;
- //Intialize the parameters for resampler coefficients
- //for high quality
- coefsBits = RESAMPLE_FIR_LERP_INT_BITS;
- cShift = kNumPhaseBits - coefsBits;
- cMask = ((1<< coefsBits)-1) << cShift;
-
- pShift = kNumPhaseBits - coefsBits - pLerpBits;
- pMask = ((1<< pLerpBits)-1) << pShift;
-
- halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
-
- //Check if qcom highest quality can be used
- char value[PROPERTY_VALUE_MAX];
- //Open the dll to get the coefficients for VERY_HIGH_QUALITY
- if (quality == VERY_HIGH_QUALITY ) {
- mResampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW);
- ALOGV("Open libaudio-resampler library = %p",mResampleCoeffLib);
- if (mResampleCoeffLib == NULL) {
- ALOGE("Could not open audio-resampler library: %s", dlerror());
- return;
- }
- mReadResampleCoefficients = (readCoefficientsFn)dlsym(mResampleCoeffLib, "readResamplerCoefficients");
- mReadResampleFirNumCoeff = (readResampleFirNumCoeffFn)dlsym(mResampleCoeffLib, "readResampleFirNumCoeff");
- mReadResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)dlsym(mResampleCoeffLib,"readResampleFirLerpIntBits");
- if (!mReadResampleCoefficients || !mReadResampleFirNumCoeff || !mReadResampleFirLerpIntBits) {
- mReadResampleCoefficients = NULL;
- mReadResampleFirNumCoeff = NULL;
- mReadResampleFirLerpIntBits = NULL;
- dlclose(mResampleCoeffLib);
- mResampleCoeffLib = NULL;
- ALOGE("Could not find convert symbol: %s", dlerror());
- return;
- }
- // we have 16 coefs samples per zero-crossing
- coefsBits = mReadResampleFirLerpIntBits();
- ALOGV("coefsBits = %d",coefsBits);
- cShift = kNumPhaseBits - coefsBits;
- cMask = ((1<<coefsBits)-1) << cShift;
- pShift = kNumPhaseBits - coefsBits - pLerpBits;
- pMask = ((1<<pLerpBits)-1) << pShift;
- // number of zero-crossing on each side
- halfNumCoefs = mReadResampleFirNumCoeff();
- ALOGV("halfNumCoefs = %d",halfNumCoefs);
+ // Load the constants for coefficients
+ int ok = pthread_once(&once_control, init_routine);
+ if (ok != 0) {
+ ALOGE("%s pthread_once failed: %d", __func__, ok);
}
+ mConstants = (quality == VERY_HIGH_QUALITY) ? &veryHighQualityConstants : &highQualityConstants;
}
AudioResamplerSinc::~AudioResamplerSinc()
{
- if(mResampleCoeffLib) {
- ALOGV("close the libaudio-resampler library");
- dlclose(mResampleCoeffLib);
- mResampleCoeffLib = NULL;
- mReadResampleCoefficients = NULL;
- mReadResampleFirNumCoeff = NULL;
- mReadResampleFirLerpIntBits = NULL;
- }
- delete [] mState;
+ delete[] mState;
}
void AudioResamplerSinc::init() {
+ const Constants *c = mConstants;
- const size_t numCoefs = 2*halfNumCoefs;
+ const size_t numCoefs = 2*c->halfNumCoefs;
const size_t stateSize = numCoefs * mChannelCount * 2;
mState = new int16_t[stateSize];
memset(mState, 0, sizeof(int16_t)*stateSize);
- mImpulse = mState + (halfNumCoefs-1)*mChannelCount;
+ mImpulse = mState + (c->halfNumCoefs-1)*mChannelCount;
mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
}
@@ -243,11 +249,14 @@
AudioBufferProvider* provider)
{
- if(mResampleCoeffLib){
+ // FIXME store current state (up or down sample) and only load the coefs when the state
+ // changes. Or load two pointers one for up and one for down in the init function.
+ // Not critical now since the read functions are fast, but would be important if read was slow.
+ if (readResampleCoefficients) {
ALOGV("get coefficient from libmm-audio resampler library");
- mFirCoefs = (mInSampleRate <= mSampleRate) ? mReadResampleCoefficients(true) : mReadResampleCoefficients(false);
- }
- else {
+ mFirCoefs = (mInSampleRate <= mSampleRate) ? readResampleCoefficients(true) :
+ readResampleCoefficients(false);
+ } else {
ALOGV("Use default coefficients");
mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
}
@@ -269,6 +278,7 @@
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
+ const Constants *c = mConstants;
int16_t* impulse = mImpulse;
uint32_t vRL = mVolumeRL;
size_t inputIndex = mInputIndex;
@@ -307,7 +317,7 @@
const size_t frameCount = mBuffer.frameCount;
// Always read-in the first samples from the input buffer
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ int16_t* head = impulse + c->halfNumCoefs*CHANNELS;
head[0] = in[inputIndex*CHANNELS + 0];
if (CHANNELS == 2)
head[1] = in[inputIndex*CHANNELS + 1];
@@ -365,15 +375,16 @@
int16_t*& impulse, uint32_t& phaseFraction,
const int16_t* in, size_t inputIndex)
{
+ const Constants *c = mConstants;
const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
impulse += CHANNELS;
phaseFraction -= 1LU<<kNumPhaseBits;
if (impulse >= mRingFull) {
- const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
+ const size_t stateSize = (c->halfNumCoefs*2)*CHANNELS;
memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
impulse -= stateSize;
}
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ int16_t* head = impulse + c->halfNumCoefs*CHANNELS;
head[0] = in[inputIndex*CHANNELS + 0];
if (CHANNELS == 2)
head[1] = in[inputIndex*CHANNELS + 1];
@@ -383,15 +394,17 @@
void AudioResamplerSinc::filterCoefficient(
int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples)
{
+ const Constants *c = mConstants;
+
// compute the index of the coefficient on the positive side and
// negative side
- uint32_t indexP = (phase & cMask) >> cShift;
- uint16_t lerpP = (phase & pMask) >> pShift;
- uint32_t indexN = (-phase & cMask) >> cShift;
- uint16_t lerpN = (-phase & pMask) >> pShift;
+ uint32_t indexP = (phase & c->cMask) >> c->cShift;
+ uint16_t lerpP = (phase & c->pMask) >> c->pShift;
+ uint32_t indexN = (-phase & c->cMask) >> c->cShift;
+ uint16_t lerpN = (-phase & c->pMask) >> c->pShift;
if ((indexP == 0) && (lerpP == 0)) {
- indexN = cMask >> cShift;
- lerpN = pMask >> pShift;
+ indexN = c->cMask >> c->cShift;
+ lerpN = c->pMask >> c->pShift;
}
l = 0;
@@ -399,19 +412,19 @@
const int32_t* coefs = mFirCoefs;
const int16_t *sP = samples;
const int16_t *sN = samples+CHANNELS;
- for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) {
+ for (unsigned int i=0 ; i < c->halfNumCoefs/4 ; i++) {
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
}
}
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index c53c66d..25fc025 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -27,14 +27,15 @@
typedef const int32_t * (*readCoefficientsFn)(bool upDownSample);
-typedef int32_t (*readResampleFirNumCoeffFn)();
-typedef int32_t (*readResampleFirLerpIntBitsFn)();
+typedef int32_t (*readResampleFirNumCoeffFn)();
+typedef int32_t (*readResampleFirLerpIntBitsFn)();
// ----------------------------------------------------------------------------
class AudioResamplerSinc : public AudioResampler {
public:
- AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate, int32_t quality = HIGH_QUALITY);
+ AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate,
+ src_quality quality = HIGH_QUALITY);
virtual ~AudioResamplerSinc();
@@ -60,10 +61,6 @@
inline void read(int16_t*& impulse, uint32_t& phaseFraction,
const int16_t* in, size_t inputIndex);
- readCoefficientsFn mReadResampleCoefficients ;
- readResampleFirNumCoeffFn mReadResampleFirNumCoeff;
- readResampleFirLerpIntBitsFn mReadResampleFirLerpIntBits;
-
int16_t *mState;
int16_t *mImpulse;
int16_t *mRingFull;
@@ -72,24 +69,28 @@
static const int32_t mFirCoefsDown[];
static const int32_t mFirCoefsUp[];
- void * mResampleCoeffLib;
// ----------------------------------------------------------------------------
static const int32_t RESAMPLE_FIR_NUM_COEF = 8;
static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4;
- // we have 16 coefs samples per zero-crossing
- static int coefsBits;
- static int cShift;
- static uint32_t cMask;
+ struct Constants {
+ // we have 16 coefs samples per zero-crossing
+ int coefsBits;
+ int cShift;
+ uint32_t cMask;
- // and we use 15 bits to interpolate between these samples
- // this cannot change because the mul below rely on it.
- static const int pLerpBits = 15;
- static int pShift;
- static uint32_t pMask;
+ int pShift;
+ uint32_t pMask;
- // number of zero-crossing on each side
- static unsigned int halfNumCoefs;
+ // number of zero-crossing on each side
+ unsigned int halfNumCoefs;
+ };
+
+ static Constants highQualityConstants;
+ static Constants veryHighQualityConstants;
+ const Constants *mConstants; // points to appropriate set of coefficient parameters
+
+ static void init_routine();
};
// ----------------------------------------------------------------------------