summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Android.mk65
-rw-r--r--services/audioflinger/AudioBufferProvider.h55
-rw-r--r--services/audioflinger/AudioBufferProviderSource.cpp141
-rw-r--r--services/audioflinger/AudioBufferProviderSource.h56
-rw-r--r--services/audioflinger/AudioFlinger.cpp1378
-rw-r--r--services/audioflinger/AudioFlinger.h474
-rw-r--r--services/audioflinger/AudioMixer.cpp27
-rw-r--r--services/audioflinger/AudioMixer.h6
-rw-r--r--services/audioflinger/AudioPolicyService.cpp83
-rw-r--r--services/audioflinger/AudioPolicyService.h11
-rw-r--r--services/audioflinger/AudioResampler.cpp135
-rw-r--r--services/audioflinger/AudioResampler.h27
-rw-r--r--services/audioflinger/AudioResamplerCubic.h2
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp722
-rw-r--r--services/audioflinger/AudioResamplerSinc.h48
-rw-r--r--services/audioflinger/AudioStreamInSource.cpp83
-rw-r--r--services/audioflinger/AudioStreamInSource.h65
-rw-r--r--services/audioflinger/AudioStreamOutSink.cpp70
-rw-r--r--services/audioflinger/AudioStreamOutSink.h63
-rw-r--r--services/audioflinger/ExtendedAudioBufferProvider.h31
-rw-r--r--services/audioflinger/FastMixer.cpp60
-rw-r--r--services/audioflinger/FastMixerState.h4
-rw-r--r--services/audioflinger/LibsndfileSink.cpp50
-rw-r--r--services/audioflinger/LibsndfileSink.h54
-rw-r--r--services/audioflinger/LibsndfileSource.cpp80
-rw-r--r--services/audioflinger/LibsndfileSource.h58
-rw-r--r--services/audioflinger/MonoPipe.cpp165
-rw-r--r--services/audioflinger/MonoPipe.h84
-rw-r--r--services/audioflinger/MonoPipeReader.cpp75
-rw-r--r--services/audioflinger/MonoPipeReader.h64
-rw-r--r--services/audioflinger/NBAIO.cpp189
-rw-r--r--services/audioflinger/NBAIO.h293
-rw-r--r--services/audioflinger/Pipe.cpp70
-rw-r--r--services/audioflinger/Pipe.h64
-rw-r--r--services/audioflinger/PipeReader.cpp95
-rw-r--r--services/audioflinger/PipeReader.h65
-rw-r--r--services/audioflinger/Soaker.h45
-rw-r--r--services/audioflinger/SourceAudioBufferProvider.cpp104
-rw-r--r--services/audioflinger/SourceAudioBufferProvider.h52
-rw-r--r--services/audioflinger/audio-resampler/Android.mk14
-rw-r--r--services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp (renamed from services/audioflinger/roundup.h)35
-rw-r--r--services/audioflinger/audio-resampler/filter_coefficients.h285
-rw-r--r--services/audioflinger/roundup.c32
-rw-r--r--services/audioflinger/test-resample.cpp274
-rw-r--r--services/camera/libcameraservice/Android.mk24
-rw-r--r--services/camera/libcameraservice/Camera2Client.cpp1636
-rw-r--r--services/camera/libcameraservice/Camera2Client.h228
-rw-r--r--services/camera/libcameraservice/Camera2Device.cpp1513
-rw-r--r--services/camera/libcameraservice/Camera2Device.h476
-rw-r--r--services/camera/libcameraservice/CameraClient.cpp959
-rw-r--r--services/camera/libcameraservice/CameraClient.h156
-rw-r--r--services/camera/libcameraservice/CameraHardwareInterface.h9
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.cpp410
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.h126
-rw-r--r--services/camera/libcameraservice/CameraService.cpp1104
-rw-r--r--services/camera/libcameraservice/CameraService.h219
-rw-r--r--services/camera/libcameraservice/CannedJpeg.h750
-rw-r--r--services/camera/libcameraservice/FakeCamera.cpp433
-rw-r--r--services/camera/libcameraservice/FakeCamera.h67
-rw-r--r--services/camera/libcameraservice/camera2/BurstCapture.cpp112
-rw-r--r--services/camera/libcameraservice/camera2/BurstCapture.h71
-rw-r--r--services/camera/libcameraservice/camera2/CallbackProcessor.cpp301
-rw-r--r--services/camera/libcameraservice/camera2/CallbackProcessor.h82
-rw-r--r--services/camera/libcameraservice/camera2/Camera2Heap.h55
-rw-r--r--services/camera/libcameraservice/camera2/CameraMetadata.cpp296
-rw-r--r--services/camera/libcameraservice/camera2/CameraMetadata.h173
-rw-r--r--services/camera/libcameraservice/camera2/CaptureSequencer.cpp673
-rw-r--r--services/camera/libcameraservice/camera2/CaptureSequencer.h177
-rw-r--r--services/camera/libcameraservice/camera2/FrameProcessor.cpp308
-rw-r--r--services/camera/libcameraservice/camera2/FrameProcessor.h89
-rw-r--r--services/camera/libcameraservice/camera2/JpegCompressor.cpp220
-rw-r--r--services/camera/libcameraservice/camera2/JpegCompressor.h107
-rw-r--r--services/camera/libcameraservice/camera2/JpegProcessor.cpp380
-rw-r--r--services/camera/libcameraservice/camera2/JpegProcessor.h83
-rw-r--r--services/camera/libcameraservice/camera2/Parameters.cpp2431
-rw-r--r--services/camera/libcameraservice/camera2/Parameters.h365
-rw-r--r--services/camera/libcameraservice/camera2/StreamingProcessor.cpp657
-rw-r--r--services/camera/libcameraservice/camera2/StreamingProcessor.h118
-rw-r--r--services/camera/libcameraservice/camera2/ZslProcessor.cpp538
-rw-r--r--services/camera/libcameraservice/camera2/ZslProcessor.h126
80 files changed, 15237 insertions, 6088 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 8473fabf..28999533 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -13,40 +13,14 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- AudioBufferProviderSource.cpp \
- AudioStreamOutSink.cpp \
- AudioStreamInSource.cpp \
- NBAIO.cpp \
- MonoPipe.cpp \
- MonoPipeReader.cpp \
- Pipe.cpp \
- PipeReader.cpp \
- roundup.c \
- SourceAudioBufferProvider.cpp
-
-# libsndfile license is incompatible; uncomment to use for local debug only
-#LOCAL_SRC_FILES += LibsndfileSink.cpp LibsndfileSource.cpp
-#LOCAL_C_INCLUDES += path/to/libsndfile/src
-#LOCAL_STATIC_LIBRARIES += libsndfile
-
-# uncomment for systrace
-# LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO
-
-LOCAL_MODULE := libnbaio
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
LOCAL_SRC_FILES:= \
AudioFlinger.cpp \
AudioMixer.cpp.arm \
AudioResampler.cpp.arm \
AudioPolicyService.cpp \
- ServiceUtilities.cpp
-# AudioResamplerSinc.cpp.arm
-# AudioResamplerCubic.cpp.arm
+ ServiceUtilities.cpp \
+ AudioResamplerCubic.cpp.arm \
+ AudioResamplerSinc.cpp.arm
LOCAL_SRC_FILES += StateQueue.cpp
@@ -66,6 +40,7 @@ LOCAL_SHARED_LIBRARIES := \
libbinder \
libmedia \
libmedia_native \
+ libnbaio \
libhardware \
libhardware_legacy \
libeffects \
@@ -74,7 +49,6 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_STATIC_LIBRARIES := \
libscheduling_policy \
- libnbaio \
libcpustats \
libmedia_helper
@@ -89,7 +63,7 @@ LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
-LOCAL_CFLAGS += -DHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE -USOAKER
+LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
# uncomment for systrace
# LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO
@@ -99,7 +73,32 @@ LOCAL_CFLAGS += -DHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
# LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000
# uncomment to enable the audio watchdog
-LOCAL_SRC_FILES += AudioWatchdog.cpp
-LOCAL_CFLAGS += -DAUDIO_WATCHDOG
+# LOCAL_SRC_FILES += AudioWatchdog.cpp
+# LOCAL_CFLAGS += -DAUDIO_WATCHDOG
include $(BUILD_SHARED_LIBRARY)
+
+#
+# build audio resampler test tool
+#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ test-resample.cpp \
+ AudioResampler.cpp.arm \
+ AudioResamplerCubic.cpp.arm \
+ AudioResamplerSinc.cpp.arm
+
+LOCAL_SHARED_LIBRARIES := \
+ libdl \
+ libcutils \
+ libutils
+
+LOCAL_MODULE:= test-resample
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
+
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h
deleted file mode 100644
index 43e4de75..00000000
--- a/services/audioflinger/AudioBufferProvider.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2007 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 ANDROID_AUDIO_BUFFER_PROVIDER_H
-#define ANDROID_AUDIO_BUFFER_PROVIDER_H
-
-#include <utils/Errors.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class AudioBufferProvider
-{
-public:
-
- struct Buffer {
- Buffer() : raw(NULL), frameCount(0) { }
- union {
- void* raw;
- short* i16;
- int8_t* i8;
- };
- size_t frameCount;
- };
-
- virtual ~AudioBufferProvider() {}
-
- // value representing an invalid presentation timestamp
- static const int64_t kInvalidPTS = 0x7FFFFFFFFFFFFFFFLL; // <stdint.h> is too painful
-
- // pts is the local time when the next sample yielded by getNextBuffer
- // will be rendered.
- // Pass kInvalidPTS if the PTS is unknown or not applicable.
- virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0;
-
- virtual void releaseBuffer(Buffer* buffer) = 0;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H
diff --git a/services/audioflinger/AudioBufferProviderSource.cpp b/services/audioflinger/AudioBufferProviderSource.cpp
deleted file mode 100644
index 43421719..00000000
--- a/services/audioflinger/AudioBufferProviderSource.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "AudioBufferProviderSource"
-//#define LOG_NDEBUG 0
-
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include "AudioBufferProviderSource.h"
-
-namespace android {
-
-AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
- NBAIO_Format format) :
- NBAIO_Source(format), mProvider(provider), mConsumed(0)
-{
- ALOG_ASSERT(provider != NULL);
- ALOG_ASSERT(format != Format_Invalid);
-}
-
-AudioBufferProviderSource::~AudioBufferProviderSource()
-{
- if (mBuffer.raw != NULL) {
- mProvider->releaseBuffer(&mBuffer);
- }
-}
-
-ssize_t AudioBufferProviderSource::availableToRead()
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0;
-}
-
-ssize_t AudioBufferProviderSource::read(void *buffer, size_t count)
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- if (CC_UNLIKELY(mBuffer.raw == NULL)) {
- mBuffer.frameCount = count;
- status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS);
- if (status != OK) {
- return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status;
- }
- ALOG_ASSERT(mBuffer.raw != NULL);
- // mConsumed is 0 either from constructor or after releaseBuffer()
- }
- size_t available = mBuffer.frameCount - mConsumed;
- if (CC_UNLIKELY(count > available)) {
- count = available;
- }
- // count could be zero, either because count was zero on entry or
- // available is zero, but both are unlikely so don't check for that
- memcpy(buffer, (char *) mBuffer.raw + (mConsumed << mBitShift), count << mBitShift);
- if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
- mProvider->releaseBuffer(&mBuffer);
- mBuffer.raw = NULL;
- mConsumed = 0;
- }
- mFramesRead += count;
- // For better responsiveness with large values of count,
- // return a short count rather than continuing with next buffer.
- // This gives the caller a chance to interpolate other actions.
- return count;
-}
-
-ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, size_t block)
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- if (CC_UNLIKELY(block == 0)) {
- block = ~0;
- }
- for (size_t accumulator = 0; ; ) {
- ALOG_ASSERT(accumulator <= total);
- size_t count = total - accumulator;
- if (CC_UNLIKELY(count == 0)) {
- return accumulator;
- }
- if (CC_LIKELY(count > block)) {
- count = block;
- }
- // 1 <= count <= block
- if (CC_UNLIKELY(mBuffer.raw == NULL)) {
- mBuffer.frameCount = count;
- status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS);
- if (CC_LIKELY(status == OK)) {
- ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count);
- // mConsumed is 0 either from constructor or after releaseBuffer()
- continue;
- }
- // FIXME simplify logic - does the initial count and block checks again for no reason;
- // don't you just want to fall through to the size_t available line?
- if (CC_LIKELY(status == NOT_ENOUGH_DATA)) {
- status = WOULD_BLOCK;
- }
- return accumulator > 0 ? accumulator : (ssize_t) status;
- }
- size_t available = mBuffer.frameCount - mConsumed;
- if (CC_UNLIKELY(count > available)) {
- count = available;
- }
- if (CC_LIKELY(count > 0)) {
- ssize_t ret = via(user, (char *) mBuffer.raw + (mConsumed << mBitShift), count);
- if (CC_UNLIKELY(ret <= 0)) {
- if (CC_LIKELY(accumulator > 0)) {
- return accumulator;
- }
- return ret;
- }
- ALOG_ASSERT((size_t) ret <= count);
- mFramesRead += ret;
- accumulator += ret;
- if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) {
- continue;
- }
- }
- mProvider->releaseBuffer(&mBuffer);
- mBuffer.raw = NULL;
- mConsumed = 0;
- // don't get next buffer until we really need it
- }
-}
-
-} // namespace android
diff --git a/services/audioflinger/AudioBufferProviderSource.h b/services/audioflinger/AudioBufferProviderSource.h
deleted file mode 100644
index 2b399372..00000000
--- a/services/audioflinger/AudioBufferProviderSource.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// Implementation of NBAIO_Source that wraps an AudioBufferProvider
-
-#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H
-#define ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H
-
-#include "NBAIO.h"
-#include "AudioBufferProvider.h"
-
-namespace android {
-
-class AudioBufferProviderSource : public NBAIO_Source {
-
-public:
- AudioBufferProviderSource(AudioBufferProvider *provider, NBAIO_Format format);
- virtual ~AudioBufferProviderSource();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format();
-
- // NBAIO_Source interface
-
- //virtual size_t framesRead() const;
- //virtual size_t framesOverrun();
- //virtual size_t overruns();
- virtual ssize_t availableToRead();
- virtual ssize_t read(void *buffer, size_t count);
- virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block);
-
-private:
- AudioBufferProvider * const mProvider;
- AudioBufferProvider::Buffer mBuffer; // current buffer
- size_t mConsumed; // number of frames consumed so far from current buffer
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index aab99849..1913b6f8 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -76,20 +76,14 @@
#include "FastMixer.h"
// NBAIO implementations
-#include "AudioStreamOutSink.h"
-#include "MonoPipe.h"
-#include "MonoPipeReader.h"
-#include "Pipe.h"
-#include "PipeReader.h"
-#include "SourceAudioBufferProvider.h"
-
-#ifdef HAVE_REQUEST_PRIORITY
-#include "SchedulingPolicyService.h"
-#endif
+#include <media/nbaio/AudioStreamOutSink.h>
+#include <media/nbaio/MonoPipe.h>
+#include <media/nbaio/MonoPipeReader.h>
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+#include <media/nbaio/SourceAudioBufferProvider.h>
-#ifdef SOAKER
-#include "Soaker.h"
-#endif
+#include "SchedulingPolicyService.h"
// ----------------------------------------------------------------------------
@@ -167,6 +161,19 @@ static const enum {
static uint32_t gScreenState; // incremented by 2 when screen state changes, bit 0 == 1 means "off"
// AudioFlinger::setParameters() updates, other threads read w/o lock
+// Priorities for requestPriority
+static const int kPriorityAudioApp = 2;
+static const int kPriorityFastMixer = 3;
+
+// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
+// for the track. The client then sub-divides this into smaller buffers for its use.
+// Currently the client uses double-buffering by default, but doesn't tell us about that.
+// So for now we just assume that client is double-buffered.
+// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
+// N-buffering, so AudioFlinger could allocate the right amount of memory.
+// See the client's minBufCount and mNotificationFramesAct calculations for details.
+static const int kFastTrackMultiplier = 2;
+
// ----------------------------------------------------------------------------
#ifdef ADD_BATTERY_DATA
@@ -216,9 +223,8 @@ out:
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
mPrimaryHardwareDev(NULL),
- mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+ mHardwareStatus(AUDIO_HW_IDLE),
mMasterVolume(1.0f),
- mMasterVolumeSupportLvl(MVS_NONE),
mMasterMute(false),
mNextUniqueId(1),
mMode(AUDIO_MODE_INVALID),
@@ -247,21 +253,17 @@ void AudioFlinger::onFirstRef()
}
mMode = AUDIO_MODE_NORMAL;
- mMasterVolumeSW = 1.0;
- mMasterVolume = 1.0;
- mHardwareStatus = AUDIO_HW_IDLE;
}
AudioFlinger::~AudioFlinger()
{
-
while (!mRecordThreads.isEmpty()) {
- // closeInput() will remove first entry from mRecordThreads
- closeInput(mRecordThreads.keyAt(0));
+ // closeInput_nonvirtual() will remove specified entry from mRecordThreads
+ closeInput_nonvirtual(mRecordThreads.keyAt(0));
}
while (!mPlaybackThreads.isEmpty()) {
- // closeOutput() will remove first entry from mPlaybackThreads
- closeOutput(mPlaybackThreads.keyAt(0));
+ // closeOutput_nonvirtual() will remove specified entry from mPlaybackThreads
+ closeOutput_nonvirtual(mPlaybackThreads.keyAt(0));
}
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
@@ -278,7 +280,9 @@ static const char * const audio_interfaces[] = {
};
#define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0])))
-audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_t module, uint32_t devices)
+AudioFlinger::AudioHwDevice* AudioFlinger::findSuitableHwDev_l(
+ audio_module_handle_t module,
+ audio_devices_t devices)
{
// if module is 0, the request comes from an old policy manager and we should load
// well known modules
@@ -287,24 +291,26 @@ audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_t modul
for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
loadHwModule_l(audio_interfaces[i]);
}
+ // then try to find a module supporting the requested device.
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(i);
+ audio_hw_device_t *dev = audioHwDevice->hwDevice();
+ if ((dev->get_supported_devices != NULL) &&
+ (dev->get_supported_devices(dev) & devices) == devices)
+ return audioHwDevice;
+ }
} else {
// check a match for the requested module handle
- AudioHwDevice *audioHwdevice = mAudioHwDevs.valueFor(module);
- if (audioHwdevice != NULL) {
- return audioHwdevice->hwDevice();
+ AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(module);
+ if (audioHwDevice != NULL) {
+ return audioHwDevice;
}
}
- // then try to find a module supporting the requested device.
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
- if ((dev->get_supported_devices(dev) & devices) == devices)
- return dev;
- }
return NULL;
}
-status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -327,11 +333,10 @@ status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
result.append(buffer);
}
write(fd, result.string(), result.size());
- return NO_ERROR;
}
-status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -344,10 +349,9 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
(uint32_t)(mStandbyTimeInNsecs / 1000000));
result.append(buffer);
write(fd, result.string(), result.size());
- return NO_ERROR;
}
-status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -358,7 +362,6 @@ status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args
IPCThreadState::self()->getCallingUid());
result.append(buffer);
write(fd, result.string(), result.size());
- return NO_ERROR;
}
static bool tryLock(Mutex& mutex)
@@ -440,7 +443,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
IAudioFlinger::track_flags_t flags,
const sp<IMemory>& sharedBuffer,
@@ -515,7 +518,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
if (mPendingSyncEvents[i]->triggerSession() == lSessionId) {
if (thread->isValidSyncEvent(mPendingSyncEvents[i])) {
if (lStatus == NO_ERROR) {
- track->setSyncEvent(mPendingSyncEvents[i]);
+ (void) track->setSyncEvent(mPendingSyncEvents[i]);
} else {
mPendingSyncEvents[i]->cancel();
}
@@ -610,30 +613,27 @@ status_t AudioFlinger::setMasterVolume(float value)
return PERMISSION_DENIED;
}
- float swmv = value;
-
Mutex::Autolock _l(mLock);
+ mMasterVolume = value;
- // when hw supports master volume, don't scale in sw mixer
- if (MVS_NONE != mMasterVolumeSupportLvl) {
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- AutoMutex lock(mHardwareLock);
- audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
+ // Set master volume in the HALs which support it.
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ AutoMutex lock(mHardwareLock);
+ AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if (NULL != dev->set_master_volume) {
- dev->set_master_volume(dev, value);
- }
- mHardwareStatus = AUDIO_HW_IDLE;
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ if (dev->canSetMasterVolume()) {
+ dev->hwDevice()->set_master_volume(dev->hwDevice(), value);
}
-
- swmv = 1.0;
+ mHardwareStatus = AUDIO_HW_IDLE;
}
- mMasterVolume = value;
- mMasterVolumeSW = swmv;
+ // Now set the master volume in each playback thread. Playback threads
+ // assigned to HALs which do not have master volume support will apply
+ // master volume during the mix operation. Threads with HALs which do
+ // support master volume will simply ignore the setting.
for (size_t i = 0; i < mPlaybackThreads.size(); i++)
- mPlaybackThreads.valueAt(i)->setMasterVolume(swmv);
+ mPlaybackThreads.valueAt(i)->setMasterVolume(value);
return NO_ERROR;
}
@@ -656,8 +656,9 @@ status_t AudioFlinger::setMode(audio_mode_t mode)
{ // scope for the lock
AutoMutex lock(mHardwareLock);
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_MODE;
- ret = mPrimaryHardwareDev->set_mode(mPrimaryHardwareDev, mode);
+ ret = dev->set_mode(dev, mode);
mHardwareStatus = AUDIO_HW_IDLE;
}
@@ -684,8 +685,9 @@ status_t AudioFlinger::setMicMute(bool state)
}
AutoMutex lock(mHardwareLock);
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state);
+ ret = dev->set_mic_mute(dev, state);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
@@ -699,22 +701,44 @@ bool AudioFlinger::getMicMute() const
bool state = AUDIO_MODE_INVALID;
AutoMutex lock(mHardwareLock);
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
- mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state);
+ dev->get_mic_mute(dev, &state);
mHardwareStatus = AUDIO_HW_IDLE;
return state;
}
status_t AudioFlinger::setMasterMute(bool muted)
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
Mutex::Autolock _l(mLock);
- // This is an optimization, so PlaybackThread doesn't have to look at the one from AudioFlinger
mMasterMute = muted;
+
+ // Set master mute in the HALs which support it.
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ AutoMutex lock(mHardwareLock);
+ AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
+
+ mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
+ if (dev->canSetMasterMute()) {
+ dev->hwDevice()->set_master_mute(dev->hwDevice(), muted);
+ }
+ mHardwareStatus = AUDIO_HW_IDLE;
+ }
+
+ // Now set the master mute in each playback thread. Playback threads
+ // assigned to HALs which do not have master mute support will apply master
+ // mute during the mix operation. Threads with HALs which do support master
+ // mute will simply ignore the setting.
for (size_t i = 0; i < mPlaybackThreads.size(); i++)
mPlaybackThreads.valueAt(i)->setMasterMute(muted);
@@ -727,12 +751,6 @@ float AudioFlinger::masterVolume() const
return masterVolume_l();
}
-float AudioFlinger::masterVolumeSW() const
-{
- Mutex::Autolock _l(mLock);
- return masterVolumeSW_l();
-}
-
bool AudioFlinger::masterMute() const
{
Mutex::Autolock _l(mLock);
@@ -741,23 +759,14 @@ bool AudioFlinger::masterMute() const
float AudioFlinger::masterVolume_l() const
{
- if (MVS_FULL == mMasterVolumeSupportLvl) {
- float ret_val;
- AutoMutex lock(mHardwareLock);
-
- mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
- ALOG_ASSERT((NULL != mPrimaryHardwareDev) &&
- (NULL != mPrimaryHardwareDev->get_master_volume),
- "can't get master volume");
-
- mPrimaryHardwareDev->get_master_volume(mPrimaryHardwareDev, &ret_val);
- mHardwareStatus = AUDIO_HW_IDLE;
- return ret_val;
- }
-
return mMasterVolume;
}
+bool AudioFlinger::masterMute_l() const
+{
+ return mMasterMute;
+}
+
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
@@ -876,17 +885,19 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&
if (mBtNrecIsOff != btNrecIsOff) {
for (size_t i = 0; i < mRecordThreads.size(); i++) {
sp<RecordThread> thread = mRecordThreads.valueAt(i);
- RecordThread::RecordTrack *track = thread->track();
- if (track != NULL) {
- audio_devices_t device = (audio_devices_t)(
- thread->device() & AUDIO_DEVICE_IN_ALL);
- bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
+ audio_devices_t device = thread->inDevice();
+ bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
+ // collect all of the thread's session IDs
+ KeyedVector<int, bool> ids = thread->sessionIds();
+ // suspend effects associated with those session IDs
+ for (size_t j = 0; j < ids.size(); ++j) {
+ int sessionId = ids.keyAt(j);
thread->setEffectSuspended(FX_IID_AEC,
suspend,
- track->sessionId());
+ sessionId);
thread->setEffectSuspended(FX_IID_NS,
suspend,
- track->sessionId());
+ sessionId);
}
}
mBtNrecIsOff = btNrecIsOff;
@@ -908,7 +919,7 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&
{
Mutex::Autolock _l(mLock);
thread = checkPlaybackThread_l(ioHandle);
- if (thread == NULL) {
+ if (thread == 0) {
thread = checkRecordThread_l(ioHandle);
} else if (thread == primaryPlaybackThread_l()) {
// indicate output device change to all input threads for pre processing
@@ -964,7 +975,8 @@ String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& k
return String8("");
}
-size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const
+size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask) const
{
status_t ret = initCheck();
if (ret != NO_ERROR) {
@@ -975,20 +987,17 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
struct audio_config config = {
sample_rate: sampleRate,
- channel_mask: audio_channel_in_mask_from_count(channelCount),
+ channel_mask: channelMask,
format: format,
};
- size_t size = mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, &config);
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
+ size_t size = dev->get_input_buffer_size(dev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
return size;
}
unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
{
- if (ioHandle == 0) {
- return 0;
- }
-
Mutex::Autolock _l(mLock);
RecordThread *recordThread = checkRecordThread_l(ioHandle);
@@ -1011,8 +1020,9 @@ status_t AudioFlinger::setVoiceVolume(float value)
}
AutoMutex lock(mHardwareLock);
+ audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_VOICE_VOLUME;
- ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value);
+ ret = dev->set_voice_volume(dev, value);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
@@ -1053,11 +1063,11 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
// the config change is always sent from playback or record threads to avoid deadlock
// with AudioSystem::gLock
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+ mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::OUTPUT_OPENED);
}
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
+ mRecordThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::INPUT_OPENED);
}
}
}
@@ -1124,16 +1134,17 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::getEffectThread_l(int sessionId,
// ----------------------------------------------------------------------------
AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
- uint32_t device, type_t type)
- : Thread(false),
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
+ : Thread(false /*canCallJava*/),
mType(type),
mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
// mChannelMask
mChannelCount(0),
mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
mParamStatus(NO_ERROR),
- mStandby(false), mId(id),
- mDevice(device),
+ mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
+ mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
+ // mName will be set by concrete (non-virtual) subclass
mDeathRecipient(new PMDeathRecipient(this))
{
}
@@ -1152,6 +1163,8 @@ AudioFlinger::ThreadBase::~ThreadBase()
void AudioFlinger::ThreadBase::exit()
{
ALOGV("ThreadBase::exit");
+ // do any cleanup required for exit to succeed
+ preExit();
{
// This lock prevents the following race in thread (uniprocessor for illustration):
// if (!exitPending()) {
@@ -1164,7 +1177,7 @@ void AudioFlinger::ThreadBase::exit()
// }
AutoMutex lock(mLock);
requestExit();
- mWaitWorkCV.signal();
+ mWaitWorkCV.broadcast();
}
// When Thread::requestExitAndWait is made virtual and this method is renamed to
// "virtual status_t requestExitAndWait()", replace by "return Thread::requestExitAndWait();"
@@ -1191,20 +1204,28 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
return status;
}
-void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param)
+void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
{
Mutex::Autolock _l(mLock);
- sendConfigEvent_l(event, param);
+ sendIoConfigEvent_l(event, param);
+}
+
+// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
+{
+ IoConfigEvent *ioEvent = new IoConfigEvent(event, param);
+ mConfigEvents.add(static_cast<ConfigEvent *>(ioEvent));
+ ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
+ mWaitWorkCV.signal();
}
-// sendConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
+// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio)
{
- ConfigEvent configEvent;
- configEvent.mEvent = event;
- configEvent.mParam = param;
- mConfigEvents.add(configEvent);
- ALOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
+ PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio);
+ mConfigEvents.add(static_cast<ConfigEvent *>(prioEvent));
+ ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d",
+ mConfigEvents.size(), pid, tid, prio);
mWaitWorkCV.signal();
}
@@ -1213,20 +1234,37 @@ void AudioFlinger::ThreadBase::processConfigEvents()
mLock.lock();
while (!mConfigEvents.isEmpty()) {
ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
- ConfigEvent configEvent = mConfigEvents[0];
+ ConfigEvent *event = mConfigEvents[0];
mConfigEvents.removeAt(0);
// release mLock before locking AudioFlinger mLock: lock order is always
// AudioFlinger then ThreadBase to avoid cross deadlock
mLock.unlock();
- mAudioFlinger->mLock.lock();
- audioConfigChanged_l(configEvent.mEvent, configEvent.mParam);
- mAudioFlinger->mLock.unlock();
+ switch(event->type()) {
+ case CFG_EVENT_PRIO: {
+ PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
+ int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio());
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
+ }
+ } break;
+ case CFG_EVENT_IO: {
+ IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
+ mAudioFlinger->mLock.lock();
+ audioConfigChanged_l(ioEvent->event(), ioEvent->param());
+ mAudioFlinger->mLock.unlock();
+ } break;
+ default:
+ ALOGE("processConfigEvents() unknown event type %d", event->type());
+ break;
+ }
+ delete event;
mLock.lock();
}
mLock.unlock();
}
-status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
+void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1270,10 +1308,8 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args
snprintf(buffer, SIZE, "\n\nPending config events: \n");
result.append(buffer);
- snprintf(buffer, SIZE, " Index event param\n");
- result.append(buffer);
for (size_t i = 0; i < mConfigEvents.size(); i++) {
- snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i].mEvent, mConfigEvents[i].mParam);
+ mConfigEvents[i]->dump(buffer, SIZE);
result.append(buffer);
}
result.append("\n");
@@ -1283,10 +1319,9 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args
if (locked) {
mLock.unlock();
}
- return NO_ERROR;
}
-status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args)
+void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1301,7 +1336,6 @@ status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String1
chain->dump(fd, args);
}
}
- return NO_ERROR;
}
void AudioFlinger::ThreadBase::acquireWakeLock()
@@ -1397,8 +1431,8 @@ void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectCha
return;
}
- KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects =
- mSuspendedSessions.editValueAt(index);
+ const KeyedVector <int, sp<SuspendedSessionDesc> >& sessionEffects =
+ mSuspendedSessions.valueAt(index);
for (size_t i = 0; i < sessionEffects.size(); i++) {
sp<SuspendedSessionDesc> desc = sessionEffects.valueAt(i);
@@ -1424,7 +1458,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty
if (suspend) {
if (index >= 0) {
- sessionEffects = mSuspendedSessions.editValueAt(index);
+ sessionEffects = mSuspendedSessions.valueAt(index);
} else {
mSuspendedSessions.add(sessionId, sessionEffects);
}
@@ -1432,7 +1466,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty
if (index < 0) {
return;
}
- sessionEffects = mSuspendedSessions.editValueAt(index);
+ sessionEffects = mSuspendedSessions.valueAt(index);
}
@@ -1449,7 +1483,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty
} else {
desc = new SuspendedSessionDesc();
if (type != NULL) {
- memcpy(&desc->mType, type, sizeof(effect_uuid_t));
+ desc->mType = *type;
}
sessionEffects.add(key, desc);
ALOGV("updateSuspendedSessions_l() suspend adding effect %08x", key);
@@ -1509,18 +1543,12 @@ void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModu
AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
audio_io_handle_t id,
- uint32_t device,
+ audio_devices_t device,
type_t type)
- : ThreadBase(audioFlinger, id, device, type),
+ : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
- // Assumes constructor is called by AudioFlinger with it's mLock held,
- // but it would be safer to explicitly pass initial masterMute as parameter
- mMasterMute(audioFlinger->masterMute_l()),
// mStreamTypes[] initialized in constructor body
mOutput(output),
- // Assumes constructor is called by AudioFlinger with it's mLock held,
- // but it would be safer to explicitly pass initial masterVolume as parameter
- mMasterVolume(audioFlinger->masterVolumeSW_l()),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
mMixerStatusIgnoringFastTracks(MIXER_IDLE),
@@ -1531,6 +1559,25 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
{
snprintf(mName, kNameLength, "AudioOut_%X", id);
+ // Assumes constructor is called by AudioFlinger with it's mLock held, but
+ // it would be safer to explicitly pass initial masterVolume/masterMute as
+ // parameter.
+ //
+ // If the HAL we are using has support for master volume or master mute,
+ // then do not attenuate or mute during mixing (just leave the volume at 1.0
+ // and the mute set to false).
+ mMasterVolume = audioFlinger->masterVolume_l();
+ mMasterMute = audioFlinger->masterMute_l();
+ if (mOutput && mOutput->audioHwDev) {
+ if (mOutput->audioHwDev->canSetMasterVolume()) {
+ mMasterVolume = 1.0;
+ }
+
+ if (mOutput->audioHwDev->canSetMasterMute()) {
+ mMasterMute = false;
+ }
+ }
+
readOutputParameters();
// mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
@@ -1549,15 +1596,14 @@ AudioFlinger::PlaybackThread::~PlaybackThread()
delete [] mMixBuffer;
}
-status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
{
dumpInternals(fd, args);
dumpTracks(fd, args);
dumpEffectChains(fd, args);
- return NO_ERROR;
}
-status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1605,11 +1651,9 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>
FastTrackUnderruns underruns = getFastTrackUnderruns(0);
fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n",
underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
-
- return NO_ERROR;
}
-status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1633,8 +1677,6 @@ status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String
fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
dumpBase(fd, args);
-
- return NO_ERROR;
}
// Thread virtuals
@@ -1654,13 +1696,22 @@ void AudioFlinger::PlaybackThread::onFirstRef()
run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
}
+// ThreadBase virtuals
+void AudioFlinger::PlaybackThread::preExit()
+{
+ ALOGV(" preExit()");
+ // FIXME this is using hard-coded strings but in the future, this functionality will be
+ // converted to use audio HAL extensions required to support tunneling
+ mOutput->stream->common.set_parameters(&mOutput->stream->common, "exiting=1");
+}
+
// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
@@ -1688,7 +1739,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
(
(tid != -1) &&
((frameCount == 0) ||
- (frameCount >= (int) (mFrameCount * 2))) // * 2 is due to SRC jitter, see below
+ (frameCount >= (int) (mFrameCount * kFastTrackMultiplier)))
)
) &&
// PCM data
@@ -1709,13 +1760,13 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
) {
// if frameCount not specified, then it defaults to fast mixer (HAL) frame count
if (frameCount == 0) {
- frameCount = mFrameCount * 2; // FIXME * 2 is due to SRC jitter, should be computed
+ frameCount = mFrameCount * kFastTrackMultiplier;
}
ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
frameCount, mFrameCount);
} else {
ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
- "mFrameCount=%d format=%d isLinear=%d channelMask=%d sampleRate=%d mSampleRate=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%d mSampleRate=%d "
"hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
isTimed, sharedBuffer.get(), frameCount, mFrameCount, format,
audio_is_linear_pcm(format),
@@ -1789,7 +1840,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
track = TimedTrack::create(this, client, streamType, sampleRate, format,
channelMask, frameCount, sharedBuffer, sessionId);
}
- if (track == NULL || track->getCblk() == NULL || track->name() < 0) {
+ if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
lStatus = NO_MEMORY;
goto Exit;
}
@@ -1802,20 +1853,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
chain->incTrackCnt();
}
- }
-#ifdef HAVE_REQUEST_PRIORITY
- if ((flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
- pid_t callingPid = IPCThreadState::self()->getCallingPid();
- // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
- // so ask activity manager to do this on our behalf
- int err = requestPriority(callingPid, tid, 1);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- 1, callingPid, tid, err);
+ if ((flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
+ // so ask activity manager to do this on our behalf
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
}
}
-#endif
lStatus = NO_ERROR;
@@ -1857,13 +1902,25 @@ uint32_t AudioFlinger::PlaybackThread::latency_l() const
void AudioFlinger::PlaybackThread::setMasterVolume(float value)
{
Mutex::Autolock _l(mLock);
- mMasterVolume = value;
+ // Don't apply master volume in SW if our HAL can do it for us.
+ if (mOutput && mOutput->audioHwDev &&
+ mOutput->audioHwDev->canSetMasterVolume()) {
+ mMasterVolume = 1.0;
+ } else {
+ mMasterVolume = value;
+ }
}
void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
{
Mutex::Autolock _l(mLock);
- setMasterMute_l(muted);
+ // Don't apply master mute in SW if our HAL can do it for us.
+ if (mOutput && mOutput->audioHwDev &&
+ mOutput->audioHwDev->canSetMasterMute()) {
+ mMasterMute = false;
+ } else {
+ mMasterMute = muted;
+ }
}
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
@@ -2070,10 +2127,20 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui
}
*halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
+ if (isSuspended()) {
+ // return an estimation of rendered frames when the output is suspended
+ int32_t frames = mBytesWritten - latency_l();
+ if (frames < 0) {
+ frames = 0;
+ }
+ *dspFrames = (uint32_t)frames;
+ return NO_ERROR;
+ } else {
+ return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
+ }
}
-uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) const
{
Mutex::Autolock _l(mLock);
uint32_t result = 0;
@@ -2155,7 +2222,7 @@ status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event)
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (event->triggerSession() == track->sessionId()) {
- track->setSyncEvent(event);
+ (void) track->setSyncEvent(event);
return NO_ERROR;
}
}
@@ -2163,15 +2230,9 @@ status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event)
return NAME_NOT_FOUND;
}
-bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event)
+bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) const
{
- switch (event->type()) {
- case AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE:
- return true;
- default:
- break;
- }
- return false;
+ return event->type() == AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE;
}
void AudioFlinger::PlaybackThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove)
@@ -2192,28 +2253,25 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(const Vector< sp<Trac
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device, type_t type)
+ audio_io_handle_t id, audio_devices_t device, type_t type)
: PlaybackThread(audioFlinger, output, id, device, type),
// mAudioMixer below
-#ifdef SOAKER
- mSoaker(NULL),
-#endif
// mFastMixer below
mFastMixerFutex(0)
// mOutputSink below
// mPipeSink below
// mNormalSink below
{
- ALOGV("MixerThread() id=%d device=%d type=%d", id, device, type);
- ALOGV("mSampleRate=%d, mChannelMask=%d, mChannelCount=%d, mFormat=%d, mFrameSize=%d, "
+ ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
+ ALOGV("mSampleRate=%d, mChannelMask=%#x, mChannelCount=%d, mFormat=%d, mFrameSize=%d, "
"mFrameCount=%d, mNormalFrameCount=%d",
mSampleRate, mChannelMask, mChannelCount, mFormat, mFrameSize, mFrameCount,
mNormalFrameCount);
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
// FIXME - Current mixer implementation only supports stereo output
- if (mChannelCount == 1) {
- ALOGE("Invalid audio hardware channel count");
+ if (mChannelCount != FCC_2) {
+ ALOGE("Invalid audio hardware channel count %d", mChannelCount);
}
// create an NBAIO sink for the HAL output stream, and negotiate
@@ -2267,13 +2325,6 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
mTeeSource = teeSource;
#endif
-#ifdef SOAKER
- // create a soaker as workaround for governor issues
- mSoaker = new Soaker();
- // FIXME Soaker should only run when needed, i.e. when FastMixer is not in COLD_IDLE
- mSoaker->run("Soaker", PRIORITY_LOWEST);
-#endif
-
// create fast mixer and configure it initially with just one fast track for our submix
mFastMixer = new FastMixer();
FastMixerStateQueue *sq = mFastMixer->sq();
@@ -2305,14 +2356,12 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
// start the fast mixer
mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
-#ifdef HAVE_REQUEST_PRIORITY
pid_t tid = mFastMixer->getTid();
- int err = requestPriority(getpid_cached, tid, 2);
+ int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
if (err != 0) {
ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- 2, getpid_cached, tid, err);
+ kPriorityFastMixer, getpid_cached, tid, err);
}
-#endif
#ifdef AUDIO_WATCHDOG
// create and start the watchdog
@@ -2320,10 +2369,10 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
mAudioWatchdog->setDump(&mAudioWatchdogDump);
mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
tid = mAudioWatchdog->getTid();
- err = requestPriority(getpid_cached, tid, 1);
+ err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
if (err != 0) {
ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- 1, getpid_cached, tid, err);
+ kPriorityFastMixer, getpid_cached, tid, err);
}
#endif
@@ -2370,17 +2419,13 @@ AudioFlinger::MixerThread::~MixerThread()
delete fastTrack->mBufferProvider;
sq->end(false /*didModify*/);
delete mFastMixer;
-#ifdef SOAKER
- if (mSoaker != NULL) {
- mSoaker->requestExitAndWait();
- }
- delete mSoaker;
-#endif
+#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
mAudioWatchdog->requestExit();
mAudioWatchdog->requestExitAndWait();
mAudioWatchdog.clear();
}
+#endif
}
delete mAudioMixer;
}
@@ -2508,9 +2553,6 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// MIXER
nsecs_t lastWarning = 0;
-if (mType == MIXER) {
- longStandbyExit = false;
-}
// DUPLICATING
// FIXME could this be made local to while loop?
@@ -2519,9 +2561,9 @@ if (mType == MIXER) {
cacheParameters_l();
sleepTime = idleSleepTime;
-if (mType == MIXER) {
- sleepTimeShift = 0;
-}
+ if (mType == MIXER) {
+ sleepTimeShift = 0;
+ }
CpuStats cpuStats;
const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid()));
@@ -2548,13 +2590,12 @@ if (mType == MIXER) {
// put audio hardware into standby after short delay
if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
- mSuspended > 0)) {
+ isSuspended())) {
if (!mStandby) {
threadLoop_standby();
mStandby = true;
- mBytesWritten = 0;
}
if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
@@ -2574,6 +2615,7 @@ if (mType == MIXER) {
mMixerStatus = MIXER_IDLE;
mMixerStatusIgnoringFastTracks = MIXER_IDLE;
+ mBytesWritten = 0;
checkSilentMode_l();
@@ -2602,8 +2644,9 @@ if (mType == MIXER) {
threadLoop_sleepTime();
}
- if (mSuspended > 0) {
+ if (isSuspended()) {
sleepTime = suspendSleepTimeUs();
+ mBytesWritten += mixBufferSize;
}
// only process effects if we're going to write
@@ -2635,11 +2678,6 @@ if (mType == MIXER) {
ns2ms(delta), mNumDelayedWrites, this);
lastWarning = now;
}
- // FIXME this is broken: longStandbyExit should be handled out of the if() and with
- // a different threshold. Or completely removed for what it is worth anyway...
- if (mStandby) {
- longStandbyExit = true;
- }
}
}
@@ -2667,15 +2705,13 @@ if (mType == MIXER) {
// is now local to this block, but will keep it for now (at least until merge done).
}
-if (mType == MIXER || mType == DIRECT) {
- // put output stream into standby mode
- if (!mStandby) {
- mOutput->stream->common.standby(&mOutput->stream->common);
+ // for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ...
+ if (mType == MIXER || mType == DIRECT) {
+ // put output stream into standby mode
+ if (!mStandby) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ }
}
-}
-if (mType == DUPLICATING) {
- // for DuplicatingThread, standby mode is handled by the outputTracks
-}
releaseWakeLock();
@@ -2702,9 +2738,11 @@ void AudioFlinger::MixerThread::threadLoop_write()
if (old == -1) {
__futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
+#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
mAudioWatchdog->resume();
}
+#endif
}
state->mCommand = FastMixerState::MIX_WRITE;
sq->end();
@@ -2781,9 +2819,11 @@ void AudioFlinger::MixerThread::threadLoop_standby()
if (kUseFastMixer == FastMixer_Dynamic) {
mNormalSink = mOutputSink;
}
+#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
mAudioWatchdog->pause();
}
+#endif
} else {
sq->end(false /*didModify*/);
}
@@ -2794,7 +2834,7 @@ void AudioFlinger::MixerThread::threadLoop_standby()
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
- ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
+ ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
}
@@ -2804,9 +2844,10 @@ void AudioFlinger::MixerThread::threadLoop_mix()
int64_t pts;
status_t status = INVALID_OPERATION;
- if (NULL != mOutput->stream->get_next_write_timestamp) {
- status = mOutput->stream->get_next_write_timestamp(
- mOutput->stream, &pts);
+ if (mNormalSink != 0) {
+ status = mNormalSink->getNextWriteTimestamp(&pts);
+ } else {
+ status = mOutputSink->getNextWriteTimestamp(&pts);
}
if (status != NO_ERROR) {
@@ -2847,11 +2888,10 @@ void AudioFlinger::MixerThread::threadLoop_sleepTime()
} else {
sleepTime = idleSleepTime;
}
- } else if (mBytesWritten != 0 ||
- (mMixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
+ } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
memset (mMixBuffer, 0, mixBufferSize);
sleepTime = 0;
- ALOGV_IF((mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+ ALOGV_IF((mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED)), "anticipated start");
}
// TODO add standby time extension fct of effect tail
}
@@ -2993,7 +3033,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
size_t framesWritten =
mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (!track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
// track stays in active list until presentation is complete
break;
}
@@ -3235,11 +3275,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// Remove it from the list of active tracks.
// TODO: use actual buffer filling status instead of latency when available from
// audio HAL
- size_t audioHALFrames =
- (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
size_t framesWritten =
mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
track->reset();
}
@@ -3296,9 +3335,11 @@ track_is_ready: ;
sq->end(didModify);
sq->push(block);
}
+#ifdef AUDIO_WATCHDOG
if (pauseAudioWatchdog && mAudioWatchdog != 0) {
mAudioWatchdog->pause();
}
+#endif
// Now perform the deferred reset on fast tracks that have stopped
while (resetMask != 0) {
@@ -3373,7 +3414,7 @@ void AudioFlinger::PlaybackThread::cacheParameters_l()
idleSleepTime = idleSleepTimeUs();
}
-void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType)
+void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
{
ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
this, streamType, mTracks.size());
@@ -3390,9 +3431,9 @@ void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType)
}
// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask)
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
{
- return mAudioMixer->getTrackName(channelMask);
+ return mAudioMixer->getTrackName(channelMask, sessionId);
}
// deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -3460,14 +3501,14 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
#ifdef ADD_BATTERY_DATA
// when changing the audio output device, call addBatteryData to notify
// the change
- if ((int)mDevice != value) {
+ if (mOutDevice != value) {
uint32_t params = 0;
// check whether speaker is on
if (value & AUDIO_DEVICE_OUT_SPEAKER) {
params |= IMediaPlayerService::kBatteryDataSpeakerOn;
}
- int deviceWithoutSpeaker
+ audio_devices_t deviceWithoutSpeaker
= AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
// check if any other device (except speaker) is on
if (value & deviceWithoutSpeaker ) {
@@ -3482,9 +3523,9 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
// forward device change to effects that have requested to be
// aware of attached audio device.
- mDevice = (uint32_t)value;
+ mOutDevice = value;
for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mDevice);
+ mEffectChains[i]->setDevice_l(mOutDevice);
}
}
@@ -3505,7 +3546,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
readOutputParameters();
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
- int name = getTrackName_l((audio_channel_mask_t)mTracks[i]->mChannelMask);
+ int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
if (name < 0) break;
mTracks[i]->mName = name;
// limit track sample rate to 2 x new output sample rate
@@ -3513,7 +3554,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
}
}
- sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
}
}
@@ -3539,7 +3580,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
return reconfig;
}
-status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -3593,7 +3634,8 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
#define TEE_SINK_READ 1024
short buffer[TEE_SINK_READ * FCC_2];
size_t count = TEE_SINK_READ;
- ssize_t actual = teeSource->read(buffer, count);
+ ssize_t actual = teeSource->read(buffer, count,
+ AudioBufferProvider::kInvalidPTS);
bool wasFirstRead = firstRead;
firstRead = false;
if (actual <= 0) {
@@ -3602,7 +3644,7 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
}
break;
}
- ALOG_ASSERT(actual <= count);
+ ALOG_ASSERT(actual <= (ssize_t)count);
write(teeFd, buffer, actual * channelCount * sizeof(short));
total += actual;
}
@@ -3619,13 +3661,13 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
}
}
+#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
// Make a non-atomic copy of audio watchdog dump so it won't change underneath us
AudioWatchdogDump wdCopy = mAudioWatchdogDump;
wdCopy.dump(fd);
}
-
- return NO_ERROR;
+#endif
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
@@ -3651,7 +3693,7 @@ void AudioFlinger::MixerThread::cacheParameters_l()
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
+ AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device)
: PlaybackThread(audioFlinger, output, id, device, DIRECT)
// mLeftVolFloat, mRightVolFloat
{
@@ -3756,11 +3798,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
// We have consumed all the buffers of this track.
// Remove it from the list of active tracks.
// TODO: implement behavior for compressed audio
- size_t audioHALFrames =
- (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
size_t framesWritten =
mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
track->reset();
}
@@ -3836,7 +3877,8 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
}
// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask)
+int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask,
+ int sessionId)
{
return 0;
}
@@ -3879,7 +3921,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
}
if (status == NO_ERROR && reconfig) {
readOutputParameters();
- sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
}
}
@@ -3940,7 +3982,7 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l()
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
AudioFlinger::MixerThread* mainThread, audio_io_handle_t id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(), DUPLICATING),
mWaitTimeMs(UINT_MAX)
{
addOutputTrack(mainThread);
@@ -4070,6 +4112,7 @@ bool AudioFlinger::DuplicatingThread::outputsReady(const SortedVector< sp<Output
return false;
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ // see note at standby() declaration
if (playbackThread->standby() && !playbackThread->isSuspended()) {
ALOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get());
return false;
@@ -4099,7 +4142,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
const sp<Client>& client,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId)
@@ -4274,7 +4317,7 @@ AudioFlinger::PlaybackThread::Track::Track(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
@@ -4300,7 +4343,7 @@ AudioFlinger::PlaybackThread::Track::Track(
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t);
// to avoid leaking a track name, do not allocate one unless there is an mCblk
- mName = thread->getTrackName_l((audio_channel_mask_t)channelMask);
+ mName = thread->getTrackName_l(channelMask, sessionId);
mCblk->mName = mName;
if (mName < 0) {
ALOGE("no more track names available");
@@ -4329,11 +4372,6 @@ AudioFlinger::PlaybackThread::Track::Track(
AudioFlinger::PlaybackThread::Track::~Track()
{
ALOGV("PlaybackThread::Track destructor");
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- mState = TERMINATED;
- }
}
void AudioFlinger::PlaybackThread::Track::destroy()
@@ -4496,8 +4534,6 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
}
buffer->raw = getBuffer(s, framesReq);
- if (buffer->raw == NULL) goto getNextBuffer_exit;
-
buffer->frameCount = framesReq;
return NO_ERROR;
}
@@ -4645,7 +4681,7 @@ void AudioFlinger::PlaybackThread::Track::flush()
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
- mState != PAUSING) {
+ mState != PAUSING && mState != IDLE && mState != FLUSHED) {
return;
}
// No point remaining in PAUSED state after a flush => go to
@@ -4808,7 +4844,7 @@ status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>&
event->cancel();
return INVALID_OPERATION;
}
- TrackBase::setSyncEvent(event);
+ (void) TrackBase::setSyncEvent(event);
return NO_ERROR;
}
@@ -4821,12 +4857,12 @@ AudioFlinger::PlaybackThread::TimedTrack::create(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId) {
if (!client->reserveTimedTrack())
- return NULL;
+ return 0;
return new TimedTrack(
thread, client, streamType, sampleRate, format, channelMask, frameCount,
@@ -4839,7 +4875,7 @@ AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId)
@@ -5061,7 +5097,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
AudioBufferProvider::Buffer* buffer, int64_t pts)
{
if (pts == AudioBufferProvider::kInvalidPTS) {
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
mTimedAudioOutputOnTime = false;
return INVALID_OPERATION;
@@ -5076,7 +5112,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
// if we have no timed buffers, then fail
if (mTimedBufferQueue.isEmpty()) {
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
return NOT_ENOUGH_DATA;
}
@@ -5103,7 +5139,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
// the transform failed. this shouldn't happen, but if it does
// then just drop this buffer
ALOGW("timedGetNextBuffer transform failed");
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
trimTimedBufferQueueHead_l("getNextBuffer; no transform");
return NO_ERROR;
@@ -5112,7 +5148,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) {
if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS,
&headLocalPTS)) {
- buffer->raw = 0;
+ buffer->raw = NULL;
buffer->frameCount = 0;
return INVALID_OPERATION;
}
@@ -5334,7 +5370,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
const sp<Client>& client,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
int sessionId)
: TrackBase(thread, client, sampleRate, format,
@@ -5355,10 +5391,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- AudioSystem::releaseInput(thread->id());
- }
+ ALOGV("%s", __func__);
}
// AudioBufferProvider interface
@@ -5389,8 +5422,6 @@ status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvi
}
buffer->raw = getBuffer(s, framesReq);
- if (buffer->raw == NULL) goto getNextBuffer_exit;
-
buffer->frameCount = framesReq;
return NO_ERROR;
}
@@ -5418,17 +5449,29 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
- recordThread->stop(this);
- TrackBase::reset();
- // Force overrun condition to avoid false overrun callback until first data is
- // read from buffer
- android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
+ recordThread->mLock.lock();
+ bool doStop = recordThread->stop_l(this);
+ if (doStop) {
+ TrackBase::reset();
+ // Force overrun condition to avoid false overrun callback until first data is
+ // read from buffer
+ android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
+ }
+ recordThread->mLock.unlock();
+ if (doStop) {
+ AudioSystem::stopInput(recordThread->id());
+ }
}
}
+/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
+{
+ result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User FrameCount\n");
+}
+
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x\n",
+ snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x %05d\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
@@ -5437,7 +5480,8 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
mState,
mCblk->sampleRate,
mCblk->server,
- mCblk->user);
+ mCblk->user,
+ mCblk->frameCount);
}
@@ -5448,7 +5492,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
DuplicatingThread *sourceThread,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
NULL, 0, IAudioFlinger::TRACK_DEFAULT),
@@ -5836,9 +5880,10 @@ sp<IAudioRecord> AudioFlinger::openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
IAudioFlinger::track_flags_t flags,
+ pid_t tid,
int *sessionId,
status_t *status)
{
@@ -5877,13 +5922,8 @@ sp<IAudioRecord> AudioFlinger::openRecord(
}
}
// create new record track. The record track uses one track in mHardwareMixerThread by convention.
- recordTrack = thread->createRecordTrack_l(client,
- sampleRate,
- format,
- channelMask,
- frameCount,
- lSessionId,
- &lStatus);
+ recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
+ frameCount, lSessionId, flags, tid, &lStatus);
}
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the RecordTrack so that the Client
@@ -5913,19 +5953,24 @@ AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::Re
}
AudioFlinger::RecordHandle::~RecordHandle() {
- stop();
+ stop_nonvirtual();
+ mRecordTrack->destroy();
}
sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
return mRecordTrack->getCblk();
}
-status_t AudioFlinger::RecordHandle::start(int event, int triggerSession) {
+status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) {
ALOGV("RecordHandle::start()");
return mRecordTrack->start((AudioSystem::sync_event_t)event, triggerSession);
}
void AudioFlinger::RecordHandle::stop() {
+ stop_nonvirtual();
+}
+
+void AudioFlinger::RecordHandle::stop_nonvirtual() {
ALOGV("RecordHandle::stop()");
mRecordTrack->stop();
}
@@ -5941,13 +5986,13 @@ status_t AudioFlinger::RecordHandle::onTransact(
AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamIn *input,
uint32_t sampleRate,
- uint32_t channels,
+ audio_channel_mask_t channelMask,
audio_io_handle_t id,
- uint32_t device) :
- ThreadBase(audioFlinger, id, device, RECORD),
- mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ audio_devices_t device) :
+ ThreadBase(audioFlinger, id, AUDIO_DEVICE_NONE, device, RECORD),
+ mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
// mRsmpInIndex and mInputBytes set by readInputParameters()
- mReqChannelCount(popcount(channels)),
+ mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
// (but might be better to also clear here for dump?)
@@ -5985,8 +6030,12 @@ bool AudioFlinger::RecordThread::threadLoop()
nsecs_t lastWarning = 0;
+ inputStandBy();
acquireWakeLock();
+ // used to verify we've read at least once before evaluating how many bytes were read
+ bool readOnce = false;
+
// start recording
while (!exitPending()) {
@@ -5996,10 +6045,7 @@ bool AudioFlinger::RecordThread::threadLoop()
Mutex::Autolock _l(mLock);
checkForNewParameters_l();
if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
- if (!mStandby) {
- mInput->stream->common.standby(&mInput->stream->common);
- mStandby = true;
- }
+ standby();
if (exitPending()) break;
@@ -6013,20 +6059,17 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (mActiveTrack != 0) {
if (mActiveTrack->mState == TrackBase::PAUSING) {
- if (!mStandby) {
- mInput->stream->common.standby(&mInput->stream->common);
- mStandby = true;
- }
+ standby();
mActiveTrack.clear();
mStartStopCond.broadcast();
} else if (mActiveTrack->mState == TrackBase::RESUMING) {
if (mReqChannelCount != mActiveTrack->channelCount()) {
mActiveTrack.clear();
mStartStopCond.broadcast();
- } else if (mBytesRead != 0) {
+ } else if (readOnce) {
// record start succeeds only if first read from audio input
// succeeds
- if (mBytesRead > 0) {
+ if (mBytesRead >= 0) {
mActiveTrack->mState = TrackBase::ACTIVE;
} else {
mActiveTrack.clear();
@@ -6034,6 +6077,9 @@ bool AudioFlinger::RecordThread::threadLoop()
mStartStopCond.broadcast();
}
mStandby = false;
+ } else if (mActiveTrack->mState == TrackBase::TERMINATED) {
+ removeTrack_l(mActiveTrack);
+ mActiveTrack.clear();
}
}
lockEffectChains_l(effectChains);
@@ -6052,6 +6098,7 @@ bool AudioFlinger::RecordThread::threadLoop()
buffer.frameCount = mFrameCount;
if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ readOnce = true;
size_t framesOut = buffer.frameCount;
if (mResampler == NULL) {
// no resampling
@@ -6068,18 +6115,12 @@ bool AudioFlinger::RecordThread::threadLoop()
mFormat != AUDIO_FORMAT_PCM_16_BIT) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
- int16_t *src16 = (int16_t *)src;
- int16_t *dst16 = (int16_t *)dst;
if (mChannelCount == 1) {
- while (framesIn--) {
- *dst16++ = *src16;
- *dst16++ = *src16++;
- }
+ upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
} else {
- while (framesIn--) {
- *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1);
- src16 += 2;
- }
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
}
}
}
@@ -6092,12 +6133,13 @@ bool AudioFlinger::RecordThread::threadLoop()
mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
mRsmpInIndex = 0;
}
- if (mBytesRead < 0) {
- ALOGE("Error reading audio input");
- if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ if (mBytesRead <= 0) {
+ if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
+ {
+ ALOGE("Error reading audio input");
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->stream->common.standby(&mInput->stream->common);
+ inputStandBy();
usleep(kRecordThreadSleepUs);
}
mRsmpInIndex = mFrameCount;
@@ -6114,18 +6156,14 @@ bool AudioFlinger::RecordThread::threadLoop()
if (mChannelCount == 1 && mReqChannelCount == 1) {
framesOut >>= 1;
}
- mResampler->resample(mRsmpOutBuffer, framesOut, this);
+ mResampler->resample(mRsmpOutBuffer, framesOut, this /* AudioBufferProvider* */);
// ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
// are 32 bit aligned which should be always true.
if (mChannelCount == 2 && mReqChannelCount == 1) {
ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
// the resampler always outputs stereo samples: do post stereo to mono conversion
- int16_t *src = (int16_t *)mRsmpOutBuffer;
- int16_t *dst = buffer.i16;
- while (framesOut--) {
- *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1);
- src += 2;
- }
+ downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
+ framesOut);
} else {
ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
}
@@ -6151,7 +6189,7 @@ bool AudioFlinger::RecordThread::threadLoop()
}
}
}
- mActiveTrack->overflow();
+ mActiveTrack->clearOverflow();
}
// client isn't retrieving buffers fast enough
else {
@@ -6173,12 +6211,13 @@ bool AudioFlinger::RecordThread::threadLoop()
effectChains.clear();
}
- if (!mStandby) {
- mInput->stream->common.standby(&mInput->stream->common);
- }
- mActiveTrack.clear();
+ standby();
- mStartStopCond.broadcast();
+ {
+ Mutex::Autolock _l(mLock);
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ }
releaseWakeLock();
@@ -6186,14 +6225,28 @@ bool AudioFlinger::RecordThread::threadLoop()
return false;
}
+void AudioFlinger::RecordThread::standby()
+{
+ if (!mStandby) {
+ inputStandBy();
+ mStandby = true;
+ }
+}
+
+void AudioFlinger::RecordThread::inputStandBy()
+{
+ mInput->stream->common.standby(&mInput->stream->common);
+}
sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
audio_format_t format,
- int channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
int sessionId,
+ IAudioFlinger::track_flags_t flags,
+ pid_t tid,
status_t *status)
{
sp<RecordTrack> track;
@@ -6205,6 +6258,8 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
goto Exit;
}
+ // FIXME use flags and tid similar to createTrack_l()
+
{ // scope for mLock
Mutex::Autolock _l(mLock);
@@ -6215,11 +6270,11 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
lStatus = NO_MEMORY;
goto Exit;
}
+ mTracks.add(track);
- mTrack = track.get();
// disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- bool suspend = audio_is_bluetooth_sco_device(
- (audio_devices_t)(mDevice & AUDIO_DEVICE_IN_ALL)) && mAudioFlinger->btNrecIsOff();
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
}
@@ -6287,7 +6342,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
mActiveTrack->mState = TrackBase::RESUMING;
// signal thread to start
ALOGV("Signal record thread");
- mWaitWorkCV.signal();
+ mWaitWorkCV.broadcast();
// do not wait for mStartStopCond if exiting
if (exitPending()) {
mActiveTrack.clear();
@@ -6337,50 +6392,97 @@ void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event
}
}
-void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
+bool AudioFlinger::RecordThread::stop_l(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
- sp<ThreadBase> strongMe = this;
- {
- AutoMutex lock(mLock);
- if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
- mActiveTrack->mState = TrackBase::PAUSING;
- // do not wait for mStartStopCond if exiting
- if (exitPending()) {
- return;
- }
- mStartStopCond.wait(mLock);
- // if we have been restarted, recordTrack == mActiveTrack.get() here
- if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) {
- mLock.unlock();
- AudioSystem::stopInput(mId);
- mLock.lock();
- ALOGV("Record stopped OK");
- }
- }
+ if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
+ return false;
}
+ recordTrack->mState = TrackBase::PAUSING;
+ // do not wait for mStartStopCond if exiting
+ if (exitPending()) {
+ return true;
+ }
+ mStartStopCond.wait(mLock);
+ // if we have been restarted, recordTrack == mActiveTrack.get() here
+ if (exitPending() || recordTrack != mActiveTrack.get()) {
+ ALOGV("Record stopped OK");
+ return true;
+ }
+ return false;
}
-bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event)
+bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const
{
return false;
}
status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
{
+#if 0 // This branch is currently dead code, but is preserved in case it will be needed in future
if (!isValidSyncEvent(event)) {
return BAD_VALUE;
}
+ int eventSession = event->triggerSession();
+ status_t ret = NAME_NOT_FOUND;
+
Mutex::Autolock _l(mLock);
- if (mTrack != NULL && event->triggerSession() == mTrack->sessionId()) {
- mTrack->setSyncEvent(event);
- return NO_ERROR;
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ if (eventSession == track->sessionId()) {
+ (void) track->setSyncEvent(event);
+ ret = NO_ERROR;
+ }
+ }
+ return ret;
+#else
+ return BAD_VALUE;
+#endif
+}
+
+void AudioFlinger::RecordThread::RecordTrack::destroy()
+{
+ // see comments at AudioFlinger::PlaybackThread::Track::destroy()
+ sp<RecordTrack> keep(this);
+ {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopInput(thread->id());
+ }
+ AudioSystem::releaseInput(thread->id());
+ Mutex::Autolock _l(thread->mLock);
+ RecordThread *recordThread = (RecordThread *) thread.get();
+ recordThread->destroyTrack_l(this);
+ }
+ }
+}
+
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
+{
+ track->mState = TrackBase::TERMINATED;
+ // active tracks are removed by threadLoop()
+ if (mActiveTrack != track) {
+ removeTrack_l(track);
}
- return NAME_NOT_FOUND;
}
-status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track)
+{
+ mTracks.remove(track);
+ // need anything related to effects here?
+}
+
+void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
+}
+
+void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -6390,11 +6492,6 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
result.append(buffer);
if (mActiveTrack != 0) {
- result.append("Active Track:\n");
- result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User\n");
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
-
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
@@ -6405,17 +6502,41 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate);
result.append(buffer);
-
-
} else {
- result.append("No record client\n");
+ result.append("No active record client\n");
}
+
write(fd, result.string(), result.size());
dumpBase(fd, args);
- dumpEffectChains(fd, args);
+}
- return NO_ERROR;
+void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Input thread %p tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<RecordTrack> track = mTracks[i];
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ if (mActiveTrack != 0) {
+ snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ mActiveTrack->dump(buffer, SIZE);
+ result.append(buffer);
+
+ }
+ write(fd, result.string(), result.size());
}
// AudioBufferProvider interface
@@ -6427,12 +6548,12 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer*
if (framesReady == 0) {
mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
- if (mBytesRead < 0) {
- ALOGE("RecordThread::getNextBuffer() Error reading audio input");
- if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ if (mBytesRead <= 0) {
+ if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
+ ALOGE("RecordThread::getNextBuffer() Error reading audio input");
// Force input into standby so that it tries to
// recover at next read attempt
- mInput->stream->common.standby(&mInput->stream->common);
+ inputStandBy();
usleep(kRecordThreadSleepUs);
}
buffer->raw = NULL;
@@ -6505,28 +6626,40 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
for (size_t i = 0; i < mEffectChains.size(); i++) {
mEffectChains[i]->setDevice_l(value);
}
+
// store input device and output device but do not forward output device to audio HAL.
// Note that status is ignored by the caller for output device
// (see AudioFlinger::setParameters()
- if (value & AUDIO_DEVICE_OUT_ALL) {
- mDevice &= (uint32_t)~(value & AUDIO_DEVICE_OUT_ALL);
+ if (audio_is_output_devices(value)) {
+ mOutDevice = value;
status = BAD_VALUE;
} else {
- mDevice &= (uint32_t)~(value & AUDIO_DEVICE_IN_ALL);
+ mInDevice = value;
// disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- if (mTrack != NULL) {
- bool suspend = audio_is_bluetooth_sco_device(
- (audio_devices_t)value) && mAudioFlinger->btNrecIsOff();
- setEffectSuspended_l(FX_IID_AEC, suspend, mTrack->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, mTrack->sessionId());
+ if (mTracks.size() > 0) {
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+ }
}
}
- mDevice |= (uint32_t)value;
+ }
+ if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
+ mAudioSource != (audio_source_t)value) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setAudioSource_l((audio_source_t)value);
+ }
+ mAudioSource = (audio_source_t)value;
}
if (status == NO_ERROR) {
status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
if (status == INVALID_OPERATION) {
- mInput->stream->common.standby(&mInput->stream->common);
+ inputStandBy();
status = mInput->stream->common.set_parameters(&mInput->stream->common,
keyValuePair.string());
}
@@ -6541,7 +6674,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
}
if (status == NO_ERROR) {
readInputParameters();
- sendConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+ sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
}
}
}
@@ -6648,7 +6781,7 @@ unsigned int AudioFlinger::RecordThread::getInputFramesLost()
return mInput->stream->get_input_frames_lost(mInput->stream);
}
-uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId)
+uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) const
{
Mutex::Autolock _l(mLock);
uint32_t result = 0;
@@ -6656,23 +6789,28 @@ uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId)
result = EFFECT_SESSION;
}
- if (mTrack != NULL && sessionId == mTrack->sessionId()) {
- result |= TRACK_SESSION;
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ if (sessionId == mTracks[i]->sessionId()) {
+ result |= TRACK_SESSION;
+ break;
+ }
}
return result;
}
-AudioFlinger::RecordThread::RecordTrack* AudioFlinger::RecordThread::track()
+KeyedVector<int, bool> AudioFlinger::RecordThread::sessionIds() const
{
+ KeyedVector<int, bool> ids;
Mutex::Autolock _l(mLock);
- return mTrack;
-}
-
-AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::getInput() const
-{
- Mutex::Autolock _l(mLock);
- return mInput;
+ for (size_t j = 0; j < mTracks.size(); ++j) {
+ sp<RecordThread::RecordTrack> track = mTracks[j];
+ int sessionId = track->sessionId();
+ if (ids.indexOfKey(sessionId) < 0) {
+ ids.add(sessionId, true);
+ }
+ }
+ return ids;
}
AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput()
@@ -6730,16 +6868,52 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
return 0;
}
- if ((mMasterVolumeSupportLvl != MVS_NONE) &&
- (NULL != dev->set_master_volume)) {
+ // Check and cache this HAL's level of support for master mute and master
+ // volume. If this is the first HAL opened, and it supports the get
+ // methods, use the initial values provided by the HAL as the current
+ // master mute and volume settings.
+
+ AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
+ { // scope for auto-lock pattern
AutoMutex lock(mHardwareLock);
+
+ if (0 == mAudioHwDevs.size()) {
+ mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
+ if (NULL != dev->get_master_volume) {
+ float mv;
+ if (OK == dev->get_master_volume(dev, &mv)) {
+ mMasterVolume = mv;
+ }
+ }
+
+ mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
+ if (NULL != dev->get_master_mute) {
+ bool mm;
+ if (OK == dev->get_master_mute(dev, &mm)) {
+ mMasterMute = mm;
+ }
+ }
+ }
+
mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- dev->set_master_volume(dev, mMasterVolume);
+ if ((NULL != dev->set_master_volume) &&
+ (OK == dev->set_master_volume(dev, mMasterVolume))) {
+ flags = static_cast<AudioHwDevice::Flags>(flags |
+ AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
+ }
+
+ mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
+ if ((NULL != dev->set_master_mute) &&
+ (OK == dev->set_master_mute(dev, mMasterMute))) {
+ flags = static_cast<AudioHwDevice::Flags>(flags |
+ AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
+ }
+
mHardwareStatus = AUDIO_HW_IDLE;
}
audio_module_handle_t handle = nextUniqueId();
- mAudioHwDevs.add(handle, new AudioHwDevice(name, dev));
+ mAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags));
ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d",
name, dev->common.module->name, dev->common.module->id, handle);
@@ -6748,6 +6922,24 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
}
+// ----------------------------------------------------------------------------
+
+int32_t AudioFlinger::getPrimaryOutputSamplingRate()
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = primaryPlaybackThread_l();
+ return thread != NULL ? thread->sampleRate() : 0;
+}
+
+int32_t AudioFlinger::getPrimaryOutputFrameCount()
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = primaryPlaybackThread_l();
+ return thread != NULL ? thread->frameCountHAL() : 0;
+}
+
+// ----------------------------------------------------------------------------
+
audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
@@ -6764,11 +6956,11 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
};
audio_stream_out_t *outStream = NULL;
- audio_hw_device_t *outHwDev;
+ AudioHwDevice *outHwDev;
ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
module,
- (pDevices != NULL) ? (int)*pDevices : 0,
+ (pDevices != NULL) ? *pDevices : 0,
config.sample_rate,
config.format,
config.channel_mask,
@@ -6784,11 +6976,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
if (outHwDev == NULL)
return 0;
+ audio_hw_device_t *hwDevHal = outHwDev->hwDevice();
audio_io_handle_t id = nextUniqueId();
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status = outHwDev->open_output_stream(outHwDev,
+ status = hwDevHal->open_output_stream(hwDevHal,
id,
*pDevices,
(audio_output_flags_t)flags,
@@ -6832,41 +7025,8 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MODE;
- outHwDev->set_mode(outHwDev, mMode);
-
- // Determine the level of master volume support the primary audio HAL has,
- // and set the initial master volume at the same time.
- float initialVolume = 1.0;
- mMasterVolumeSupportLvl = MVS_NONE;
-
- mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
- if ((NULL != outHwDev->get_master_volume) &&
- (NO_ERROR == outHwDev->get_master_volume(outHwDev, &initialVolume))) {
- mMasterVolumeSupportLvl = MVS_FULL;
- } else {
- mMasterVolumeSupportLvl = MVS_SETONLY;
- initialVolume = 1.0;
- }
-
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if ((NULL == outHwDev->set_master_volume) ||
- (NO_ERROR != outHwDev->set_master_volume(outHwDev, initialVolume))) {
- mMasterVolumeSupportLvl = MVS_NONE;
- }
- // now that we have a primary device, initialize master volume on other devices
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
-
- if ((dev != mPrimaryHardwareDev) &&
- (NULL != dev->set_master_volume)) {
- dev->set_master_volume(dev, initialVolume);
- }
- }
+ hwDevHal->set_mode(hwDevHal, mMode);
mHardwareStatus = AUDIO_HW_IDLE;
- mMasterVolumeSW = (MVS_NONE == mMasterVolumeSupportLvl)
- ? initialVolume
- : 1.0;
- mMasterVolume = initialVolume;
}
return id;
}
@@ -6897,6 +7057,11 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1,
status_t AudioFlinger::closeOutput(audio_io_handle_t output)
{
+ return closeOutput_nonvirtual(output);
+}
+
+status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
+{
// keep strong reference on the playback thread so that
// it is not destroyed while exit() is executed
sp<PlaybackThread> thread;
@@ -6928,7 +7093,7 @@ status_t AudioFlinger::closeOutput(audio_io_handle_t output)
AudioStreamOut *out = thread->clearOutput();
ALOG_ASSERT(out != NULL, "out shouldn't be NULL");
// from now on thread->mOutput is NULL
- out->hwDev->close_output_stream(out->hwDev, out->stream);
+ out->hwDev()->close_output_stream(out->hwDev(), out->stream);
delete out;
}
return NO_ERROR;
@@ -6969,7 +7134,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
audio_format_t *pFormat,
- uint32_t *pChannelMask)
+ audio_channel_mask_t *pChannelMask)
{
status_t status;
RecordThread *thread = NULL;
@@ -6982,7 +7147,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
audio_format_t reqFormat = config.format;
audio_channel_mask_t reqChannels = config.channel_mask;
audio_stream_in_t *inStream = NULL;
- audio_hw_device_t *inHwDev;
+ AudioHwDevice *inHwDev;
if (pDevices == NULL || *pDevices == 0) {
return 0;
@@ -6994,9 +7159,10 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
if (inHwDev == NULL)
return 0;
+ audio_hw_device_t *inHwHal = inHwDev->hwDevice();
audio_io_handle_t id = nextUniqueId();
- status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config,
+ status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,
&inStream);
ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d",
inStream,
@@ -7012,9 +7178,9 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
(config.sample_rate <= 2 * reqSamplingRate) &&
(popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) {
- ALOGV("openInput() reopening with proposed sampling rate and channels");
+ ALOGV("openInput() reopening with proposed sampling rate and channel mask");
inStream = NULL;
- status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config, &inStream);
+ status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);
}
if (status == NO_ERROR && inStream != NULL) {
@@ -7023,7 +7189,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
// Start record thread
// RecorThread require both input and output device indication to forward to audio
// pre processing modules
- uint32_t device = (*pDevices) | primaryOutputDevice_l();
+ audio_devices_t device = (*pDevices) | primaryOutputDevice_l();
thread = new RecordThread(this,
input,
reqSamplingRate,
@@ -7036,8 +7202,6 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
if (pFormat != NULL) *pFormat = config.format;
if (pChannelMask != NULL) *pChannelMask = reqChannels;
- input->stream->common.standby(&input->stream->common);
-
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
return id;
@@ -7048,13 +7212,18 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
status_t AudioFlinger::closeInput(audio_io_handle_t input)
{
+ return closeInput_nonvirtual(input);
+}
+
+status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input)
+{
// keep strong reference on the record thread so that
// it is not destroyed while exit() is executed
sp<RecordThread> thread;
{
Mutex::Autolock _l(mLock);
thread = checkRecordThread_l(input);
- if (thread == NULL) {
+ if (thread == 0) {
return BAD_VALUE;
}
@@ -7069,7 +7238,7 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input)
AudioStreamIn *in = thread->clearInput();
ALOG_ASSERT(in != NULL, "in shouldn't be NULL");
// from now on thread->mInput is NULL
- in->hwDev->close_input_stream(in->hwDev, in->stream);
+ in->hwDev()->close_input_stream(in->hwDev(), in->stream);
delete in;
return NO_ERROR;
@@ -7078,21 +7247,11 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input)
status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
{
Mutex::Autolock _l(mLock);
- MixerThread *dstThread = checkMixerThread_l(output);
- if (dstThread == NULL) {
- ALOGW("setStreamOutput() bad output id %d", output);
- return BAD_VALUE;
- }
-
ALOGV("setStreamOutput() stream %d to output %d", stream, output);
- audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream);
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
- if (thread != dstThread && thread->type() != ThreadBase::DIRECT) {
- MixerThread *srcThread = (MixerThread *)thread;
- srcThread->invalidateTracks(stream);
- }
+ thread->invalidateTracks(stream);
}
return NO_ERROR;
@@ -7186,20 +7345,14 @@ void AudioFlinger::purgeStaleEffects_l() {
}
}
if (!found) {
+ Mutex::Autolock _l (t->mLock);
// remove all effects from the chain
while (ec->mEffects.size()) {
sp<EffectModule> effect = ec->mEffects[0];
effect->unPin();
- Mutex::Autolock _l (t->mLock);
t->removeEffect_l(effect);
- for (size_t j = 0; j < effect->mHandles.size(); j++) {
- sp<EffectHandle> handle = effect->mHandles[j].promote();
- if (handle != 0) {
- handle->mEffect.clear();
- if (handle->mHasControl && handle->mEnabled) {
- t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
- }
- }
+ if (effect->purgeHandles()) {
+ t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
}
AudioSystem::unregisterEffect(effect->id());
}
@@ -7237,14 +7390,14 @@ AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
AudioStreamOut *output = thread->getOutput();
- if (output != NULL && output->hwDev == mPrimaryHardwareDev) {
+ if (output != NULL && output->audioHwDev == mPrimaryHardwareDev) {
return thread;
}
}
return NULL;
}
-uint32_t AudioFlinger::primaryOutputDevice_l() const
+audio_devices_t AudioFlinger::primaryOutputDevice_l() const
{
PlaybackThread *thread = primaryPlaybackThread_l();
@@ -7252,7 +7405,7 @@ uint32_t AudioFlinger::primaryOutputDevice_l() const
return 0;
}
- return thread->device();
+ return thread->outDevice();
}
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
@@ -7401,7 +7554,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
// 0 and the effect is not auxiliary, continue enumeration in case
// an auxiliary version of this effect type is available
found = true;
- memcpy(&d, &desc, sizeof(effect_descriptor_t));
+ d = desc;
if (sessionId != AUDIO_SESSION_OUTPUT_MIX ||
(desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
break;
@@ -7417,7 +7570,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
// connect to output mix (Compliance to OpenSL ES)
if (sessionId == AUDIO_SESSION_OUTPUT_MIX &&
(d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) {
- memcpy(&desc, &d, sizeof(effect_descriptor_t));
+ desc = d;
}
}
@@ -7436,7 +7589,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
}
// return effect descriptor
- memcpy(pDesc, &desc, sizeof(effect_descriptor_t));
+ *pDesc = desc;
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
@@ -7665,12 +7818,14 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
}
effectCreated = true;
- effect->setDevice(mDevice);
+ effect->setDevice(mOutDevice);
+ effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
+ effect->setAudioSource(mAudioSource);
}
// create effect handle and connect it to effect module
handle = new EffectHandle(effect, client, effectClient, priority);
- lStatus = effect->addHandle(handle);
+ lStatus = effect->addHandle(handle.get());
if (enabled != NULL) {
*enabled = (int)effect->isEnabled();
}
@@ -7742,8 +7897,10 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
return status;
}
- effect->setDevice(mDevice);
+ effect->setDevice(mOutDevice);
+ effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
+ effect->setAudioSource(mAudioSource);
return NO_ERROR;
}
@@ -7789,7 +7946,7 @@ sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain(int sessi
return getEffectChain_l(sessionId);
}
-sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId)
+sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId) const
{
size_t size = mEffectChains.size();
for (size_t i = 0; i < size; i++) {
@@ -7810,7 +7967,7 @@ void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
}
void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
- const wp<EffectHandle>& handle,
+ EffectHandle *handle,
bool unpinIfLast) {
Mutex::Autolock _l(mLock);
@@ -8002,16 +8159,18 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
effect_descriptor_t *desc,
int id,
int sessionId)
- : mThread(thread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL),
- mStatus(NO_INIT), mState(IDLE), mSuspended(false)
+ : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
+ mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
+ mDescriptor(*desc),
+ // mConfig is set by configure() and not used before then
+ mEffectInterface(NULL),
+ mStatus(NO_INIT), mState(IDLE),
+ // mMaxDisableWaitCnt is set by configure() and not used before then
+ // mDisableWaitCnt is set by process() and updateState() and not used before then
+ mSuspended(false)
{
ALOGV("Constructor %p", this);
int lStatus;
- if (thread == NULL) {
- return;
- }
-
- memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
// create effect engine from effect factory
mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);
@@ -8025,9 +8184,6 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
goto Error;
}
- if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
- mPinned = true;
- }
ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
return;
Error:
@@ -8055,38 +8211,41 @@ AudioFlinger::EffectModule::~EffectModule()
}
}
-status_t AudioFlinger::EffectModule::addHandle(const sp<EffectHandle>& handle)
+status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
{
status_t status;
Mutex::Autolock _l(mLock);
int priority = handle->priority();
size_t size = mHandles.size();
- sp<EffectHandle> h;
+ EffectHandle *controlHandle = NULL;
size_t i;
for (i = 0; i < size; i++) {
- h = mHandles[i].promote();
- if (h == 0) continue;
+ EffectHandle *h = mHandles[i];
+ if (h == NULL || h->destroyed_l()) continue;
+ // first non destroyed handle is considered in control
+ if (controlHandle == NULL)
+ controlHandle = h;
if (h->priority() <= priority) break;
}
// if inserted in first place, move effect control from previous owner to this handle
if (i == 0) {
bool enabled = false;
- if (h != 0) {
- enabled = h->enabled();
- h->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/);
+ if (controlHandle != NULL) {
+ enabled = controlHandle->enabled();
+ controlHandle->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/);
}
handle->setControl(true /*hasControl*/, false /*signal*/, enabled /*enabled*/);
status = NO_ERROR;
} else {
status = ALREADY_EXISTS;
}
- ALOGV("addHandle() %p added handle %p in position %d", this, handle.get(), i);
+ ALOGV("addHandle() %p added handle %p in position %d", this, handle, i);
mHandles.insertAt(handle, i);
return status;
}
-size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
+size_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
{
Mutex::Autolock _l(mLock);
size_t size = mHandles.size();
@@ -8097,43 +8256,44 @@ size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle)
if (i == size) {
return size;
}
- ALOGV("removeHandle() %p removed handle %p in position %d", this, handle.unsafe_get(), i);
+ ALOGV("removeHandle() %p removed handle %p in position %d", this, handle, i);
- bool enabled = false;
- EffectHandle *hdl = handle.unsafe_get();
- if (hdl != NULL) {
- ALOGV("removeHandle() unsafe_get OK");
- enabled = hdl->enabled();
- }
mHandles.removeAt(i);
- size = mHandles.size();
// if removed from first place, move effect control from this handle to next in line
- if (i == 0 && size != 0) {
- sp<EffectHandle> h = mHandles[0].promote();
- if (h != 0) {
- h->setControl(true /*hasControl*/, true /*signal*/ , enabled /*enabled*/);
+ if (i == 0) {
+ EffectHandle *h = controlHandle_l();
+ if (h != NULL) {
+ h->setControl(true /*hasControl*/, true /*signal*/ , handle->enabled() /*enabled*/);
}
}
// Prevent calls to process() and other functions on effect interface from now on.
// The effect engine will be released by the destructor when the last strong reference on
// this object is released which can happen after next process is called.
- if (size == 0 && !mPinned) {
+ if (mHandles.size() == 0 && !mPinned) {
mState = DESTROYED;
}
- return size;
+ return mHandles.size();
}
-sp<AudioFlinger::EffectHandle> AudioFlinger::EffectModule::controlHandle()
+// must be called with EffectModule::mLock held
+AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
{
- Mutex::Autolock _l(mLock);
- return mHandles.size() != 0 ? mHandles[0].promote() : 0;
+ // the first valid handle in the list has control over the module
+ for (size_t i = 0; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
+ return h;
+ }
+ }
+
+ return NULL;
}
-void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool unpinIfLast)
+size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)
{
- ALOGV("disconnect() %p handle %p", this, handle.unsafe_get());
+ ALOGV("disconnect() %p handle %p", this, handle);
// keep a strong reference on this EffectModule to avoid calling the
// destructor before we exit
sp<EffectModule> keep(this);
@@ -8143,6 +8303,7 @@ void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool
thread->disconnectEffect(keep, handle, unpinIfLast);
}
}
+ return mHandles.size();
}
void AudioFlinger::EffectModule::updateState() {
@@ -8240,7 +8401,6 @@ void AudioFlinger::EffectModule::reset_l()
status_t AudioFlinger::EffectModule::configure()
{
- uint32_t channels;
if (mEffectInterface == NULL) {
return NO_INIT;
}
@@ -8251,18 +8411,14 @@ status_t AudioFlinger::EffectModule::configure()
}
// TODO: handle configuration of effects replacing track process
- if (thread->channelCount() == 1) {
- channels = AUDIO_CHANNEL_OUT_MONO;
- } else {
- channels = AUDIO_CHANNEL_OUT_STEREO;
- }
+ audio_channel_mask_t channelMask = thread->channelMask();
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
} else {
- mConfig.inputCfg.channels = channels;
+ mConfig.inputCfg.channels = channelMask;
}
- mConfig.outputCfg.channels = channels;
+ mConfig.outputCfg.channels = channelMask;
mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
mConfig.inputCfg.samplingRate = thread->sampleRate();
@@ -8452,8 +8608,8 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
uint32_t size = (replySize == NULL) ? 0 : *replySize;
for (size_t i = 1; i < mHandles.size(); i++) {
- sp<EffectHandle> h = mHandles[i].promote();
- if (h != 0) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
}
}
@@ -8463,8 +8619,14 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
{
-
Mutex::Autolock _l(mLock);
+ return setEnabled_l(enabled);
+}
+
+// must be called with EffectModule::mLock held
+status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
+{
+
ALOGV("setEnabled %p enabled %d", this, enabled);
if (enabled != isEnabled()) {
@@ -8499,8 +8661,8 @@ status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
return NO_ERROR; // simply ignore as we are being destroyed
}
for (size_t i = 1; i < mHandles.size(); i++) {
- sp<EffectHandle> h = mHandles[i].promote();
- if (h != 0) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
h->setEnabled(enabled);
}
}
@@ -8573,46 +8735,25 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right,
return status;
}
-status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
+status_t AudioFlinger::EffectModule::setDevice(audio_devices_t device)
{
+ if (device == AUDIO_DEVICE_NONE) {
+ return NO_ERROR;
+ }
+
Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
- // audio pre processing modules on RecordThread can receive both output and
- // input device indication in the same call
- uint32_t dev = device & AUDIO_DEVICE_OUT_ALL;
- if (dev) {
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
-
- status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_DEVICE,
- sizeof(uint32_t),
- &dev,
- &size,
- &cmdStatus);
- if (status == NO_ERROR) {
- status = cmdStatus;
- }
- }
- dev = device & AUDIO_DEVICE_IN_ALL;
- if (dev) {
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
-
- status_t status2 = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_INPUT_DEVICE,
- sizeof(uint32_t),
- &dev,
- &size,
- &cmdStatus);
- if (status2 == NO_ERROR) {
- status2 = cmdStatus;
- }
- if (status == NO_ERROR) {
- status = status2;
- }
- }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE :
+ EFFECT_CMD_SET_INPUT_DEVICE;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ cmd,
+ sizeof(uint32_t),
+ &device,
+ &size,
+ &cmdStatus);
}
return status;
}
@@ -8637,6 +8778,22 @@ status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
return status;
}
+status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source)
+{
+ Mutex::Autolock _l(mLock);
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) {
+ uint32_t size = 0;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_AUDIO_SOURCE,
+ sizeof(audio_source_t),
+ &source,
+ &size,
+ NULL);
+ }
+ return status;
+}
+
void AudioFlinger::EffectModule::setSuspended(bool suspended)
{
Mutex::Autolock _l(mLock);
@@ -8649,7 +8806,23 @@ bool AudioFlinger::EffectModule::suspended() const
return mSuspended;
}
-status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+bool AudioFlinger::EffectModule::purgeHandles()
+{
+ bool enabled = false;
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mHandles.size(); i++) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->destroyed_l()) {
+ handle->effect().clear();
+ if (handle->hasControl()) {
+ enabled = handle->enabled();
+ }
+ }
+ }
+ return enabled;
+}
+
+void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -8715,8 +8888,8 @@ status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
result.append(buffer);
result.append("\t\t\tPid Priority Ctrl Locked client server\n");
for (size_t i = 0; i < mHandles.size(); ++i) {
- sp<EffectHandle> handle = mHandles[i].promote();
- if (handle != 0) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->destroyed_l()) {
handle->dump(buffer, SIZE);
result.append(buffer);
}
@@ -8729,8 +8902,6 @@ status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
if (locked) {
mLock.unlock();
}
-
- return NO_ERROR;
}
// ----------------------------------------------------------------------------
@@ -8746,7 +8917,7 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
int32_t priority)
: BnEffect(),
mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
- mPriority(priority), mHasControl(false), mEnabled(false)
+ mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
{
ALOGV("constructor %p", this);
@@ -8771,8 +8942,15 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect,
AudioFlinger::EffectHandle::~EffectHandle()
{
ALOGV("Destructor %p", this);
+
+ if (mEffect == 0) {
+ mDestroyed = true;
+ return;
+ }
+ mEffect->lock();
+ mDestroyed = true;
+ mEffect->unlock();
disconnect(false);
- ALOGV("Destructor DONE %p", this);
}
status_t AudioFlinger::EffectHandle::enable()
@@ -8843,9 +9021,8 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
if (mEffect == 0) {
return;
}
- mEffect->disconnect(this, unpinIfLast);
-
- if (mHasControl && mEnabled) {
+ // restore suspended effects if the disconnected handle was enabled and the last one.
+ if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) {
sp<ThreadBase> thread = mEffect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
@@ -9275,7 +9452,7 @@ size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
}
// setDevice_l() must be called with PlaybackThread::mLock held
-void AudioFlinger::EffectChain::setDevice_l(uint32_t device)
+void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device)
{
size_t size = mEffects.size();
for (size_t i = 0; i < size; i++) {
@@ -9292,6 +9469,15 @@ void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
}
}
+// setAudioSource_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setAudioSource(source);
+ }
+}
+
// setVolume_l() must be called with PlaybackThread::mLock held
bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
{
@@ -9350,7 +9536,7 @@ bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
return hasControl;
}
-status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -9384,8 +9570,6 @@ status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
if (locked) {
mLock.unlock();
}
-
- return NO_ERROR;
}
// must be called with ThreadBase::mLock held
@@ -9401,7 +9585,7 @@ void AudioFlinger::EffectChain::setEffectSuspended_l(
desc = mSuspendedEffects.valueAt(index);
} else {
desc = new SuspendedEffectDesc();
- memcpy(&desc->mType, type, sizeof(effect_uuid_t));
+ desc->mType = *type;
mSuspendedEffects.add(type->timeLow, desc);
ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
}
@@ -9428,10 +9612,12 @@ void AudioFlinger::EffectChain::setEffectSuspended_l(
sp<EffectModule> effect = desc->mEffect.promote();
if (effect != 0) {
effect->setSuspended(false);
- sp<EffectHandle> handle = effect->controlHandle();
- if (handle != 0) {
- effect->setEnabled(handle->enabled());
+ effect->lock();
+ EffectHandle *handle = effect->controlHandle_l();
+ if (handle != NULL && !handle->destroyed_l()) {
+ effect->setEnabled_l(handle->enabled());
}
+ effect->unlock();
}
desc->mEffect.clear();
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index cfd718f4..49e2b2cb 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -45,10 +45,10 @@
#include <hardware/audio.h>
#include <hardware/audio_policy.h>
-#include "AudioBufferProvider.h"
-#include "ExtendedAudioBufferProvider.h"
+#include <media/AudioBufferProvider.h>
+#include <media/ExtendedAudioBufferProvider.h>
#include "FastMixer.h"
-#include "NBAIO.h"
+#include <media/nbaio/NBAIO.h>
#include "AudioWatchdog.h"
#include <powermanager/IPowerManager.h>
@@ -91,7 +91,7 @@ public:
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
IAudioFlinger::track_flags_t flags,
const sp<IMemory>& sharedBuffer,
@@ -105,9 +105,10 @@ public:
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
IAudioFlinger::track_flags_t flags,
+ pid_t tid,
int *sessionId,
status_t *status);
@@ -121,7 +122,6 @@ public:
virtual status_t setMasterMute(bool muted);
virtual float masterVolume() const;
- virtual float masterVolumeSW() const;
virtual bool masterMute() const;
virtual status_t setStreamVolume(audio_stream_type_t stream, float value,
@@ -142,7 +142,8 @@ public:
virtual void registerClient(const sp<IAudioFlingerClient>& client);
- virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const;
+ virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask) const;
virtual audio_io_handle_t openOutput(audio_module_handle_t module,
audio_devices_t *pDevices,
@@ -206,6 +207,9 @@ public:
virtual audio_module_handle_t loadHwModule(const char *name);
+ virtual int32_t getPrimaryOutputSamplingRate();
+ virtual int32_t getPrimaryOutputFrameCount();
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -232,8 +236,8 @@ public:
virtual ~SyncEvent() {}
void trigger() { Mutex::Autolock _l(mLock); if (mCallback) mCallback(this); }
- bool isCancelled() { Mutex::Autolock _l(mLock); return (mCallback == NULL); }
- void cancel() {Mutex::Autolock _l(mLock); mCallback = NULL; }
+ bool isCancelled() const { Mutex::Autolock _l(mLock); return (mCallback == NULL); }
+ void cancel() { Mutex::Autolock _l(mLock); mCallback = NULL; }
AudioSystem::sync_event_t type() const { return mType; }
int triggerSession() const { return mTriggerSession; }
int listenerSession() const { return mListenerSession; }
@@ -245,7 +249,7 @@ public:
const int mListenerSession;
sync_event_callback_t mCallback;
void * const mCookie;
- Mutex mLock;
+ mutable Mutex mLock;
};
sp<SyncEvent> createSyncEvent(AudioSystem::sync_event_t type,
@@ -255,6 +259,8 @@ public:
void *cookie);
private:
+ class AudioHwDevice; // fwd declaration for findSuitableHwDev_l
+
audio_mode_t getMode() const { return mMode; }
bool btNrecIsOff() const { return mBtNrecIsOff; }
@@ -268,17 +274,17 @@ private:
// RefBase
virtual void onFirstRef();
- audio_hw_device_t* findSuitableHwDev_l(audio_module_handle_t module, uint32_t devices);
+ AudioHwDevice* findSuitableHwDev_l(audio_module_handle_t module, audio_devices_t devices);
void purgeStaleEffects_l();
// standby delay for MIXER and DUPLICATING playback threads is read from property
// ro.audio.flinger_standbytime_ms or defaults to kDefaultStandbyTimeInNsecs
static nsecs_t mStandbyTimeInNsecs;
- // Internal dump utilites.
- status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
- status_t dumpClients(int fd, const Vector<String16>& args);
- status_t dumpInternals(int fd, const Vector<String16>& args);
+ // Internal dump utilities.
+ void dumpPermissionDenial(int fd, const Vector<String16>& args);
+ void dumpClients(int fd, const Vector<String16>& args);
+ void dumpInternals(int fd, const Vector<String16>& args);
// --- Client ---
class Client : public RefBase {
@@ -350,11 +356,12 @@ private:
RECORD // Thread class is RecordThread
};
- ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, uint32_t device, type_t type);
+ ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type);
virtual ~ThreadBase();
- status_t dumpBase(int fd, const Vector<String16>& args);
- status_t dumpEffectChains(int fd, const Vector<String16>& args);
+ void dumpBase(int fd, const Vector<String16>& args);
+ void dumpEffectChains(int fd, const Vector<String16>& args);
void clearPowerManager();
@@ -380,14 +387,14 @@ private:
const sp<Client>& client,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId);
virtual ~TrackBase();
- virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
- int triggerSession = 0) = 0;
+ virtual status_t start(AudioSystem::sync_event_t event,
+ int triggerSession) = 0;
virtual void stop() = 0;
sp<IMemory> getCblk() const { return mCblkMemory; }
audio_track_cblk_t* cblk() const { return mCblk; }
@@ -412,10 +419,17 @@ private:
int channelCount() const { return mChannelCount; }
- uint32_t channelMask() const { return mChannelMask; }
+ audio_channel_mask_t channelMask() const { return mChannelMask; }
int sampleRate() const; // FIXME inline after cblk sr moved
+ // Return a pointer to the start of a contiguous slice of the track buffer.
+ // Parameter 'offset' is the requested start position, expressed in
+ // monotonically increasing frame units relative to the track epoch.
+ // Parameter 'frames' is the requested length, also in frame units.
+ // Always returns non-NULL. It is the caller's responsibility to
+ // verify that this will be successful; the result of calling this
+ // function with invalid 'offset' or 'frames' is undefined.
void* getBuffer(uint32_t offset, uint32_t frames) const;
bool isStopped() const {
@@ -444,8 +458,9 @@ private:
/*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
- void* mBuffer;
- void* mBufferEnd;
+ void* mBuffer; // start of track buffer, typically in shared memory
+ void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
+ // is based on mChannelCount and 16-bit samples
uint32_t mFrameCount;
// we don't really need a lock for these
track_state mState;
@@ -455,18 +470,67 @@ private:
bool mStepServerFailed;
const int mSessionId;
uint8_t mChannelCount;
- uint32_t mChannelMask;
+ audio_channel_mask_t mChannelMask;
Vector < sp<SyncEvent> >mSyncEvents;
};
+ enum {
+ CFG_EVENT_IO,
+ CFG_EVENT_PRIO
+ };
+
class ConfigEvent {
public:
- ConfigEvent() : mEvent(0), mParam(0) {}
+ ConfigEvent(int type) : mType(type) {}
+ virtual ~ConfigEvent() {}
+
+ int type() const { return mType; }
+
+ virtual void dump(char *buffer, size_t size) = 0;
+
+ private:
+ const int mType;
+ };
+
+ class IoConfigEvent : public ConfigEvent {
+ public:
+ IoConfigEvent(int event, int param) :
+ ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(event) {}
+ virtual ~IoConfigEvent() {}
+
+ int event() const { return mEvent; }
+ int param() const { return mParam; }
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
+ }
+
+ private:
+ const int mEvent;
+ const int mParam;
+ };
+
+ class PrioConfigEvent : public ConfigEvent {
+ public:
+ PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
+ ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {}
+ virtual ~PrioConfigEvent() {}
+
+ pid_t pid() const { return mPid; }
+ pid_t tid() const { return mTid; }
+ int32_t prio() const { return mPrio; }
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio);
+ }
- int mEvent;
- int mParam;
+ private:
+ const pid_t mPid;
+ const pid_t mTid;
+ const int32_t mPrio;
};
+
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {}
@@ -483,14 +547,22 @@ private:
};
virtual status_t initCheck() const = 0;
+
+ // static externally-visible
type_t type() const { return mType; }
+ audio_io_handle_t id() const { return mId;}
+
+ // dynamic externally-visible
uint32_t sampleRate() const { return mSampleRate; }
int channelCount() const { return mChannelCount; }
+ audio_channel_mask_t channelMask() const { return mChannelMask; }
audio_format_t format() const { return mFormat; }
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
- // and returns the normal mix buffer's frame count. No API for HAL frame count.
+ // and returns the normal mix buffer's frame count.
size_t frameCount() const { return mNormalFrameCount; }
- void wakeUp() { mWaitWorkCV.broadcast(); }
+ // Return's the HAL's frame count i.e. fast mixer buffer size.
+ size_t frameCountHAL() const { return mFrameCount; }
+
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
void exit();
@@ -498,12 +570,16 @@ private:
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
virtual void audioConfigChanged_l(int event, int param = 0) = 0;
- void sendConfigEvent(int event, int param = 0);
- void sendConfigEvent_l(int event, int param = 0);
+ void sendIoConfigEvent(int event, int param = 0);
+ void sendIoConfigEvent_l(int event, int param = 0);
+ void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
void processConfigEvents();
- audio_io_handle_t id() const { return mId;}
+
+ // see note at declaration of mStandby, mOutDevice and mInDevice
bool standby() const { return mStandby; }
- uint32_t device() const { return mDevice; }
+ audio_devices_t outDevice() const { return mOutDevice; }
+ audio_devices_t inDevice() const { return mInDevice; }
+
virtual audio_stream_t* stream() const = 0;
sp<EffectHandle> createEffect_l(
@@ -515,7 +591,7 @@ private:
int *enabled,
status_t *status);
void disconnectEffect(const sp< EffectModule>& effect,
- const wp<EffectHandle>& handle,
+ EffectHandle *handle,
bool unpinIfLast);
// return values for hasAudioSession (bit field)
@@ -529,7 +605,7 @@ private:
// get effect chain corresponding to session Id.
sp<EffectChain> getEffectChain(int sessionId);
// same as getEffectChain() but must be called with ThreadBase mutex locked
- sp<EffectChain> getEffectChain_l(int sessionId);
+ sp<EffectChain> getEffectChain_l(int sessionId) const;
// add an effect chain to the chain list (mEffectChains)
virtual status_t addEffectChain_l(const sp<EffectChain>& chain) = 0;
// remove an effect chain from the chain list (mEffectChains)
@@ -556,7 +632,7 @@ private:
virtual void detachAuxEffect_l(int effectId) {}
// returns either EFFECT_SESSION if effects on this audio session exist in one
// chain, or TRACK_SESSION if tracks on this audio session exist, or both
- virtual uint32_t hasAudioSession(int sessionId) = 0;
+ virtual uint32_t hasAudioSession(int sessionId) const = 0;
// the value returned by default implementation is not important as the
// strategy is only meaningful for PlaybackThread which implements this method
virtual uint32_t getStrategyForSession_l(int sessionId) { return 0; }
@@ -576,7 +652,7 @@ private:
int sessionId = AUDIO_SESSION_OUTPUT_MIX);
virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event) = 0;
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
mutable Mutex mLock;
@@ -598,7 +674,7 @@ private:
void releaseWakeLock_l();
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
- int sessionId = AUDIO_SESSION_OUTPUT_MIX);
+ int sessionId);
// updated mSuspendedSessions when an effect suspended or restored
void updateSuspendedSessions_l(const effect_uuid_t *type,
bool suspend,
@@ -606,6 +682,8 @@ private:
// check if some effects must be suspended when an effect chain is added
void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
+ virtual void preExit() { }
+
friend class AudioFlinger; // for mEffectChains
const type_t mType;
@@ -617,7 +695,7 @@ private:
uint32_t mSampleRate;
size_t mFrameCount; // output HAL, direct output, record
size_t mNormalFrameCount; // normal mixer and effects
- uint32_t mChannelMask;
+ audio_channel_mask_t mChannelMask;
uint16_t mChannelCount;
size_t mFrameSize;
audio_format_t mFormat;
@@ -645,12 +723,22 @@ private:
Vector<String8> mNewParameters;
status_t mParamStatus;
- Vector<ConfigEvent> mConfigEvents;
- bool mStandby;
+ Vector<ConfigEvent *> mConfigEvents;
+
+ // These fields are written and read by thread itself without lock or barrier,
+ // and read by other threads without lock or barrier via standby() , outDevice()
+ // and inDevice().
+ // Because of the absence of a lock or barrier, any other thread that reads
+ // these fields must use the information in isolation, or be prepared to deal
+ // with possibility that it might be inconsistent with other information.
+ bool mStandby; // Whether thread is currently in standby.
+ audio_devices_t mOutDevice; // output device
+ audio_devices_t mInDevice; // input device
+ audio_source_t mAudioSource; // (see audio.h, audio_source_t)
+
const audio_io_handle_t mId;
Vector< sp<EffectChain> > mEffectChains;
- uint32_t mDevice; // output device for PlaybackThread
- // input + output devices for RecordThread
+
static const int kNameLength = 16; // prctl(PR_SET_NAME) limit
char mName[kNameLength];
sp<IPowerManager> mPowerManager;
@@ -691,7 +779,7 @@ private:
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
@@ -708,9 +796,7 @@ private:
void flush();
void destroy();
void mute(bool);
- int name() const {
- return mName;
- }
+ int name() const { return mName; }
audio_stream_type_t streamType() const {
return mStreamType;
@@ -767,10 +853,14 @@ private:
void triggerEvents(AudioSystem::sync_event_t type);
virtual bool isTimedTrack() const { return false; }
bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+
protected:
- // we don't really need a lock for these
- volatile bool mMute;
+ // written by Track::mute() called by binder thread(s), without a mutex or barrier.
+ // read by Track::isMuted() called by playback thread, also without a mutex or barrier.
+ // The lack of mutex or barrier is safe because the mute status is only used by itself.
+ bool mMute;
+
// FILLED state is used for suppressing volume ramp at begin of playing
enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
mutable uint8_t mFillingUpStatus;
@@ -813,11 +903,11 @@ private:
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId);
- ~TimedTrack();
+ virtual ~TimedTrack();
class TimedBuffer {
public:
@@ -856,7 +946,7 @@ private:
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId);
@@ -905,7 +995,7 @@ private:
DuplicatingThread *sourceThread,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount);
virtual ~OutputTrack();
@@ -936,10 +1026,10 @@ private:
}; // end of OutputTrack
PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device, type_t type);
+ audio_io_handle_t id, audio_devices_t device, type_t type);
virtual ~PlaybackThread();
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
// Thread virtuals
virtual status_t readyToRun();
@@ -962,6 +1052,9 @@ protected:
// is safe to do so. That will drop the final ref count and destroy the tracks.
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
+ // ThreadBase virtuals
+ virtual void preExit();
+
public:
virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; }
@@ -984,7 +1077,7 @@ public:
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
@@ -996,9 +1089,19 @@ public:
AudioStreamOut* clearOutput();
virtual audio_stream_t* stream() const;
- void suspend() { mSuspended++; }
- void restore() { if (mSuspended > 0) mSuspended--; }
- bool isSuspended() const { return (mSuspended > 0); }
+ // a very large number of suspend() will eventually wraparound, but unlikely
+ void suspend() { (void) android_atomic_inc(&mSuspended); }
+ void restore()
+ {
+ // if restore() is done without suspend(), get back into
+ // range so that the next suspend() will operate correctly
+ if (android_atomic_dec(&mSuspended) <= 0) {
+ android_atomic_release_store(0, &mSuspended);
+ }
+ }
+ bool isSuspended() const
+ { return android_atomic_acquire_load(&mSuspended) > 0; }
+
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged_l(int event, int param = 0);
status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
@@ -1012,16 +1115,25 @@ public:
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(int sessionId);
+ virtual uint32_t hasAudioSession(int sessionId) const;
virtual uint32_t getStrategyForSession_l(int sessionId);
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event);
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
+ void invalidateTracks(audio_stream_type_t streamType);
+
protected:
int16_t* mMixBuffer;
- uint32_t mSuspended; // suspend count, > 0 means suspended
+
+ // suspend count, > 0 means suspended. While suspended, the thread continues to pull from
+ // tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
+ // concurrent use of both of them, so Audio Policy Service suspends one of the threads to
+ // workaround that restriction.
+ // 'volatile' means accessed via atomic operations and no lock.
+ volatile int32_t mSuspended;
+
int mBytesWritten;
private:
// mMasterMute is in both PlaybackThread and in AudioFlinger. When a
@@ -1034,7 +1146,7 @@ public:
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
- virtual int getTrackName_l(audio_channel_mask_t channelMask) = 0;
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
virtual void deleteTrackName_l(int name) = 0;
// Time to sleep between cycles when:
@@ -1069,13 +1181,14 @@ public:
void readOutputParameters();
- virtual status_t dumpInternals(int fd, const Vector<String16>& args);
- status_t dumpTracks(int fd, const Vector<String16>& args);
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by DuplicatingThread
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
AudioStreamOut *mOutput;
+
float mMasterVolume;
nsecs_t mLastWriteTime;
int mNumWrites;
@@ -1100,7 +1213,6 @@ public:
// FIXME move these declarations into the specific sub-class that needs them
// MIXER only
- bool longStandbyExit;
uint32_t sleepTimeShift;
// same as AudioFlinger::mStandbyTimeInNsecs except for DIRECT which uses a shorter value
@@ -1139,19 +1251,18 @@ public:
MixerThread (const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
audio_io_handle_t id,
- uint32_t device,
+ audio_devices_t device,
type_t type = MIXER);
virtual ~MixerThread();
// Thread virtuals
- void invalidateTracks(audio_stream_type_t streamType);
virtual bool checkForNewParameters_l();
- virtual status_t dumpInternals(int fd, const Vector<String16>& args);
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
@@ -1167,9 +1278,6 @@ public:
AudioMixer* mAudioMixer; // normal mixer
private:
-#ifdef SOAKER
- Thread* mSoaker;
-#endif
// one-time initialization, no locks required
FastMixer* mFastMixer; // non-NULL if there is also a fast mixer
sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread
@@ -1198,7 +1306,7 @@ public:
public:
DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, uint32_t device);
+ audio_io_handle_t id, audio_devices_t device);
virtual ~DirectOutputThread();
// Thread virtuals
@@ -1206,7 +1314,7 @@ public:
virtual bool checkForNewParameters_l();
protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
@@ -1287,7 +1395,7 @@ private:
bool reRegister);
// return thread associated with primary hardware device, or NULL
PlaybackThread *primaryPlaybackThread_l() const;
- uint32_t primaryOutputDevice_l() const;
+ audio_devices_t primaryOutputDevice_l() const;
sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId);
@@ -1321,6 +1429,7 @@ private:
// record thread
class RecordThread : public ThreadBase, public AudioBufferProvider
+ // derives from AudioBufferProvider interface for use by resampler
{
public:
@@ -1331,18 +1440,22 @@ private:
const sp<Client>& client,
uint32_t sampleRate,
audio_format_t format,
- uint32_t channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
int sessionId);
virtual ~RecordTrack();
- virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
- int triggerSession = 0);
+ virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
virtual void stop();
- bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
+ void destroy();
+
+ // clear the buffer overflow flag
+ void clearOverflow() { mOverflow = false; }
+ // set the buffer overflow flag and return previous value
bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
+ static void appendDumpHeader(String8& result);
void dump(char* buffer, size_t size);
private:
@@ -1355,19 +1468,25 @@ private:
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts = kInvalidPTS);
// releaseBuffer() not overridden
- bool mOverflow;
+ bool mOverflow; // overflow on most recent attempt to fill client buffer
};
-
RecordThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamIn *input,
uint32_t sampleRate,
- uint32_t channels,
+ audio_channel_mask_t channelMask,
audio_io_handle_t id,
- uint32_t device);
+ audio_devices_t device);
virtual ~RecordThread();
- // Thread
+ // no addTrack_l ?
+ void destroyTrack_l(const sp<RecordTrack>& track);
+ void removeTrack_l(const sp<RecordTrack>& track);
+
+ void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
+
+ // Thread virtuals
virtual bool threadLoop();
virtual status_t readyToRun();
@@ -1379,17 +1498,22 @@ private:
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
audio_format_t format,
- int channelMask,
+ audio_channel_mask_t channelMask,
int frameCount,
int sessionId,
+ IAudioFlinger::track_flags_t flags,
+ pid_t tid,
status_t *status);
status_t start(RecordTrack* recordTrack,
AudioSystem::sync_event_t event,
int triggerSession);
- void stop(RecordTrack* recordTrack);
- status_t dump(int fd, const Vector<String16>& args);
- AudioStreamIn* getInput() const;
+
+ // ask the thread to stop the specified track, and
+ // return true if the caller should then do it's part of the stopping process
+ bool stop_l(RecordTrack* recordTrack);
+
+ void dump(int fd, const Vector<String16>& args);
AudioStreamIn* clearInput();
virtual audio_stream_t* stream() const;
@@ -1405,11 +1529,15 @@ private:
virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(int sessionId);
- RecordTrack* track();
+ virtual uint32_t hasAudioSession(int sessionId) const;
+
+ // Return the set of unique session IDs across all tracks.
+ // The keys are the session IDs, and the associated values are meaningless.
+ // FIXME replace by Set [and implement Bag/Multiset for other uses].
+ KeyedVector<int, bool> sessionIds() const;
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event);
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
static void syncStartEventCallback(const wp<SyncEvent>& event);
void handleSyncStartEvent(const sp<SyncEvent>& event);
@@ -1417,9 +1545,16 @@ private:
private:
void clearSyncStartEvent();
- RecordThread();
+ // Enter standby if not already in standby, and set mStandby flag
+ void standby();
+
+ // Call the HAL standby method unconditionally, and don't change mStandby flag
+ void inputStandBy();
+
AudioStreamIn *mInput;
- RecordTrack* mTrack;
+ SortedVector < sp<RecordTrack> > mTracks;
+ // mActiveTrack has dual roles: it indicates the current active track, and
+ // is used together with mStartStopCond to indicate start()/stop() progress
sp<RecordTrack> mActiveTrack;
Condition mStartStopCond;
AudioResampler *mResampler;
@@ -1445,12 +1580,15 @@ private:
RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
virtual ~RecordHandle();
virtual sp<IMemory> getCblk() const;
- virtual status_t start(int event, int triggerSession);
+ virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession);
virtual void stop();
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
const sp<RecordThread::RecordTrack> mRecordTrack;
+
+ // for use from destructor
+ void stop_nonvirtual();
};
//--- Audio Effect Management
@@ -1510,6 +1648,7 @@ private:
return mSessionId;
}
status_t setEnabled(bool enabled);
+ status_t setEnabled_l(bool enabled);
bool isEnabled() const;
bool isProcessEnabled() const;
@@ -1521,27 +1660,31 @@ private:
void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
const wp<ThreadBase>& thread() { return mThread; }
- status_t addHandle(const sp<EffectHandle>& handle);
- void disconnect(const wp<EffectHandle>& handle, bool unpinIfLast);
- size_t removeHandle (const wp<EffectHandle>& handle);
+ status_t addHandle(EffectHandle *handle);
+ size_t disconnect(EffectHandle *handle, bool unpinIfLast);
+ size_t removeHandle(EffectHandle *handle);
- effect_descriptor_t& desc() { return mDescriptor; }
+ const effect_descriptor_t& desc() const { return mDescriptor; }
wp<EffectChain>& chain() { return mChain; }
- status_t setDevice(uint32_t device);
+ status_t setDevice(audio_devices_t device);
status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
status_t setMode(audio_mode_t mode);
+ status_t setAudioSource(audio_source_t source);
status_t start();
status_t stop();
void setSuspended(bool suspended);
bool suspended() const;
- sp<EffectHandle> controlHandle();
+ EffectHandle* controlHandle_l();
bool isPinned() const { return mPinned; }
void unPin() { mPinned = false; }
+ bool purgeHandles();
+ void lock() { mLock.lock(); }
+ void unlock() { mLock.unlock(); }
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
protected:
friend class AudioFlinger; // for mHandles
@@ -1559,14 +1702,14 @@ private:
mutable Mutex mLock; // mutex for process, commands and handles list protection
wp<ThreadBase> mThread; // parent thread
wp<EffectChain> mChain; // parent effect chain
- int mId; // this instance unique ID
- int mSessionId; // audio session ID
- effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ const int mId; // this instance unique ID
+ const int mSessionId; // audio session ID
+ const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
effect_config_t mConfig; // input and output audio configuration
effect_handle_t mEffectInterface; // Effect module C API
status_t mStatus; // initialization status
effect_state mState; // current activation state
- Vector< wp<EffectHandle> > mHandles; // list of client handles
+ Vector<EffectHandle *> mHandles; // list of client handles
// First handle in mHandles has highest priority and controls the effect module
uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
// sending disable command.
@@ -1624,6 +1767,8 @@ mutable Mutex mLock; // mutex for process, commands and handl
int priority() const { return mPriority; }
bool hasControl() const { return mHasControl; }
sp<EffectModule> effect() const { return mEffect; }
+ // destroyed_l() must be called with the associated EffectModule mLock held
+ bool destroyed_l() const { return mDestroyed; }
void dump(char* buffer, size_t size);
@@ -1642,6 +1787,8 @@ mutable Mutex mLock; // mutex for process, commands and handl
bool mHasControl; // true if this handle is controlling the effect
bool mEnabled; // cached enable state: needed when the effect is
// restored after being suspended
+ bool mDestroyed; // Set to true by destructor. Access with EffectModule
+ // mLock held
};
// the EffectChain class represents a group of effects associated to one audio session.
@@ -1684,8 +1831,9 @@ mutable Mutex mLock; // mutex for process, commands and handl
sp<EffectModule> getEffectFromId_l(int id);
sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
bool setVolume_l(uint32_t *left, uint32_t *right);
- void setDevice_l(uint32_t device);
+ void setDevice_l(audio_devices_t device);
void setMode_l(audio_mode_t mode);
+ void setAudioSource_l(audio_source_t source);
void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
mInBuffer = buffer;
@@ -1703,12 +1851,12 @@ mutable Mutex mLock; // mutex for process, commands and handl
void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
void decTrackCnt() { android_atomic_dec(&mTrackCnt); }
- int32_t trackCnt() const { return mTrackCnt;}
+ int32_t trackCnt() const { return android_atomic_acquire_load(&mTrackCnt); }
void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt);
mTailBufferCount = mMaxTailBuffers; }
void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
- int32_t activeTrackCnt() const { return mActiveTrackCnt;}
+ int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); }
uint32_t strategy() const { return mStrategy; }
void setStrategy(uint32_t strategy)
@@ -1725,7 +1873,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
void clearInputBuffer();
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
protected:
friend class AudioFlinger; // for mThread, mEffects
@@ -1760,8 +1908,11 @@ mutable Mutex mLock; // mutex for process, commands and handl
int mSessionId; // audio session ID
int16_t *mInBuffer; // chain input buffer
int16_t *mOutBuffer; // chain output buffer
- volatile int32_t mActiveTrackCnt; // number of active tracks connected
- volatile int32_t mTrackCnt; // number of tracks connected
+
+ // 'volatile' here means these are accessed with atomic operations instead of mutex
+ volatile int32_t mActiveTrackCnt; // number of active tracks connected
+ volatile int32_t mTrackCnt; // number of tracks connected
+
int32_t mTailBufferCount; // current effect tail buffer count
int32_t mMaxTailBuffers; // maximum effect tail buffers
bool mOwnInBuffer; // true if the chain owns its input buffer
@@ -1778,24 +1929,59 @@ mutable Mutex mLock; // mutex for process, commands and handl
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
};
+ class AudioHwDevice {
+ public:
+ enum Flags {
+ AHWD_CAN_SET_MASTER_VOLUME = 0x1,
+ AHWD_CAN_SET_MASTER_MUTE = 0x2,
+ };
+
+ AudioHwDevice(const char *moduleName,
+ audio_hw_device_t *hwDevice,
+ Flags flags)
+ : mModuleName(strdup(moduleName))
+ , mHwDevice(hwDevice)
+ , mFlags(flags) { }
+ /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); }
+
+ bool canSetMasterVolume() const {
+ return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME));
+ }
+
+ bool canSetMasterMute() const {
+ return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE));
+ }
+
+ const char *moduleName() const { return mModuleName; }
+ audio_hw_device_t *hwDevice() const { return mHwDevice; }
+ private:
+ const char * const mModuleName;
+ audio_hw_device_t * const mHwDevice;
+ Flags mFlags;
+ };
+
// AudioStreamOut and AudioStreamIn are immutable, so their fields are const.
// For emphasis, we could also make all pointers to them be "const *",
// but that would clutter the code unnecessarily.
struct AudioStreamOut {
- audio_hw_device_t* const hwDev;
+ AudioHwDevice* const audioHwDev;
audio_stream_out_t* const stream;
- AudioStreamOut(audio_hw_device_t *dev, audio_stream_out_t *out) :
- hwDev(dev), stream(out) {}
+ audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
+
+ AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
+ audioHwDev(dev), stream(out) {}
};
struct AudioStreamIn {
- audio_hw_device_t* const hwDev;
+ AudioHwDevice* const audioHwDev;
audio_stream_in_t* const stream;
- AudioStreamIn(audio_hw_device_t *dev, audio_stream_in_t *in) :
- hwDev(dev), stream(in) {}
+ audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
+
+ AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in) :
+ audioHwDev(dev), stream(in) {}
};
// for mAudioSessionRefs only
@@ -1807,41 +1993,6 @@ mutable Mutex mLock; // mutex for process, commands and handl
int mCnt;
};
- enum master_volume_support {
- // MVS_NONE:
- // Audio HAL has no support for master volume, either setting or
- // getting. All master volume control must be implemented in SW by the
- // AudioFlinger mixing core.
- MVS_NONE,
-
- // MVS_SETONLY:
- // Audio HAL has support for setting master volume, but not for getting
- // master volume (original HAL design did not include a getter).
- // AudioFlinger needs to keep track of the last set master volume in
- // addition to needing to set an initial, default, master volume at HAL
- // load time.
- MVS_SETONLY,
-
- // MVS_FULL:
- // Audio HAL has support both for setting and getting master volume.
- // AudioFlinger should send all set and get master volume requests
- // directly to the HAL.
- MVS_FULL,
- };
-
- class AudioHwDevice {
- public:
- AudioHwDevice(const char *moduleName, audio_hw_device_t *hwDevice) :
- mModuleName(strdup(moduleName)), mHwDevice(hwDevice){}
- ~AudioHwDevice() { free((void *)mModuleName); }
-
- const char *moduleName() const { return mModuleName; }
- audio_hw_device_t *hwDevice() const { return mHwDevice; }
- private:
- const char * const mModuleName;
- audio_hw_device_t * const mHwDevice;
- };
-
mutable Mutex mLock;
DefaultKeyedVector< pid_t, wp<Client> > mClients; // see ~Client()
@@ -1851,7 +2002,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
// always take mLock before mHardwareLock
// These two fields are immutable after onFirstRef(), so no lock needed to access
- audio_hw_device_t* mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL
+ AudioHwDevice* mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL
DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*> mAudioHwDevs;
// for dump, indicates which hardware operation is currently in progress (but not stream ops)
@@ -1875,6 +2026,8 @@ mutable Mutex mLock; // mutex for process, commands and handl
AUDIO_HW_GET_INPUT_BUFFER_SIZE, // get_input_buffer_size
AUDIO_HW_GET_MASTER_VOLUME, // get_master_volume
AUDIO_HW_GET_PARAMETER, // get_parameters
+ AUDIO_HW_SET_MASTER_MUTE, // set_master_mute
+ AUDIO_HW_GET_MASTER_MUTE, // get_master_mute
};
mutable hardware_call_state mHardwareStatus; // for dump only
@@ -1883,11 +2036,10 @@ mutable Mutex mLock; // mutex for process, commands and handl
DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> > mPlaybackThreads;
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
- // both are protected by mLock
+ // member variables below are protected by mLock
float mMasterVolume;
- float mMasterVolumeSW;
- master_volume_support mMasterVolumeSupportLvl;
bool mMasterMute;
+ // end of variables protected by mLock
DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads;
@@ -1900,8 +2052,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
Vector<AudioSessionRef*> mAudioSessionRefs;
float masterVolume_l() const;
- float masterVolumeSW_l() const { return mMasterVolumeSW; }
- bool masterMute_l() const { return mMasterMute; }
+ bool masterMute_l() const;
audio_module_handle_t loadHwModule_l(const char *name);
Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session
@@ -1910,6 +2061,9 @@ mutable Mutex mLock; // mutex for process, commands and handl
private:
sp<Client> registerPid_l(pid_t pid); // always returns non-0
+ // for use from destructor
+ status_t closeOutput_nonvirtual(audio_io_handle_t output);
+ status_t closeInput_nonvirtual(audio_io_handle_t input);
};
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 3e4c55e0..1e4049ab 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -163,7 +163,7 @@ AudioMixer::~AudioMixer()
delete [] mState.resampleTemp;
}
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask)
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
{
uint32_t names = (~mTrackNames) & mConfiguredNames;
if (names != 0) {
@@ -189,6 +189,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask)
t->enabled = false;
t->format = 16;
t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
+ t->sessionId = sessionId;
// setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
t->bufferProvider = NULL;
t->downmixerBufferProvider = NULL;
@@ -270,7 +271,7 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
}
if (EffectCreate(&dwnmFxDesc.uuid,
- -2 /*sessionId*/, -2 /*ioId*/,// both not relevant here, using random value
+ pTrack->sessionId /*sessionId*/, -2 /*ioId not relevant here, using random value*/,
&pDbp->mDownmixHandle/*pHandle*/) != 0) {
ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName);
goto noDownmixForActiveTrack;
@@ -416,7 +417,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
case TRACK:
switch (param) {
case CHANNEL_MASK: {
- uint32_t mask = (uint32_t)value;
+ audio_channel_mask_t mask = (audio_channel_mask_t) value;
if (track.channelMask != mask) {
uint32_t channelCount = popcount(mask);
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
@@ -538,11 +539,23 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
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;
@@ -1085,6 +1098,12 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
e0 &= ~(1<<i);
track_t& t = state->tracks[i];
t.buffer.frameCount = state->frameCount;
+ int valid = t.bufferProvider->getValid();
+ if (valid != AudioBufferProvider::kValid) {
+ ALOGE("invalid bufferProvider=%p name=%d frameCount=%d valid=%#x enabledTracks=%#x",
+ t.bufferProvider, i, t.buffer.frameCount, valid, enabledTracks);
+ // expect to crash
+ }
t.bufferProvider->getNextBuffer(&t.buffer, pts);
t.frameCount = t.buffer.frameCount;
t.in = t.buffer.raw;
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 46deae75..63333578 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -23,7 +23,7 @@
#include <utils/threads.h>
-#include "AudioBufferProvider.h"
+#include <media/AudioBufferProvider.h>
#include "AudioResampler.h"
#include <audio_effects/effect_downmix.h>
@@ -91,7 +91,7 @@ public:
// For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
// Allocate a track name. Returns new track name if successful, -1 on failure.
- int getTrackName(audio_channel_mask_t channelMask);
+ int getTrackName(audio_channel_mask_t channelMask, int sessionId);
// Free an allocated track by name
void deleteTrackName(int name);
@@ -192,7 +192,7 @@ private:
DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
- int32_t padding;
+ int32_t sessionId;
// 16-byte boundary
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 0d139703..55e08269 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -49,6 +49,8 @@ static const char kCmdDeadlockedString[] = "AudioPolicyService command thread ma
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
+static const nsecs_t kAudioCommandTimeout = 3000000000; // 3 seconds
+
namespace {
extern struct audio_policy_service_ops aps_ops;
};
@@ -91,10 +93,6 @@ AudioPolicyService::AudioPolicyService()
if (rc)
return;
- property_get("ro.camera.sound.forced", value, "0");
- forced_val = strtol(value, NULL, 0);
- mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy, !forced_val);
-
ALOGI("Loaded audio policy from %s (%s)", module->name, module->id);
// load audio pre processing modules
@@ -223,7 +221,7 @@ audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use
audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
audio_format_t format,
- uint32_t channels,
+ audio_channel_mask_t channelMask,
audio_output_flags_t flags)
{
if (mpAudioPolicy == NULL) {
@@ -231,7 +229,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
}
ALOGV("getOutput() tid %d", gettid());
Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channels, flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channelMask, flags);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -271,8 +269,7 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
uint32_t samplingRate,
audio_format_t format,
- uint32_t channels,
- audio_in_acoustics_t acoustics,
+ audio_channel_mask_t channelMask,
int audioSession)
{
if (mpAudioPolicy == NULL) {
@@ -283,8 +280,9 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return 0;
}
Mutex::Autolock _l(mLock);
+ // the audio_in_acoustics_t parameter is ignored by get_input()
audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
- format, channels, acoustics);
+ format, channelMask, (audio_in_acoustics_t) 0);
if (input == 0) {
return input;
@@ -373,6 +371,7 @@ status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream,
if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
+ Mutex::Autolock _l(mLock);
mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax);
return NO_ERROR;
}
@@ -390,7 +389,7 @@ status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
-
+ Mutex::Autolock _l(mLock);
if (mpAudioPolicy->set_stream_volume_index_for_device) {
return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy,
stream,
@@ -411,6 +410,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream,
if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
return BAD_VALUE;
}
+ Mutex::Autolock _l(mLock);
if (mpAudioPolicy->get_stream_volume_index_for_device) {
return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy,
stream,
@@ -439,7 +439,7 @@ audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stre
return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream);
}
-audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc)
+audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc)
{
if (mpAudioPolicy == NULL) {
return NO_INIT;
@@ -448,7 +448,7 @@ audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *de
return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc);
}
-status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc,
+status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
uint32_t strategy,
int session,
@@ -485,6 +485,18 @@ bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inP
return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs);
}
+bool AudioPolicyService::isSourceActive(audio_source_t source) const
+{
+ if (mpAudioPolicy == NULL) {
+ return false;
+ }
+ if (mpAudioPolicy->is_source_active == 0) {
+ return false;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpAudioPolicy->is_source_active(mpAudioPolicy, source);
+}
+
status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession,
effect_descriptor_t *descriptors,
uint32_t *count)
@@ -512,7 +524,7 @@ status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession,
for (size_t i = 0; i < effects.size(); i++) {
effect_descriptor_t desc = effects[i]->descriptor();
if (i < *count) {
- memcpy(descriptors + i, &desc, sizeof(effect_descriptor_t));
+ descriptors[i] = desc;
}
}
if (effects.size() > *count) {
@@ -687,7 +699,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
data->mIO);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
}
delete data;
}break;
@@ -698,7 +710,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
}
delete data;
}break;
@@ -709,7 +721,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
}
delete data;
}break;
@@ -778,7 +790,6 @@ void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::ton
data->mType = type;
data->mStream = stream;
command->mParam = (void *)data;
- command->mWaitStatus = false;
Mutex::Autolock _l(mLock);
insertCommand_l(command);
ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream);
@@ -790,7 +801,6 @@ void AudioPolicyService::AudioCommandThread::stopToneCommand()
AudioCommand *command = new AudioCommand();
command->mCommand = STOP_TONE;
command->mParam = NULL;
- command->mWaitStatus = false;
Mutex::Autolock _l(mLock);
insertCommand_l(command);
ALOGV("AudioCommandThread() adding tone stop");
@@ -811,11 +821,6 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type
data->mVolume = volume;
data->mIO = output;
command->mParam = data;
- if (delayMs == 0) {
- command->mWaitStatus = true;
- } else {
- command->mWaitStatus = false;
- }
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
@@ -824,7 +829,7 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
@@ -841,11 +846,6 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_hand
data->mIO = ioHandle;
data->mKeyValuePairs = String8(keyValuePairs);
command->mParam = data;
- if (delayMs == 0) {
- command->mWaitStatus = true;
- } else {
- command->mWaitStatus = false;
- }
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d",
@@ -854,7 +854,7 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_hand
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
@@ -868,11 +868,6 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
VoiceVolumeData *data = new VoiceVolumeData();
data->mVolume = volume;
command->mParam = data;
- if (delayMs == 0) {
- command->mWaitStatus = true;
- } else {
- command->mWaitStatus = false;
- }
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
ALOGV("AudioCommandThread() adding set voice volume volume %f", volume);
@@ -880,7 +875,7 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
@@ -891,6 +886,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
ssize_t i; // not size_t because i will count down to -1
Vector <AudioCommand *> removedCommands;
+ nsecs_t time = 0;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
@@ -936,6 +932,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
} else {
data2->mKeyValuePairs = param2.toString();
}
+ time = command2->mTime;
} break;
case SET_VOLUME: {
@@ -946,6 +943,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
ALOGV("Filtering out volume command on output %d for stream %d",
data->mIO, data->mStream);
removedCommands.add(command2);
+ time = command2->mTime;
} break;
case START_TONE:
case STOP_TONE:
@@ -967,6 +965,17 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
}
removedCommands.clear();
+ // wait for status only if delay is 0 and command time was not modified above
+ if (delayMs == 0 && time == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ // update command time if modified above
+ if (time != 0) {
+ command->mTime = time;
+ }
+
// insert command at the right place according to its time stamp
ALOGV("inserting command: %d at index %d, num commands %d",
command->mCommand, (int)i+1, mAudioCommands.size());
@@ -1422,7 +1431,7 @@ static int aps_restore_output(void *service, audio_io_handle_t output)
return af->restoreOutput(output);
}
-// deprecated: replaced by aps_open_input_on_module()
+// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored
static audio_io_handle_t aps_open_input(void *service,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index fbca0008..63f95494 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -64,7 +64,7 @@ public:
virtual audio_io_handle_t getOutput(audio_stream_type_t stream,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
- uint32_t channels = 0,
+ audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags =
AUDIO_OUTPUT_FLAG_NONE);
virtual status_t startOutput(audio_io_handle_t output,
@@ -77,9 +77,7 @@ public:
virtual audio_io_handle_t getInput(audio_source_t inputSource,
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
- uint32_t channels = 0,
- audio_in_acoustics_t acoustics =
- (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/,
+ audio_channel_mask_t channelMask = 0,
int audioSession = 0);
virtual status_t startInput(audio_io_handle_t input);
virtual status_t stopInput(audio_io_handle_t input);
@@ -97,8 +95,8 @@ public:
virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
- virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc);
- virtual status_t registerEffect(effect_descriptor_t *desc,
+ virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc);
+ virtual status_t registerEffect(const effect_descriptor_t *desc,
audio_io_handle_t io,
uint32_t strategy,
int session,
@@ -106,6 +104,7 @@ public:
virtual status_t unregisterEffect(int id);
virtual status_t setEffectEnabled(int id, bool enabled);
virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+ virtual bool isSourceActive(audio_source_t source) const;
virtual status_t queryDefaultPreProcessing(int audioSession,
effect_descriptor_t *descriptors,
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index fbb54cf9..2c3c719b 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -23,10 +23,8 @@
#include <cutils/log.h>
#include <cutils/properties.h>
#include "AudioResampler.h"
-#if 0
#include "AudioResamplerSinc.h"
#include "AudioResamplerCubic.h"
-#endif
#ifdef __arm__
#include <machine/cpu-features.h>
@@ -42,7 +40,7 @@ namespace android {
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,38 +77,129 @@ private:
int mX0R;
};
+bool AudioResampler::qualityIsSupported(src_quality quality)
+{
+ switch (quality) {
+ case DEFAULT_QUALITY:
+ case LOW_QUALITY:
+ case MED_QUALITY:
+ case HIGH_QUALITY:
+ 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;
+ }
+ }
}
+}
- if (quality == DEFAULT)
- quality = LOW_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 = 130; // an arbitrary number that permits 3 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;
+ }
+
+ // 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
case MED_QUALITY:
ALOGV("Create cubic Resampler");
resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
break;
case HIGH_QUALITY:
- ALOGV("Create sinc Resampler");
+ ALOGV("Create HIGH_QUALITY sinc Resampler");
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
break;
-#endif
+ case VERY_HIGH_QUALITY:
+ ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
+ resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
+ break;
}
// initialize resampler
@@ -119,17 +208,20 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
}
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;
@@ -138,6 +230,15 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
}
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 1610e003..2b8694f1 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -20,7 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include "AudioBufferProvider.h"
+#include <media/AudioBufferProvider.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -35,14 +35,15 @@ public:
// 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
+ HIGH_QUALITY=3,
+ 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();
@@ -60,6 +61,9 @@ public:
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;
@@ -70,7 +74,7 @@ protected:
// 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&);
@@ -93,6 +97,19 @@ protected:
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 892785aa..203b9335 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -29,7 +29,7 @@ namespace android {
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 76662d80..3f22ca64 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -14,9 +14,36 @@
* limitations under the License.
*/
+#define LOG_TAG "AudioResamplerSinc"
+//#define LOG_NDEBUG 0
+
+#include <malloc.h>
#include <string.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+
+#include <utils/Log.h>
+
#include "AudioResamplerSinc.h"
+
+
+#if defined(__arm__) && !defined(__thumb__)
+#define USE_INLINE_ASSEMBLY (true)
+#else
+#define USE_INLINE_ASSEMBLY (false)
+#endif
+
+#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__)
+#define USE_NEON (true)
+#else
+#define USE_NEON (false)
+#endif
+
+
namespace android {
// ----------------------------------------------------------------------------
@@ -24,45 +51,345 @@ namespace android {
/*
* These coeficients are computed with the "fir" utility found in
* tools/resampler_tools
- * TODO: A good optimization would be to transpose this matrix, to take
- * better advantage of the data-cache.
+ * cmd-line: fir -l 7 -s 48000 -c 20478
*/
-const int32_t AudioResamplerSinc::mFirCoefsUp[] = {
- 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621,
- 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9,
- 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9,
- 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798,
- 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636,
- 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2,
- 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070,
- 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000 // this one is needed for lerping the last coefficient
+const int32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = {
+ 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300,
+ 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592,
+ 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e,
+ 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f,
+ 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10,
+ 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e,
+ 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6,
+ 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5,
+ 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd,
+ 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb,
+ 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2,
+ 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3,
+ 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371,
+ 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921,
+ 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5,
+ 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564,
+ 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04,
+ 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab,
+ 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62,
+ 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230,
+ 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e,
+ 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337,
+ 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85,
+ 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112,
+ 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec,
+ 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d,
+ 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3,
+ 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb,
+ 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244,
+ 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c,
+ 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012,
+ 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075,
+ 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97,
+ 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486,
+ 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854,
+ 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812,
+ 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3,
+ 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7,
+ 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1,
+ 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5,
+ 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54,
+ 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533,
+ 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84,
+ 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c,
+ 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace,
+ 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef,
+ 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2,
+ 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d,
+ 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434,
+ 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc,
+ 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99,
+ 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781,
+ 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8,
+ 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125,
+ 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b,
+ 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070,
+ 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69,
+ 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b,
+ 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b,
+ 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e,
+ 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba,
+ 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3,
+ 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd,
+ 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f,
+ 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb,
+ 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7,
+ 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6,
+ 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae,
+ 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1,
+ 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5,
+ 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b,
+ 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8,
+ 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff,
+ 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422,
+ 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245,
+ 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79,
+ 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2,
+ 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760,
+ 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235,
+ 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64,
+ 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc,
+ 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f,
+ 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac,
+ 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5,
+ 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9,
+ 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67,
+ 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce,
+ 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e,
+ 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535,
+ 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50,
+ 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e,
+ 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c,
+ 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8,
+ 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d,
+ 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108,
+ 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5,
+ 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130,
+ 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4,
+ 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb,
+ 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21,
+ 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff,
+ 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f,
+ 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b,
+ 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb,
+ 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8,
+ 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa,
+ 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a,
+ 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e,
+ 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d,
+ 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0,
+ 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b,
+ 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65,
+ 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74,
+ 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e,
+ 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426,
+ 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2,
+ 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65,
+ 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5,
+ 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54,
+ 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7,
+ 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e,
+ 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f,
+ 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa,
+ 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2,
+ 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39,
+ 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f,
+ 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7,
+ 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10,
+ 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c,
};
/*
- * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz)
- * It's possible to use the above coefficient for any down-sampling
- * at the expense of a slower processing loop (we can interpolate
- * these coefficient from the above by "Stretching" them in time).
+ * These coefficients are optimized for 48KHz -> 44.1KHz
+ * cmd-line: fir -l 7 -s 48000 -c 17189
*/
-const int32_t AudioResamplerSinc::mFirCoefsDown[] = {
- 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540,
- 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4,
- 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa,
- 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066,
- 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf,
- 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d,
- 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a,
- 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000,
- 0x00000000 // this one is needed for lerping the last coefficient
+const int32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = {
+ 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631,
+ 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d,
+ 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545,
+ 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86,
+ 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639,
+ 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b,
+ 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4,
+ 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce,
+ 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12,
+ 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9,
+ 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c,
+ 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1,
+ 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910,
+ 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f,
+ 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057,
+ 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b,
+ 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812,
+ 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02,
+ 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0,
+ 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf,
+ 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794,
+ 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934,
+ 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3,
+ 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2,
+ 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7,
+ 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542,
+ 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968,
+ 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b,
+ 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c,
+ 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef,
+ 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3,
+ 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c,
+ 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea,
+ 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf,
+ 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb,
+ 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0,
+ 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef,
+ 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508,
+ 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b,
+ 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9,
+ 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93,
+ 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9,
+ 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b,
+ 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9,
+ 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3,
+ 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa,
+ 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd,
+ 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c,
+ 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119,
+ 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821,
+ 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156,
+ 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8,
+ 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07,
+ 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962,
+ 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab,
+ 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1,
+ 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5,
+ 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976,
+ 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6,
+ 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5,
+ 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764,
+ 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473,
+ 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3,
+ 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6,
+ 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b,
+ 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686,
+ 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36,
+ 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d,
+ 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd,
+ 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9,
+ 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0,
+ 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7,
+ 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f,
+ 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a,
+ 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c,
+ 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7,
+ 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e,
+ 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5,
+ 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de,
+ 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d,
+ 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57,
+ 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf,
+ 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89,
+ 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa,
+ 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17,
+ 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5,
+ 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8,
+ 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5,
+ 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4,
+ 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329,
+ 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a,
+ 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd,
+ 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a,
+ 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857,
+ 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a,
+ 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c,
+ 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52,
+ 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6,
+ 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60,
+ 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86,
+ 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62,
+ 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec,
+ 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e,
+ 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef,
+ 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b,
+ 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a,
+ 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5,
+ 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8,
+ 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d,
+ 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d,
+ 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525,
+ 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e,
+ 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84,
+ 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453,
+ 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86,
+ 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a,
+ 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09,
+ 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552,
+ 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef,
+ 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df,
+ 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e,
+ 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9,
+ 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e,
+ 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a,
+ 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc,
+ 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1,
+ 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787,
+ 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae,
+ 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713,
};
+// 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;
+ }
+
+ readResampleFirNumCoeffFn readResampleFirNumCoeff;
+ readResampleFirLerpIntBitsFn readResampleFirLerpIntBits;
+
+ readResampleCoefficients = (readCoefficientsFn)
+ dlsym(resampleCoeffLib, "readResamplerCoefficients");
+ readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
+ dlsym(resampleCoeffLib, "readResampleFirNumCoeff");
+ 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;
+ c->coefsBits = readResampleFirLerpIntBits();
+ 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("coefsBits = %d", c->coefsBits);
+ ALOGV("halfNumCoefs = %d", c->halfNumCoefs);
+ // note that we "leak" resampleCoeffLib until the process exits
+}
+
// ----------------------------------------------------------------------------
static inline
int32_t mulRL(int left, int32_t in, uint32_t vRL)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
if (left) {
asm( "smultb %[out], %[in], %[vRL] \n"
@@ -77,18 +404,15 @@ int32_t mulRL(int left, int32_t in, uint32_t vRL)
}
return out;
#else
- if (left) {
- return int16_t(in>>16) * int16_t(vRL&0xFFFF);
- } else {
- return int16_t(in>>16) * int16_t(vRL>>16);
- }
+ int16_t v = left ? int16_t(vRL) : int16_t(vRL>>16);
+ return int32_t((int64_t(in) * v) >> 16);
#endif
}
static inline
int32_t mulAdd(int16_t in, int32_t v, int32_t a)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
asm( "smlawb %[out], %[v], %[in], %[a] \n"
: [out]"=r"(out)
@@ -96,16 +420,14 @@ int32_t mulAdd(int16_t in, int32_t v, int32_t a)
: );
return out;
#else
- return a + in * (v>>16);
- // improved precision
- // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16);
+ return a + int32_t((int64_t(v) * in) >> 16);
#endif
}
static inline
int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
if (left) {
asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
@@ -120,22 +442,17 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
}
return out;
#else
- if (left) {
- return a + (int16_t(inRL&0xFFFF) * (v>>16));
- //improved precision
- // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16);
- } else {
- return a + (int16_t(inRL>>16) * (v>>16));
- }
+ int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
+ return a + int32_t((int64_t(v) * s) >> 16);
#endif
}
// ----------------------------------------------------------------------------
AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
- int inChannelCount, int32_t sampleRate)
- : AudioResampler(bitDepth, inChannelCount, sampleRate),
- mState(0)
+ int inChannelCount, int32_t sampleRate, src_quality quality)
+ : AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
+ mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0)
{
/*
* Layout of the state buffer for 32 tap:
@@ -153,26 +470,50 @@ AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
*
*/
- const size_t numCoefs = 2*halfNumCoefs;
- const size_t stateSize = numCoefs * inChannelCount * 2;
- mState = new int16_t[stateSize];
- memset(mState, 0, sizeof(int16_t)*stateSize);
- mImpulse = mState + (halfNumCoefs-1)*inChannelCount;
- mRingFull = mImpulse + (numCoefs+1)*inChannelCount;
+ mVolumeSIMD[0] = 0;
+ mVolumeSIMD[1] = 0;
+
+ // 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()
-{
- delete [] mState;
+
+AudioResamplerSinc::~AudioResamplerSinc() {
+ free(mState);
}
void AudioResamplerSinc::init() {
+ const Constants& c(*mConstants);
+ const size_t numCoefs = 2 * c.halfNumCoefs;
+ const size_t stateSize = numCoefs * mChannelCount * 2;
+ mState = (int16_t*)memalign(32, stateSize*sizeof(int16_t));
+ memset(mState, 0, sizeof(int16_t)*stateSize);
+ mImpulse = mState + (c.halfNumCoefs-1)*mChannelCount;
+ mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
+}
+
+void AudioResamplerSinc::setVolume(int16_t left, int16_t right) {
+ AudioResampler::setVolume(left, right);
+ mVolumeSIMD[0] = int32_t(left)<<16;
+ mVolumeSIMD[1] = int32_t(right)<<16;
}
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
+ // 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 (mConstants == &veryHighQualityConstants && readResampleCoefficients) {
+ mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate );
+ } else {
+ mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
+ }
// select the appropriate resampler
switch (mChannelCount) {
@@ -190,6 +531,8 @@ template<int CHANNELS>
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
+ const Constants& c(*mConstants);
+ const size_t headOffset = c.halfNumCoefs*CHANNELS;
int16_t* impulse = mImpulse;
uint32_t vRL = mVolumeRL;
size_t inputIndex = mInputIndex;
@@ -224,43 +567,31 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
}
}
}
- int16_t *in = mBuffer.i16;
+ int16_t const * const in = mBuffer.i16;
const size_t frameCount = mBuffer.frameCount;
// Always read-in the first samples from the input buffer
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
- head[0] = in[inputIndex*CHANNELS + 0];
- if (CHANNELS == 2)
- head[1] = in[inputIndex*CHANNELS + 1];
+ int16_t* head = impulse + headOffset;
+ for (size_t i=0 ; i<CHANNELS ; i++) {
+ head[i] = in[inputIndex*CHANNELS + i];
+ }
// handle boundary case
- int32_t l, r;
- while (outputIndex < outputSampleCount) {
- filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse);
- out[outputIndex++] += 2 * mulRL(1, l, vRL);
- out[outputIndex++] += 2 * mulRL(0, r, vRL);
+ while (CC_LIKELY(outputIndex < outputSampleCount)) {
+ filterCoefficient<CHANNELS>(&out[outputIndex], phaseFraction, impulse, vRL);
+ outputIndex += 2;
phaseFraction += phaseIncrement;
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
- if (phaseIndex == 1) {
- inputIndex++;
- if (inputIndex >= frameCount)
- break; // need a new buffer
- read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
- } else if (phaseIndex == 2) { // maximum value
- inputIndex++;
- if (inputIndex >= frameCount)
- break; // 0 frame available, 2 frames needed
- // read first frame
- read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
+ const size_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ for (size_t i=0 ; i<phaseIndex ; i++) {
inputIndex++;
- if (inputIndex >= frameCount)
- break; // 0 frame available, 1 frame needed
- // read second frame
+ if (inputIndex >= frameCount) {
+ goto done; // need a new buffer
+ }
read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
}
}
-
+done:
// if done with buffer, save samples
if (inputIndex >= frameCount) {
inputIndex -= frameCount;
@@ -286,63 +617,215 @@ void AudioResamplerSinc::read(
int16_t*& impulse, uint32_t& phaseFraction,
const int16_t* in, size_t inputIndex)
{
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
impulse += CHANNELS;
phaseFraction -= 1LU<<kNumPhaseBits;
- if (impulse >= mRingFull) {
- const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
+
+ const Constants& c(*mConstants);
+ if (CC_UNLIKELY(impulse >= mRingFull)) {
+ const size_t stateSize = (c.halfNumCoefs*2)*CHANNELS;
memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
impulse -= stateSize;
}
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
- head[0] = in[inputIndex*CHANNELS + 0];
- if (CHANNELS == 2)
- head[1] = in[inputIndex*CHANNELS + 1];
+
+ int16_t* head = impulse + c.halfNumCoefs*CHANNELS;
+ for (size_t i=0 ; i<CHANNELS ; i++) {
+ head[i] = in[inputIndex*CHANNELS + i];
+ }
}
template<int CHANNELS>
void AudioResamplerSinc::filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples)
+ int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL)
{
+ // NOTE: be very careful when modifying the code here. register
+ // pressure is very high and a small change might cause the compiler
+ // to generate far less efficient code.
+ // Always sanity check the result with objdump or test-resample.
+
// 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;
- if ((indexP == 0) && (lerpP == 0)) {
- indexN = cMask >> cShift;
- lerpN = pMask >> pShift;
- }
+ const Constants& c(*mConstants);
+ const int32_t ONE = c.cMask | c.pMask;
+ uint32_t indexP = ( phase & c.cMask) >> c.cShift;
+ uint32_t lerpP = ( phase & c.pMask) >> c.pShift;
+ uint32_t indexN = ((ONE-phase) & c.cMask) >> c.cShift;
+ uint32_t lerpN = ((ONE-phase) & c.pMask) >> c.pShift;
+
+ const size_t offset = c.halfNumCoefs;
+ indexP *= offset;
+ indexN *= offset;
+
+ int32_t const* coefsP = mFirCoefs + indexP;
+ int32_t const* coefsN = mFirCoefs + indexN;
+ int16_t const* sP = samples;
+ int16_t const* sN = samples + CHANNELS;
- l = 0;
- r = 0;
- 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++) {
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ size_t count = offset;
+
+ if (!USE_NEON) {
+ int32_t l = 0;
+ int32_t r = 0;
+ for (size_t i=0 ; i<count ; i++) {
+ interpolate<CHANNELS>(l, r, coefsP++, offset, lerpP, sP);
+ sP -= CHANNELS;
+ interpolate<CHANNELS>(l, r, coefsN++, offset, lerpN, sN);
+ sN += CHANNELS;
+ }
+ out[0] += 2 * mulRL(1, l, vRL);
+ out[1] += 2 * mulRL(0, r, vRL);
+ } else if (CHANNELS == 1) {
+ int32_t const* coefsP1 = coefsP + offset;
+ int32_t const* coefsN1 = coefsN + offset;
+ sP -= CHANNELS*3;
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n" // load the positive phase
+ "vmov.32 d2[1], %[lerpN] \n" // load the negative phase
+ "veor q0, q0, q0 \n" // result, initialize to 0
+ "vshl.s32 d2, d2, #16 \n" // convert to 32 bits
+
+ "1: \n"
+ "vld1.16 { d4}, [%[sP]] \n" // load 4 16-bits stereo samples
+ "vld1.32 { q8}, [%[coefsP0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 { q9}, [%[coefsP1]:128]! \n" // load 4 32-bits coefs for interpolation
+ "vld1.16 { d6}, [%[sN]]! \n" // load 4 16-bits stereo samples
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN1]:128]! \n" // load 4 32-bits coefs for interpolation
+
+ "vrev64.16 d4, d4 \n" // reverse 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n" // interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n" // interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n" // extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n" // interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[1] \n" // interpolate (step3) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n" // extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n" // interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n" // interpolate (step4) 2nd set
+ "subs %[count], %[count], #4 \n" // update loop counter
+
+ "vqrdmulh.s32 q12, q12, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n" // multiply samples by interpolated coef
+ "sub %[sP], %[sP], #8 \n" // move pointer to next set of samples
+
+ "vadd.s32 q0, q0, q12 \n" // accumulate result
+ "vadd.s32 q0, q0, q14 \n" // accumulate result
+
+ "bne 1b \n" // loop
+
+ "vld1.s32 {d2}, [%[vLR]] \n" // load volumes
+ "vld1.s32 {d3}, %[out] \n" // load the output
+ "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums
+ "vpadd.s32 d0, d0, d0 \n" // together
+ "vdup.i32 d0, d0[0] \n" // interleave L,R channels
+ "vqrdmulh.s32 d0, d0, d2 \n" // apply volume
+ "vadd.s32 d3, d3, d0 \n" // accumulate result
+ "vst1.s32 {d3}, %[out] \n" // store result
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [lerpN] "r" (lerpN),
+ [vLR] "r" (mVolumeSIMD)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q14"
+ );
+ } else if (CHANNELS == 2) {
+ int32_t const* coefsP1 = coefsP + offset;
+ int32_t const* coefsN1 = coefsN + offset;
+ sP -= CHANNELS*3;
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n" // load the positive phase
+ "vmov.32 d2[1], %[lerpN] \n" // load the negative phase
+ "veor q0, q0, q0 \n" // result, initialize to 0
+ "veor q4, q4, q4 \n" // result, initialize to 0
+ "vshl.s32 d2, d2, #16 \n" // convert to 32 bits
+
+ "1: \n"
+ "vld2.16 {d4,d5}, [%[sP]] \n" // load 4 16-bits stereo samples
+ "vld1.32 { q8}, [%[coefsP0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 { q9}, [%[coefsP1]:128]! \n" // load 4 32-bits coefs for interpolation
+ "vld2.16 {d6,d7}, [%[sN]]! \n" // load 4 16-bits stereo samples
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN1]:128]! \n" // load 4 32-bits coefs for interpolation
+
+ "vrev64.16 d4, d4 \n" // reverse 2 frames of the positive side
+ "vrev64.16 d5, d5 \n" // reverse 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n" // interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n" // interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n" // extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n" // extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n" // interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[1] \n" // interpolate (step3) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n" // extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n" // extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n" // interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n" // interpolate (step4) 2nd set
+ "subs %[count], %[count], #4 \n" // update loop counter
+
+ "vqrdmulh.s32 q12, q12, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q10 \n" // multiply samples by interpolated coef
+ "sub %[sP], %[sP], #16 \n" // move pointer to next set of samples
+
+ "vadd.s32 q0, q0, q12 \n" // accumulate result
+ "vadd.s32 q4, q4, q13 \n" // accumulate result
+ "vadd.s32 q0, q0, q14 \n" // accumulate result
+ "vadd.s32 q4, q4, q15 \n" // accumulate result
+
+ "bne 1b \n" // loop
+
+ "vld1.s32 {d2}, [%[vLR]] \n" // load volumes
+ "vld1.s32 {d3}, %[out] \n" // load the output
+ "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums from q0
+ "vpadd.s32 d8, d8, d9 \n" // add all 4 partial sums from q4
+ "vpadd.s32 d0, d0, d0 \n" // together
+ "vpadd.s32 d8, d8, d8 \n" // together
+ "vtrn.s32 d0, d8 \n" // interlace L,R channels
+ "vqrdmulh.s32 d0, d0, d2 \n" // apply volume
+ "vadd.s32 d3, d3, d0 \n" // accumulate result
+ "vst1.s32 {d3}, %[out] \n" // store result
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [lerpN] "r" (lerpN),
+ [vLR] "r" (mVolumeSIMD)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3", "q4",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
}
}
template<int CHANNELS>
void AudioResamplerSinc::interpolate(
int32_t& l, int32_t& r,
- const int32_t* coefs, int16_t lerp, const int16_t* samples)
+ const int32_t* coefs, size_t offset,
+ int32_t lerp, const int16_t* samples)
{
int32_t c0 = coefs[0];
- int32_t c1 = coefs[1];
+ int32_t c1 = coefs[offset];
int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
if (CHANNELS == 2) {
uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
@@ -352,6 +835,5 @@ void AudioResamplerSinc::interpolate(
r = l = mulAdd(samples[0], sinc, l);
}
}
-
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index f0a07b8d..09c68660 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -25,11 +25,17 @@
namespace android {
+
+typedef const int32_t * (*readCoefficientsFn)(bool upDownSample);
+typedef int32_t (*readResampleFirNumCoeffFn)();
+typedef int32_t (*readResampleFirLerpIntBitsFn)();
+
// ----------------------------------------------------------------------------
class AudioResamplerSinc : public AudioResampler {
public:
- AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate);
+ AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate,
+ src_quality quality = HIGH_QUALITY);
virtual ~AudioResamplerSinc();
@@ -38,18 +44,21 @@ public:
private:
void init();
+ virtual void setVolume(int16_t left, int16_t right);
+
template<int CHANNELS>
void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
template<int CHANNELS>
inline void filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples);
+ int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL);
template<int CHANNELS>
inline void interpolate(
int32_t& l, int32_t& r,
- const int32_t* coefs, int16_t lerp, const int16_t* samples);
+ const int32_t* coefs, size_t offset,
+ int32_t lerp, const int16_t* samples);
template<int CHANNELS>
inline void read(int16_t*& impulse, uint32_t& phaseFraction,
@@ -58,6 +67,7 @@ private:
int16_t *mState;
int16_t *mImpulse;
int16_t *mRingFull;
+ int32_t mVolumeSIMD[2];
const int32_t * mFirCoefs;
static const int32_t mFirCoefsDown[];
@@ -65,21 +75,23 @@ private:
// ----------------------------------------------------------------------------
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 const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4
- static const int cShift = kNumPhaseBits - coefsBits; // 26
- static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000
-
- // 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 const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11
- static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11
-
- // number of zero-crossing on each side
- static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
+ static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7;
+
+ struct Constants {
+ int coefsBits;
+ int cShift;
+ uint32_t cMask;
+ int pShift;
+ uint32_t pMask;
+ // 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();
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioStreamInSource.cpp b/services/audioflinger/AudioStreamInSource.cpp
deleted file mode 100644
index 8b4bebf0..00000000
--- a/services/audioflinger/AudioStreamInSource.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "AudioStreamInSource"
-//#define LOG_NDEBUG 0
-
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include "AudioStreamInSource.h"
-
-namespace android {
-
-AudioStreamInSource::AudioStreamInSource(audio_stream_in *stream) :
- NBAIO_Source(),
- mStream(stream),
- mStreamBufferSizeBytes(0),
- mFramesOverrun(0),
- mOverruns(0)
-{
- ALOG_ASSERT(stream != NULL);
-}
-
-AudioStreamInSource::~AudioStreamInSource()
-{
-}
-
-ssize_t AudioStreamInSource::negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers)
-{
- if (mFormat == Format_Invalid) {
- mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common);
- audio_format_t streamFormat = mStream->common.get_format(&mStream->common);
- if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) {
- uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common);
- audio_channel_mask_t channelMask =
- (audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
- mFormat = Format_from_SR_C(sampleRate, popcount(channelMask));
- mBitShift = Format_frameBitShift(mFormat);
- }
- }
- return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
-}
-
-size_t AudioStreamInSource::framesOverrun()
-{
- uint32_t framesOverrun = mStream->get_input_frames_lost(mStream);
- if (framesOverrun > 0) {
- mFramesOverrun += framesOverrun;
- // FIXME only increment for contiguous ranges
- ++mOverruns;
- }
- return mFramesOverrun;
-}
-
-ssize_t AudioStreamInSource::read(void *buffer, size_t count)
-{
- if (CC_UNLIKELY(mFormat == Format_Invalid)) {
- return NEGOTIATE;
- }
- ssize_t bytesRead = mStream->read(mStream, buffer, count << mBitShift);
- if (bytesRead > 0) {
- size_t framesRead = bytesRead >> mBitShift;
- mFramesRead += framesRead;
- return framesRead;
- } else {
- return bytesRead;
- }
-}
-
-} // namespace android
diff --git a/services/audioflinger/AudioStreamInSource.h b/services/audioflinger/AudioStreamInSource.h
deleted file mode 100644
index 07d8c89e..00000000
--- a/services/audioflinger/AudioStreamInSource.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_STREAM_IN_SOURCE_H
-#define ANDROID_AUDIO_STREAM_IN_SOURCE_H
-
-#include <hardware/audio.h>
-#include "NBAIO.h"
-
-namespace android {
-
-// not multi-thread safe
-class AudioStreamInSource : public NBAIO_Source {
-
-public:
- AudioStreamInSource(audio_stream_in *stream);
- virtual ~AudioStreamInSource();
-
- // NBAIO_Port interface
-
- virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Sink interface
-
- //virtual size_t framesRead() const;
- virtual size_t framesOverrun();
- virtual size_t overruns() { (void) framesOverrun(); return mOverruns; }
-
- // This is an over-estimate, and could dupe the caller into making a blocking read()
- // FIXME Use an audio HAL API to query the buffer filling status when it's available.
- virtual ssize_t availableToRead() { return mStreamBufferSizeBytes >> mBitShift; }
-
- virtual ssize_t read(void *buffer, size_t count);
-
- // NBAIO_Sink end
-
-#if 0 // until necessary
- audio_stream_in *stream() const { return mStream; }
-#endif
-
-private:
- audio_stream_in * const mStream;
- size_t mStreamBufferSizeBytes; // as reported by get_buffer_size()
- size_t mFramesOverrun;
- size_t mOverruns;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_STREAM_IN_SOURCE_H
diff --git a/services/audioflinger/AudioStreamOutSink.cpp b/services/audioflinger/AudioStreamOutSink.cpp
deleted file mode 100644
index 8a5aa0c3..00000000
--- a/services/audioflinger/AudioStreamOutSink.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "AudioStreamOutSink"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include "AudioStreamOutSink.h"
-
-namespace android {
-
-AudioStreamOutSink::AudioStreamOutSink(audio_stream_out *stream) :
- NBAIO_Sink(),
- mStream(stream),
- mStreamBufferSizeBytes(0)
-{
- ALOG_ASSERT(stream != NULL);
-}
-
-AudioStreamOutSink::~AudioStreamOutSink()
-{
-}
-
-ssize_t AudioStreamOutSink::negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers)
-{
- if (mFormat == Format_Invalid) {
- mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common);
- audio_format_t streamFormat = mStream->common.get_format(&mStream->common);
- if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) {
- uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common);
- audio_channel_mask_t channelMask =
- (audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
- mFormat = Format_from_SR_C(sampleRate, popcount(channelMask));
- mBitShift = Format_frameBitShift(mFormat);
- }
- }
- return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers);
-}
-
-ssize_t AudioStreamOutSink::write(const void *buffer, size_t count)
-{
- if (!mNegotiated) {
- return NEGOTIATE;
- }
- ALOG_ASSERT(mFormat != Format_Invalid);
- ssize_t ret = mStream->write(mStream, buffer, count << mBitShift);
- if (ret > 0) {
- ret >>= mBitShift;
- mFramesWritten += ret;
- } else {
- // FIXME verify HAL implementations are returning the correct error codes e.g. WOULD_BLOCK
- }
- return ret;
-}
-
-} // namespace android
diff --git a/services/audioflinger/AudioStreamOutSink.h b/services/audioflinger/AudioStreamOutSink.h
deleted file mode 100644
index 1eff3f64..00000000
--- a/services/audioflinger/AudioStreamOutSink.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_STREAM_OUT_SINK_H
-#define ANDROID_AUDIO_STREAM_OUT_SINK_H
-
-#include <hardware/audio.h>
-#include "NBAIO.h"
-
-namespace android {
-
-// not multi-thread safe
-class AudioStreamOutSink : public NBAIO_Sink {
-
-public:
- AudioStreamOutSink(audio_stream_out *stream);
- virtual ~AudioStreamOutSink();
-
- // NBAIO_Port interface
-
- virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format();
-
- // NBAIO_Sink interface
-
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
-
- // This is an over-estimate, and could dupe the caller into making a blocking write()
- // FIXME Use an audio HAL API to query the buffer emptying status when it's available.
- virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes >> mBitShift; }
-
- virtual ssize_t write(const void *buffer, size_t count);
-
- // NBAIO_Sink end
-
-#if 0 // until necessary
- audio_stream_out *stream() const { return mStream; }
-#endif
-
-private:
- audio_stream_out * const mStream;
- size_t mStreamBufferSizeBytes; // as reported by get_buffer_size()
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_STREAM_OUT_SINK_H
diff --git a/services/audioflinger/ExtendedAudioBufferProvider.h b/services/audioflinger/ExtendedAudioBufferProvider.h
deleted file mode 100644
index 88279b48..00000000
--- a/services/audioflinger/ExtendedAudioBufferProvider.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_EXTENDED_AUDIO_BUFFER_PROVIDER_H
-#define ANDROID_EXTENDED_AUDIO_BUFFER_PROVIDER_H
-
-#include "AudioBufferProvider.h"
-
-namespace android {
-
-class ExtendedAudioBufferProvider : public AudioBufferProvider {
-public:
- virtual size_t framesReady() const = 0; // see description at AudioFlinger.h
-};
-
-} // namespace android
-
-#endif // ANDROID_EXTENDED_AUDIO_BUFFER_PROVIDER_H
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 76521323..3c8a256a 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -222,8 +222,8 @@ bool FastMixer::threadLoop()
mixBuffer = new short[frameCount * 2];
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
- overrunNs = (frameCount * 250000000LL) / sampleRate; // 0.25
- forceNs = (frameCount * 750000000LL) / sampleRate; // 0.75
+ overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
+ forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95
warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50
} else {
periodNs = 0;
@@ -281,8 +281,9 @@ bool FastMixer::threadLoop()
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
if (mixer != NULL) {
- // calling getTrackName with default channel mask
- name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO);
+ // calling getTrackName with default channel mask and a random invalid
+ // sessionId (no effects here)
+ name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO, -555);
ALOG_ASSERT(name >= 0);
fastTrackNames[i] = name;
mixer->setBufferProvider(name, bufferProvider);
@@ -399,8 +400,13 @@ bool FastMixer::threadLoop()
ftDump->mUnderruns = underruns;
ftDump->mFramesReady = framesReady;
}
+
+ int64_t pts;
+ if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts)))
+ pts = AudioBufferProvider::kInvalidPTS;
+
// process() is CPU-bound
- mixer->process(AudioBufferProvider::kInvalidPTS);
+ mixer->process(pts);
mixBufferState = MIXED;
} else if (mixBufferState == MIXED) {
mixBufferState = UNDEFINED;
@@ -448,6 +454,9 @@ bool FastMixer::threadLoop()
if (oldTsValid) {
time_t sec = newTs.tv_sec - oldTs.tv_sec;
long nsec = newTs.tv_nsec - oldTs.tv_nsec;
+ ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0),
+ "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld",
+ oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec);
if (nsec < 0) {
--sec;
nsec += 1000000000;
@@ -607,6 +616,20 @@ FastMixerDumpState::~FastMixerDumpState()
{
}
+// helper function called by qsort()
+static int compare_uint32_t(const void *pa, const void *pb)
+{
+ uint32_t a = *(const uint32_t *)pa;
+ uint32_t b = *(const uint32_t *)pb;
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
void FastMixerDumpState::dump(int fd)
{
if (mCommand == FastMixerState::INITIAL) {
@@ -669,10 +692,18 @@ void FastMixerDumpState::dump(int fd)
CentralTendencyStatistics kHz, loadMHz;
uint32_t previousCpukHz = 0;
#endif
+ // Assuming a normal distribution for cycle times, three standard deviations on either side of
+ // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the
+ // sample set, we get 99.8% combined, or close to three standard deviations.
+ static const uint32_t kTailDenominator = 1000;
+ uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL;
// loop over all the samples
- for (; n > 0; --n) {
+ for (uint32_t j = 0; j < n; ++j) {
size_t i = oldestClosed++ & (kSamplingN - 1);
uint32_t wallNs = mMonotonicNs[i];
+ if (tail != NULL) {
+ tail[j] = wallNs;
+ }
wall.sample(wallNs);
uint32_t sampleLoadNs = mLoadNs[i];
loadNs.sample(sampleLoadNs);
@@ -706,6 +737,23 @@ void FastMixerDumpState::dump(int fd)
" mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
#endif
+ if (tail != NULL) {
+ qsort(tail, n, sizeof(uint32_t), compare_uint32_t);
+ // assume same number of tail samples on each side, left and right
+ uint32_t count = n / kTailDenominator;
+ CentralTendencyStatistics left, right;
+ for (uint32_t i = 0; i < count; ++i) {
+ left.sample(tail[i]);
+ right.sample(tail[n - (i + 1)]);
+ }
+ fdprintf(fd, "Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
+ " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
+ " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+ left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
+ right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
+ right.stddev()*1e-6);
+ delete[] tail;
+ }
#endif
// The active track mask and track states are updated non-atomically.
// So if we relied on isActive to decide whether to display,
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index bc69c9c4..6e53f217 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -18,8 +18,8 @@
#define ANDROID_AUDIO_FAST_MIXER_STATE_H
#include <system/audio.h>
-#include "ExtendedAudioBufferProvider.h"
-#include "NBAIO.h"
+#include <media/ExtendedAudioBufferProvider.h>
+#include <media/nbaio/NBAIO.h>
namespace android {
diff --git a/services/audioflinger/LibsndfileSink.cpp b/services/audioflinger/LibsndfileSink.cpp
deleted file mode 100644
index efc1c8f7..00000000
--- a/services/audioflinger/LibsndfileSink.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "LibsndfileSink"
-//#define LOG_NDEBUG 0
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include "LibsndfileSink.h"
-
-namespace android {
-
-LibsndfileSink::LibsndfileSink(SNDFILE *sndfile, const SF_INFO &sfinfo) :
- NBAIO_Sink(Format_from_SR_C(sfinfo.samplerate, sfinfo.channels)),
- mSndfile(sndfile)
-{
-}
-
-LibsndfileSink::~LibsndfileSink()
-{
- // do not close mSndfile; we don't own it
-}
-
-ssize_t LibsndfileSink::write(const void *buffer, size_t count)
-{
- if (!mNegotiated) {
- return (ssize_t) NEGOTIATE;
- }
- if (mSndfile == NULL) {
- return (ssize_t) NO_INIT;
- }
- sf_count_t actual = sf_writef_short(mSndfile, (short *) buffer, (sf_count_t) count);
- mFramesWritten += actual;
- return actual;
-}
-
-} // namespace android
diff --git a/services/audioflinger/LibsndfileSink.h b/services/audioflinger/LibsndfileSink.h
deleted file mode 100644
index f5d53d51..00000000
--- a/services/audioflinger/LibsndfileSink.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_LIBSNDFILE_SINK_H
-#define ANDROID_AUDIO_LIBSNDFILE_SINK_H
-
-#include "NBAIO.h"
-#include "sndfile.h"
-
-// Implementation of NBAIO_Sink that wraps a libsndfile opened in SFM_WRITE mode
-
-namespace android {
-
-class LibsndfileSink : public NBAIO_Sink {
-
-public:
- LibsndfileSink(SNDFILE *sndfile, const SF_INFO &sfinfo);
- virtual ~LibsndfileSink();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Sink interface
-
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
- //virtual ssize_t availableToWrite() const;
- virtual ssize_t write(const void *buffer, size_t count);
- //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block);
-
-private:
- SNDFILE * mSndfile;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_LIBSNDFILE_SINK_H
diff --git a/services/audioflinger/LibsndfileSource.cpp b/services/audioflinger/LibsndfileSource.cpp
deleted file mode 100644
index 28317d65..00000000
--- a/services/audioflinger/LibsndfileSource.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "LibsndfileSource"
-//#define LOG_NDEBUG 0
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include "LibsndfileSource.h"
-
-namespace android {
-
-LibsndfileSource::LibsndfileSource(SNDFILE *sndfile, const SF_INFO &sfinfo, bool loop) :
- NBAIO_Source(Format_from_SR_C(sfinfo.samplerate, sfinfo.channels)),
- mSndfile(sndfile),
- mEstimatedFramesUntilEOF(sfinfo.frames),
- mLooping(loop && sfinfo.seekable),
- mReadAnyFramesThisLoopCycle(false)
-{
-}
-
-LibsndfileSource::~LibsndfileSource()
-{
- // do not close mSndfile; we don't own it
-}
-
-ssize_t LibsndfileSource::availableToRead()
-{
- // after we reach the presumed EOF, report infinity just in case there's actually more
- return !mLooping && mEstimatedFramesUntilEOF > 0 ? mEstimatedFramesUntilEOF : SSIZE_MAX;
-}
-
-ssize_t LibsndfileSource::read(void *buffer, size_t count)
-{
- if (!mNegotiated) {
- return (ssize_t) NEGOTIATE;
- }
- if (mSndfile == NULL) {
- return (ssize_t) NO_INIT;
- }
- sf_count_t actual = sf_readf_short(mSndfile, (short *) buffer, (sf_count_t) count);
- // Detect EOF by zero frames read, not by mFramesUntilEOF as it could be inaccurate
- if (actual == 0) {
- if (mLooping) {
- if (mReadAnyFramesThisLoopCycle) {
- (void) sf_seek(mSndfile, (sf_count_t) 0, SEEK_SET);
- mReadAnyFramesThisLoopCycle = false;
- } else {
- // We didn't read any frames during the current loop cycle, so disable
- // further looping to prevent the caller from busy waiting at read().
- // This is especially important when looping an empty file.
- mLooping = false;
- }
- }
- } else {
- mFramesRead += actual;
- if (actual >= mEstimatedFramesUntilEOF) {
- mEstimatedFramesUntilEOF = 0;
- } else {
- mEstimatedFramesUntilEOF -= actual;
- }
- mReadAnyFramesThisLoopCycle = true;
- }
- return actual;
-}
-
-} // namespace android
diff --git a/services/audioflinger/LibsndfileSource.h b/services/audioflinger/LibsndfileSource.h
deleted file mode 100644
index 4fbdb4b7..00000000
--- a/services/audioflinger/LibsndfileSource.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_LIBSNDFILE_SOURCE_H
-#define ANDROID_AUDIO_LIBSNDFILE_SOURCE_H
-
-#include "NBAIO.h"
-#include "sndfile.h"
-
-// Implementation of NBAIO_Source that wraps a libsndfile opened in SFM_READ mode
-
-namespace android {
-
-class LibsndfileSource : public NBAIO_Source {
-
-public:
- // If 'loop' is true and it permits seeking, then we'll act as an infinite source
- LibsndfileSource(SNDFILE *sndfile, const SF_INFO &sfinfo, bool loop = false);
- virtual ~LibsndfileSource();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Source interface
-
- //virtual size_t framesRead() const;
- //virtual size_t framesOverrun();
- //virtual size_t overruns();
- virtual ssize_t availableToRead();
- virtual ssize_t read(void *buffer, size_t count);
- //virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block);
-
-private:
- SNDFILE * mSndfile;
- sf_count_t mEstimatedFramesUntilEOF;
- bool mLooping;
- bool mReadAnyFramesThisLoopCycle;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_LIBSNDFILE_SOURCE_H
diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp
deleted file mode 100644
index f3fc19a7..00000000
--- a/services/audioflinger/MonoPipe.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "MonoPipe"
-//#define LOG_NDEBUG 0
-
-#include <cutils/atomic.h>
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include <utils/Trace.h>
-#include "MonoPipe.h"
-#include "roundup.h"
-
-namespace android {
-
-MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
- NBAIO_Sink(format),
- mReqFrames(reqFrames),
- mMaxFrames(roundup(reqFrames)),
- mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
- mFront(0),
- mRear(0),
- mWriteTsValid(false),
- // mWriteTs
- mSetpoint((reqFrames * 11) / 16),
- mWriteCanBlock(writeCanBlock)
-{
-}
-
-MonoPipe::~MonoPipe()
-{
- free(mBuffer);
-}
-
-ssize_t MonoPipe::availableToWrite() const
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit
- ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
- ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
- return ret;
-}
-
-ssize_t MonoPipe::write(const void *buffer, size_t count)
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- size_t totalFramesWritten = 0;
- while (count > 0) {
- // can't return a negative value, as we already checked for !mNegotiated
- size_t avail = availableToWrite();
- size_t written = avail;
- if (CC_LIKELY(written > count)) {
- written = count;
- }
- size_t rear = mRear & (mMaxFrames - 1);
- size_t part1 = mMaxFrames - rear;
- if (part1 > written) {
- part1 = written;
- }
- if (CC_LIKELY(part1 > 0)) {
- memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
- if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
- size_t part2 = written - part1;
- if (CC_LIKELY(part2 > 0)) {
- memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
- }
- }
- android_atomic_release_store(written + mRear, &mRear);
- totalFramesWritten += written;
- }
- if (!mWriteCanBlock) {
- break;
- }
- count -= written;
- buffer = (char *) buffer + (written << mBitShift);
- // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
- // The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter.
- uint32_t ns;
- if (written > 0) {
- size_t filled = (mMaxFrames - avail) + written;
- // FIXME cache these values to avoid re-computation
- if (filled <= mSetpoint / 2) {
- // pipe is (nearly) empty, fill quickly
- ns = written * ( 500000000 / Format_sampleRate(mFormat));
- } else if (filled <= (mSetpoint * 3) / 4) {
- // pipe is below setpoint, fill at slightly faster rate
- ns = written * ( 750000000 / Format_sampleRate(mFormat));
- } else if (filled <= (mSetpoint * 5) / 4) {
- // pipe is at setpoint, fill at nominal rate
- ns = written * (1000000000 / Format_sampleRate(mFormat));
- } else if (filled <= (mSetpoint * 3) / 2) {
- // pipe is above setpoint, fill at slightly slower rate
- ns = written * (1150000000 / Format_sampleRate(mFormat));
- } else if (filled <= (mSetpoint * 7) / 4) {
- // pipe is overflowing, fill slowly
- ns = written * (1350000000 / Format_sampleRate(mFormat));
- } else {
- // pipe is severely overflowing
- ns = written * (1750000000 / Format_sampleRate(mFormat));
- }
- } else {
- ns = count * (1350000000 / Format_sampleRate(mFormat));
- }
- if (ns > 999999999) {
- ns = 999999999;
- }
- struct timespec nowTs;
- bool nowTsValid = !clock_gettime(CLOCK_MONOTONIC, &nowTs);
- // deduct the elapsed time since previous write() completed
- if (nowTsValid && mWriteTsValid) {
- time_t sec = nowTs.tv_sec - mWriteTs.tv_sec;
- long nsec = nowTs.tv_nsec - mWriteTs.tv_nsec;
- if (nsec < 0) {
- --sec;
- nsec += 1000000000;
- }
- if (sec == 0) {
- if ((long) ns > nsec) {
- ns -= nsec;
- } else {
- ns = 0;
- }
- }
- }
- if (ns > 0) {
- const struct timespec req = {0, ns};
- nanosleep(&req, NULL);
- }
- // record the time that this write() completed
- if (nowTsValid) {
- mWriteTs = nowTs;
- if ((mWriteTs.tv_nsec += ns) >= 1000000000) {
- mWriteTs.tv_nsec -= 1000000000;
- ++mWriteTs.tv_sec;
- }
- }
- mWriteTsValid = nowTsValid;
- }
- mFramesWritten += totalFramesWritten;
- return totalFramesWritten;
-}
-
-void MonoPipe::setAvgFrames(size_t setpoint)
-{
- mSetpoint = setpoint;
-}
-
-} // namespace android
diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h
deleted file mode 100644
index f6e2cb35..00000000
--- a/services/audioflinger/MonoPipe.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_MONO_PIPE_H
-#define ANDROID_AUDIO_MONO_PIPE_H
-
-#include <time.h>
-#include "NBAIO.h"
-
-namespace android {
-
-// MonoPipe is similar to Pipe except:
-// - supports only a single reader, called MonoPipeReader
-// - write() cannot overrun; instead it will return a short actual count if insufficient space
-// - write() can optionally block if the pipe is full
-// Like Pipe, it is not multi-thread safe for either writer or reader
-// but writer and reader can be different threads.
-class MonoPipe : public NBAIO_Sink {
-
- friend class MonoPipeReader;
-
-public:
- // reqFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
- // Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like
- // creating it the object before creating the other thread, or storing the object with a
- // release_store). Otherwise the other thread could see a partially-constructed object.
- MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false);
- virtual ~MonoPipe();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Sink interface
-
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
-
- virtual ssize_t availableToWrite() const;
- virtual ssize_t write(const void *buffer, size_t count);
- //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block);
-
- // average number of frames present in the pipe under normal conditions.
- // See throttling mechanism in MonoPipe::write()
- size_t getAvgFrames() const { return mSetpoint; }
- void setAvgFrames(size_t setpoint);
- size_t maxFrames() const { return mMaxFrames; }
-
-private:
- const size_t mReqFrames; // as requested in constructor, unrounded
- const size_t mMaxFrames; // always a power of 2
- void * const mBuffer;
- // mFront and mRear will never be separated by more than mMaxFrames.
- // 32-bit overflow is possible if the pipe is active for a long time, but if that happens it's
- // safe because we "&" with (mMaxFrames-1) at end of computations to calculate a buffer index.
- volatile int32_t mFront; // written by reader with android_atomic_release_store,
- // read by writer with android_atomic_acquire_load
- volatile int32_t mRear; // written by writer with android_atomic_release_store,
- // read by reader with android_atomic_acquire_load
- bool mWriteTsValid; // whether mWriteTs is valid
- struct timespec mWriteTs; // time that the previous write() completed
- size_t mSetpoint; // target value for pipe fill depth
- const bool mWriteCanBlock; // whether write() should block if the pipe is full
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_MONO_PIPE_H
diff --git a/services/audioflinger/MonoPipeReader.cpp b/services/audioflinger/MonoPipeReader.cpp
deleted file mode 100644
index b80d0c08..00000000
--- a/services/audioflinger/MonoPipeReader.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "MonoPipeReader"
-//#define LOG_NDEBUG 0
-
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include "MonoPipeReader.h"
-
-namespace android {
-
-MonoPipeReader::MonoPipeReader(MonoPipe* pipe) :
- NBAIO_Source(pipe->mFormat),
- mPipe(pipe)
-{
-}
-
-MonoPipeReader::~MonoPipeReader()
-{
-}
-
-ssize_t MonoPipeReader::availableToRead()
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- ssize_t ret = android_atomic_acquire_load(&mPipe->mRear) - mPipe->mFront;
- ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
- return ret;
-}
-
-ssize_t MonoPipeReader::read(void *buffer, size_t count)
-{
- // count == 0 is unlikely and not worth checking for explicitly; will be handled automatically
- ssize_t red = availableToRead();
- if (CC_UNLIKELY(red <= 0)) {
- return red;
- }
- if (CC_LIKELY((size_t) red > count)) {
- red = count;
- }
- size_t front = mPipe->mFront & (mPipe->mMaxFrames - 1);
- size_t part1 = mPipe->mMaxFrames - front;
- if (part1 > (size_t) red) {
- part1 = red;
- }
- if (CC_LIKELY(part1 > 0)) {
- memcpy(buffer, (char *) mPipe->mBuffer + (front << mBitShift), part1 << mBitShift);
- if (CC_UNLIKELY(front + part1 == mPipe->mMaxFrames)) {
- size_t part2 = red - part1;
- if (CC_LIKELY(part2 > 0)) {
- memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift);
- }
- }
- android_atomic_release_store(red + mPipe->mFront, &mPipe->mFront);
- mFramesRead += red;
- }
- return red;
-}
-
-} // namespace android
diff --git a/services/audioflinger/MonoPipeReader.h b/services/audioflinger/MonoPipeReader.h
deleted file mode 100644
index 9bb0a94f..00000000
--- a/services/audioflinger/MonoPipeReader.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_MONO_PIPE_READER_H
-#define ANDROID_AUDIO_MONO_PIPE_READER_H
-
-#include "MonoPipe.h"
-
-namespace android {
-
-// MonoPipeReader is safe for only a single reader thread
-class MonoPipeReader : public NBAIO_Source {
-
-public:
-
- // Construct a MonoPipeReader and associate it with a MonoPipe;
- // any data already in the pipe is visible to this PipeReader.
- // There can be only a single MonoPipeReader per MonoPipe.
- // FIXME make this constructor a factory method of MonoPipe.
- MonoPipeReader(MonoPipe* pipe);
- virtual ~MonoPipeReader();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Source interface
-
- //virtual size_t framesRead() const;
- //virtual size_t framesOverrun();
- //virtual size_t overruns();
-
- virtual ssize_t availableToRead();
-
- virtual ssize_t read(void *buffer, size_t count);
-
- // NBAIO_Source end
-
-#if 0 // until necessary
- MonoPipe* pipe() const { return mPipe; }
-#endif
-
-private:
- MonoPipe * const mPipe;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_MONO_PIPE_READER_H
diff --git a/services/audioflinger/NBAIO.cpp b/services/audioflinger/NBAIO.cpp
deleted file mode 100644
index 9d71eae5..00000000
--- a/services/audioflinger/NBAIO.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "NBAIO"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include "NBAIO.h"
-
-namespace android {
-
-size_t Format_frameSize(NBAIO_Format format)
-{
- switch (format) {
- case Format_SR44_1_C2_I16:
- case Format_SR48_C2_I16:
- return 2 * sizeof(short);
- case Format_SR44_1_C1_I16:
- case Format_SR48_C1_I16:
- return 1 * sizeof(short);
- case Format_Invalid:
- default:
- return 0;
- }
-}
-
-size_t Format_frameBitShift(NBAIO_Format format)
-{
- switch (format) {
- case Format_SR44_1_C2_I16:
- case Format_SR48_C2_I16:
- return 2; // 1 << 2 == 2 * sizeof(short)
- case Format_SR44_1_C1_I16:
- case Format_SR48_C1_I16:
- return 1; // 1 << 1 == 1 * sizeof(short)
- case Format_Invalid:
- default:
- return 0;
- }
-}
-
-unsigned Format_sampleRate(NBAIO_Format format)
-{
- switch (format) {
- case Format_SR44_1_C1_I16:
- case Format_SR44_1_C2_I16:
- return 44100;
- case Format_SR48_C1_I16:
- case Format_SR48_C2_I16:
- return 48000;
- case Format_Invalid:
- default:
- return 0;
- }
-}
-
-unsigned Format_channelCount(NBAIO_Format format)
-{
- switch (format) {
- case Format_SR44_1_C1_I16:
- case Format_SR48_C1_I16:
- return 1;
- case Format_SR44_1_C2_I16:
- case Format_SR48_C2_I16:
- return 2;
- case Format_Invalid:
- default:
- return 0;
- }
-}
-
-NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount)
-{
- if (sampleRate == 44100 && channelCount == 2) return Format_SR44_1_C2_I16;
- if (sampleRate == 48000 && channelCount == 2) return Format_SR48_C2_I16;
- if (sampleRate == 44100 && channelCount == 1) return Format_SR44_1_C1_I16;
- if (sampleRate == 48000 && channelCount == 1) return Format_SR48_C1_I16;
- return Format_Invalid;
-}
-
-// This is a default implementation; it is expected that subclasses will optimize this.
-ssize_t NBAIO_Sink::writeVia(writeVia_t via, size_t total, void *user, size_t block)
-{
- if (!mNegotiated) {
- return (ssize_t) NEGOTIATE;
- }
- static const size_t maxBlock = 32;
- size_t frameSize = Format_frameSize(mFormat);
- ALOG_ASSERT(frameSize > 0 && frameSize <= 8);
- // double guarantees alignment for stack similar to what malloc() gives for heap
- if (block == 0 || block > maxBlock) {
- block = maxBlock;
- }
- double buffer[((frameSize * block) + sizeof(double) - 1) / sizeof(double)];
- size_t accumulator = 0;
- while (accumulator < total) {
- size_t count = total - accumulator;
- if (count > block) {
- count = block;
- }
- ssize_t ret = via(user, buffer, count);
- if (ret > 0) {
- ALOG_ASSERT((size_t) ret <= count);
- size_t maxRet = ret;
- ret = write(buffer, maxRet);
- if (ret > 0) {
- ALOG_ASSERT((size_t) ret <= maxRet);
- accumulator += ret;
- continue;
- }
- }
- return accumulator > 0 ? accumulator : ret;
- }
- return accumulator;
-}
-
-// This is a default implementation; it is expected that subclasses will optimize this.
-ssize_t NBAIO_Source::readVia(readVia_t via, size_t total, void *user, size_t block)
-{
- if (!mNegotiated) {
- return (ssize_t) NEGOTIATE;
- }
- static const size_t maxBlock = 32;
- size_t frameSize = Format_frameSize(mFormat);
- ALOG_ASSERT(frameSize > 0 && frameSize <= 8);
- // double guarantees alignment for stack similar to what malloc() gives for heap
- if (block == 0 || block > maxBlock) {
- block = maxBlock;
- }
- double buffer[((frameSize * block) + sizeof(double) - 1) / sizeof(double)];
- size_t accumulator = 0;
- while (accumulator < total) {
- size_t count = total - accumulator;
- if (count > block) {
- count = block;
- }
- ssize_t ret = read(buffer, count);
- if (ret > 0) {
- ALOG_ASSERT((size_t) ret <= count);
- size_t maxRet = ret;
- ret = via(user, buffer, maxRet);
- if (ret > 0) {
- ALOG_ASSERT((size_t) ret <= maxRet);
- accumulator += ret;
- continue;
- }
- }
- return accumulator > 0 ? accumulator : ret;
- }
- return accumulator;
-}
-
-// Default implementation that only accepts my mFormat
-ssize_t NBAIO_Port::negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers)
-{
- ALOGV("negotiate offers=%p numOffers=%u countersOffers=%p numCounterOffers=%u",
- offers, numOffers, counterOffers, numCounterOffers);
- if (mFormat != Format_Invalid) {
- for (size_t i = 0; i < numOffers; ++i) {
- if (offers[i] == mFormat) {
- mNegotiated = true;
- return i;
- }
- }
- if (numCounterOffers > 0) {
- counterOffers[0] = mFormat;
- }
- numCounterOffers = 1;
- } else {
- numCounterOffers = 0;
- }
- return (ssize_t) NEGOTIATE;
-}
-
-} // namespace android
diff --git a/services/audioflinger/NBAIO.h b/services/audioflinger/NBAIO.h
deleted file mode 100644
index b5ae0f10..00000000
--- a/services/audioflinger/NBAIO.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_NBAIO_H
-#define ANDROID_AUDIO_NBAIO_H
-
-// Non-blocking audio I/O interface
-//
-// This header file has the abstract interfaces only. Concrete implementation classes are declared
-// elsewhere. Implementations _should_ be non-blocking for all methods, especially read() and
-// write(), but this is not enforced. In general, implementations do not need to be multi-thread
-// safe, and any exceptions are noted in the particular implementation.
-
-#include <limits.h>
-#include <stdlib.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-// In addition to the usual status_t
-enum {
- NEGOTIATE = 0x80000010, // Must (re-)negotiate format. For negotiate() only, the offeree
- // doesn't accept offers, and proposes counter-offers
- OVERRUN = 0x80000011, // availableToRead(), read(), or readVia() detected lost input due
- // to overrun; an event is counted and the caller should re-try
- UNDERRUN = 0x80000012, // availableToWrite(), write(), or writeVia() detected a gap in
- // output due to underrun (not being called often enough, or with
- // enough data); an event is counted and the caller should re-try
-};
-
-// Negotiation of format is based on the data provider and data sink, or the data consumer and
-// data source, exchanging prioritized arrays of offers and counter-offers until a single offer is
-// mutually agreed upon. Each offer is an NBAIO_Format. For simplicity and performance,
-// NBAIO_Format is an enum that ties together the most important combinations of the various
-// attributes, rather than a struct with separate fields for format, sample rate, channel count,
-// interleave, packing, alignment, etc. The reason is that NBAIO_Format tries to abstract out only
-// the combinations that are actually needed within AudioFligner. If the list of combinations grows
-// too large, then this decision should be re-visited.
-enum NBAIO_Format {
- Format_Invalid,
- Format_SR44_1_C2_I16, // 44.1 kHz PCM stereo interleaved 16-bit signed
- Format_SR48_C2_I16, // 48 kHz PCM stereo interleaved 16-bit signed
- Format_SR44_1_C1_I16, // 44.1 kHz PCM mono interleaved 16-bit signed
- Format_SR48_C1_I16, // 48 kHz PCM mono interleaved 16-bit signed
-};
-
-// Return the frame size of an NBAIO_Format in bytes
-size_t Format_frameSize(NBAIO_Format format);
-
-// Return the frame size of an NBAIO_Format as a bit shift
-size_t Format_frameBitShift(NBAIO_Format format);
-
-// Convert a sample rate in Hz and channel count to an NBAIO_Format
-NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount);
-
-// Return the sample rate in Hz of an NBAIO_Format
-unsigned Format_sampleRate(NBAIO_Format format);
-
-// Return the channel count of an NBAIO_Format
-unsigned Format_channelCount(NBAIO_Format format);
-
-// Callbacks used by NBAIO_Sink::writeVia() and NBAIO_Source::readVia() below.
-typedef ssize_t (*writeVia_t)(void *user, void *buffer, size_t count);
-typedef ssize_t (*readVia_t)(void *user, const void *buffer, size_t count);
-
-// Abstract class (interface) representing a data port.
-class NBAIO_Port : public RefBase {
-
-public:
-
- // negotiate() must called first. The purpose of negotiate() is to check compatibility of
- // formats, not to automatically adapt if they are incompatible. It's the responsibility of
- // whoever sets up the graph connections to make sure formats are compatible, and this method
- // just verifies that. The edges are "dumb" and don't attempt to adapt to bad connections.
- // How it works: offerer proposes an array of formats, in descending order of preference from
- // offers[0] to offers[numOffers - 1]. If offeree accepts one of these formats, it returns
- // the index of that offer. Otherwise, offeree sets numCounterOffers to the number of
- // counter-offers (up to a maximumum of the entry value of numCounterOffers), fills in the
- // provided array counterOffers[] with its counter-offers, in descending order of preference
- // from counterOffers[0] to counterOffers[numCounterOffers - 1], and returns NEGOTIATE.
- // Note that since the offerer allocates space for counter-offers, but only the offeree knows
- // how many counter-offers it has, there may be insufficient space for all counter-offers.
- // In that case, the offeree sets numCounterOffers to the requested number of counter-offers
- // (which is greater than the entry value of numCounterOffers), fills in as many of the most
- // important counterOffers as will fit, and returns NEGOTIATE. As this implies a re-allocation,
- // it should be used as a last resort. It is preferable for the offerer to simply allocate a
- // larger space to begin with, and/or for the offeree to tolerate a smaller space than desired.
- // Alternatively, the offerer can pass NULL for offers and counterOffers, and zero for
- // numOffers. This indicates that it has not allocated space for any counter-offers yet.
- // In this case, the offerree should set numCounterOffers appropriately and return NEGOTIATE.
- // Then the offerer will allocate the correct amount of memory and retry.
- // Format_Invalid is not allowed as either an offer or counter-offer.
- // Returns:
- // >= 0 Offer accepted.
- // NEGOTIATE No offer accepted, and counter-offer(s) optionally made. See above for details.
- virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- NBAIO_Format counterOffers[], size_t& numCounterOffers);
-
- // Return the current negotiated format, or Format_Invalid if negotiation has not been done,
- // or if re-negotiation is required.
- virtual NBAIO_Format format() const { return mNegotiated ? mFormat : Format_Invalid; }
-
-protected:
- NBAIO_Port(NBAIO_Format format) : mNegotiated(false), mFormat(format),
- mBitShift(Format_frameBitShift(format)) { }
- virtual ~NBAIO_Port() { }
-
- // Implementations are free to ignore these if they don't need them
-
- bool mNegotiated; // mNegotiated implies (mFormat != Format_Invalid)
- NBAIO_Format mFormat; // (mFormat != Format_Invalid) does not imply mNegotiated
- size_t mBitShift; // assign in parallel with any assignment to mFormat
-};
-
-// Abstract class (interface) representing a non-blocking data sink, for use by a data provider.
-class NBAIO_Sink : public NBAIO_Port {
-
-public:
-
- // For the next two APIs:
- // 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically.
-
- // Return the number of frames written successfully since construction.
- virtual size_t framesWritten() const { return mFramesWritten; }
-
- // Number of frames lost due to underrun since construction.
- virtual size_t framesUnderrun() const { return 0; }
-
- // Number of underruns since construction, where a set of contiguous lost frames is one event.
- virtual size_t underruns() const { return 0; }
-
- // Estimate of number of frames that could be written successfully now without blocking.
- // When a write() is actually attempted, the implementation is permitted to return a smaller or
- // larger transfer count, however it will make a good faith effort to give an accurate estimate.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up.
- // An underrun event is counted, and the caller should re-try this operation.
- // WOULD_BLOCK Determining how many frames can be written without blocking would itself block.
- virtual ssize_t availableToWrite() const { return SSIZE_MAX; }
-
- // Transfer data to sink from single input buffer. Implies a copy.
- // Inputs:
- // buffer Non-NULL buffer owned by provider.
- // count Maximum number of frames to transfer.
- // Return value:
- // > 0 Number of frames successfully transferred prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // WOULD_BLOCK No frames can be transferred without blocking.
- // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up.
- // An underrun event is counted, and the caller should re-try this operation.
- virtual ssize_t write(const void *buffer, size_t count) = 0;
-
- // Transfer data to sink using a series of callbacks. More suitable for zero-fill, synthesis,
- // and non-contiguous transfers (e.g. circular buffer or writev).
- // Inputs:
- // via Callback function that the sink will call as many times as needed to consume data.
- // total Estimate of the number of frames the provider has available. This is an estimate,
- // and it can provide a different number of frames during the series of callbacks.
- // user Arbitrary void * reserved for data provider.
- // block Number of frames per block, that is a suggested value for 'count' in each callback.
- // Zero means no preference. This parameter is a hint only, and may be ignored.
- // Return value:
- // > 0 Total number of frames successfully transferred prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // WOULD_BLOCK No frames can be transferred without blocking.
- // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up.
- // An underrun event is counted, and the caller should re-try this operation.
- //
- // The 'via' callback is called by the data sink as follows:
- // Inputs:
- // user Arbitrary void * reserved for data provider.
- // buffer Non-NULL buffer owned by sink that callback should fill in with data,
- // up to a maximum of 'count' frames.
- // count Maximum number of frames to transfer during this callback.
- // Return value:
- // > 0 Number of frames successfully transferred during this callback prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer during this callback.
- virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block = 0);
-
-protected:
- NBAIO_Sink(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { }
- virtual ~NBAIO_Sink() { }
-
- // Implementations are free to ignore these if they don't need them
- size_t mFramesWritten;
-};
-
-// Abstract class (interface) representing a non-blocking data source, for use by a data consumer.
-class NBAIO_Source : public NBAIO_Port {
-
-public:
-
- // For the next two APIs:
- // 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically.
-
- // Number of frames read successfully since construction.
- virtual size_t framesRead() const { return mFramesRead; }
-
- // Number of frames lost due to overrun since construction.
- // Not const because implementations may need to do I/O.
- virtual size_t framesOverrun() /*const*/ { return 0; }
-
- // Number of overruns since construction, where a set of contiguous lost frames is one event.
- // Not const because implementations may need to do I/O.
- virtual size_t overruns() /*const*/ { return 0; }
-
- // Estimate of number of frames that could be read successfully now.
- // When a read() is actually attempted, the implementation is permitted to return a smaller or
- // larger transfer count, however it will make a good faith effort to give an accurate estimate.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // OVERRUN One or more frames were lost due to overrun, try again to read more recent data.
- // WOULD_BLOCK Determining how many frames can be read without blocking would itself block.
- virtual ssize_t availableToRead() { return SSIZE_MAX; }
-
- // Transfer data from source into single destination buffer. Implies a copy.
- // Inputs:
- // buffer Non-NULL destination buffer owned by consumer.
- // count Maximum number of frames to transfer.
- // Return value:
- // > 0 Number of frames successfully transferred prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // WOULD_BLOCK No frames can be transferred without blocking.
- // OVERRUN read() has not been called frequently enough, or with enough frames to keep up.
- // One or more frames were lost due to overrun, try again to read more recent data.
- virtual ssize_t read(void *buffer, size_t count) = 0;
-
- // Transfer data from source using a series of callbacks. More suitable for zero-fill,
- // synthesis, and non-contiguous transfers (e.g. circular buffer or readv).
- // Inputs:
- // via Callback function that the source will call as many times as needed to provide data.
- // total Estimate of the number of frames the consumer desires. This is an estimate,
- // and it can consume a different number of frames during the series of callbacks.
- // user Arbitrary void * reserved for data consumer.
- // block Number of frames per block, that is a suggested value for 'count' in each callback.
- // Zero means no preference. This parameter is a hint only, and may be ignored.
- // Return value:
- // > 0 Total number of frames successfully transferred prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer.
- // Errors:
- // NEGOTIATE (Re-)negotiation is needed.
- // WOULD_BLOCK No frames can be transferred without blocking.
- // OVERRUN read() has not been called frequently enough, or with enough frames to keep up.
- // One or more frames were lost due to overrun, try again to read more recent data.
- //
- // The 'via' callback is called by the data source as follows:
- // Inputs:
- // user Arbitrary void * reserved for data consumer.
- // dest Non-NULL buffer owned by source that callback should consume data from,
- // up to a maximum of 'count' frames.
- // count Maximum number of frames to transfer during this callback.
- // Return value:
- // > 0 Number of frames successfully transferred during this callback prior to first error.
- // = 0 Count was zero.
- // < 0 status_t error occurred prior to the first frame transfer during this callback.
- virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block = 0);
-
-protected:
- NBAIO_Source(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { }
- virtual ~NBAIO_Source() { }
-
- // Implementations are free to ignore these if they don't need them
- size_t mFramesRead;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_NBAIO_H
diff --git a/services/audioflinger/Pipe.cpp b/services/audioflinger/Pipe.cpp
deleted file mode 100644
index e5b35610..00000000
--- a/services/audioflinger/Pipe.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "Pipe"
-//#define LOG_NDEBUG 0
-
-#include <cutils/atomic.h>
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include "Pipe.h"
-#include "roundup.h"
-
-namespace android {
-
-Pipe::Pipe(size_t maxFrames, NBAIO_Format format) :
- NBAIO_Sink(format),
- mMaxFrames(roundup(maxFrames)),
- mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
- mRear(0),
- mReaders(0)
-{
-}
-
-Pipe::~Pipe()
-{
- ALOG_ASSERT(android_atomic_acquire_load(&mReaders) == 0);
- free(mBuffer);
-}
-
-ssize_t Pipe::write(const void *buffer, size_t count)
-{
- // count == 0 is unlikely and not worth checking for
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- // write() is not multi-thread safe w.r.t. itself, so no mutex or atomic op needed to read mRear
- size_t rear = mRear & (mMaxFrames - 1);
- size_t written = mMaxFrames - rear;
- if (CC_LIKELY(written > count)) {
- written = count;
- }
- memcpy((char *) mBuffer + (rear << mBitShift), buffer, written << mBitShift);
- if (CC_UNLIKELY(rear + written == mMaxFrames)) {
- if (CC_UNLIKELY((count -= written) > rear)) {
- count = rear;
- }
- if (CC_LIKELY(count > 0)) {
- memcpy(mBuffer, (char *) buffer + (written << mBitShift), count << mBitShift);
- written += count;
- }
- }
- android_atomic_release_store(written + mRear, &mRear);
- mFramesWritten += written;
- return written;
-}
-
-} // namespace android
diff --git a/services/audioflinger/Pipe.h b/services/audioflinger/Pipe.h
deleted file mode 100644
index 79a4eee3..00000000
--- a/services/audioflinger/Pipe.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_PIPE_H
-#define ANDROID_AUDIO_PIPE_H
-
-#include "NBAIO.h"
-
-namespace android {
-
-// Pipe is multi-thread safe for readers (see PipeReader), but safe for only a single writer thread.
-// It cannot UNDERRUN on write, unless we allow designation of a master reader that provides the
-// time-base. Readers can be added and removed dynamically, and it's OK to have no readers.
-class Pipe : public NBAIO_Sink {
-
- friend class PipeReader;
-
-public:
- // maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2.
- Pipe(size_t maxFrames, NBAIO_Format format);
- virtual ~Pipe();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Sink interface
-
- //virtual size_t framesWritten() const;
- //virtual size_t framesUnderrun() const;
- //virtual size_t underruns() const;
-
- // The write side of a pipe permits overruns; flow control is the caller's responsibility.
- // It doesn't return +infinity because that would guarantee an overrun.
- virtual ssize_t availableToWrite() const { return mMaxFrames; }
-
- virtual ssize_t write(const void *buffer, size_t count);
- //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block);
-
-private:
- const size_t mMaxFrames; // always a power of 2
- void * const mBuffer;
- volatile int32_t mRear; // written by android_atomic_release_store
- volatile int32_t mReaders; // number of PipeReader clients currently attached to this Pipe
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_PIPE_H
diff --git a/services/audioflinger/PipeReader.cpp b/services/audioflinger/PipeReader.cpp
deleted file mode 100644
index df3ee04f..00000000
--- a/services/audioflinger/PipeReader.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "PipeReader"
-//#define LOG_NDEBUG 0
-
-#include <cutils/compiler.h>
-#include <utils/Log.h>
-#include "PipeReader.h"
-
-namespace android {
-
-PipeReader::PipeReader(Pipe& pipe) :
- NBAIO_Source(pipe.mFormat),
- mPipe(pipe),
- // any data already in the pipe is not visible to this PipeReader
- mFront(android_atomic_acquire_load(&pipe.mRear)),
- mFramesOverrun(0),
- mOverruns(0)
-{
- android_atomic_inc(&pipe.mReaders);
-}
-
-PipeReader::~PipeReader()
-{
- int32_t readers = android_atomic_dec(&mPipe.mReaders);
- ALOG_ASSERT(readers > 0);
-}
-
-ssize_t PipeReader::availableToRead()
-{
- if (CC_UNLIKELY(!mNegotiated)) {
- return NEGOTIATE;
- }
- int32_t rear = android_atomic_acquire_load(&mPipe.mRear);
- // read() is not multi-thread safe w.r.t. itself, so no mutex or atomic op needed to read mFront
- size_t avail = rear - mFront;
- if (CC_UNLIKELY(avail > mPipe.mMaxFrames)) {
- // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately
- int32_t oldFront = mFront;
- mFront = rear - mPipe.mMaxFrames + (mPipe.mMaxFrames >> 4);
- mFramesOverrun += (size_t) (mFront - oldFront);
- ++mOverruns;
- return OVERRUN;
- }
- return avail;
-}
-
-ssize_t PipeReader::read(void *buffer, size_t count)
-{
- ssize_t avail = availableToRead();
- if (CC_UNLIKELY(avail <= 0)) {
- return avail;
- }
- // An overrun can occur from here on and be silently ignored,
- // but it will be caught at next read()
- if (CC_LIKELY(count > (size_t) avail)) {
- count = avail;
- }
- size_t front = mFront & (mPipe.mMaxFrames - 1);
- size_t red = mPipe.mMaxFrames - front;
- if (CC_LIKELY(red > count)) {
- red = count;
- }
- // In particular, an overrun during the memcpy will result in reading corrupt data
- memcpy(buffer, (char *) mPipe.mBuffer + (front << mBitShift), red << mBitShift);
- // We could re-read the rear pointer here to detect the corruption, but why bother?
- if (CC_UNLIKELY(front + red == mPipe.mMaxFrames)) {
- if (CC_UNLIKELY((count -= red) > front)) {
- count = front;
- }
- if (CC_LIKELY(count > 0)) {
- memcpy((char *) buffer + (red << mBitShift), mPipe.mBuffer, count << mBitShift);
- red += count;
- }
- }
- mFront += red;
- mFramesRead += red;
- return red;
-}
-
-} // namespace android
diff --git a/services/audioflinger/PipeReader.h b/services/audioflinger/PipeReader.h
deleted file mode 100644
index 398353b0..00000000
--- a/services/audioflinger/PipeReader.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 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 ANDROID_AUDIO_PIPE_READER_H
-#define ANDROID_AUDIO_PIPE_READER_H
-
-#include "Pipe.h"
-
-namespace android {
-
-// PipeReader is safe for only a single thread
-class PipeReader : public NBAIO_Source {
-
-public:
-
- // Construct a PipeReader and associate it with a Pipe
- // FIXME make this constructor a factory method of Pipe.
- PipeReader(Pipe& pipe);
- virtual ~PipeReader();
-
- // NBAIO_Port interface
-
- //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
- // NBAIO_Format counterOffers[], size_t& numCounterOffers);
- //virtual NBAIO_Format format() const;
-
- // NBAIO_Source interface
-
- //virtual size_t framesRead() const;
- virtual size_t framesOverrun() { return mFramesOverrun; }
- virtual size_t overruns() { return mOverruns; }
-
- virtual ssize_t availableToRead();
-
- virtual ssize_t read(void *buffer, size_t count);
-
- // NBAIO_Source end
-
-#if 0 // until necessary
- Pipe& pipe() const { return mPipe; }
-#endif
-
-private:
- Pipe& mPipe;
- int32_t mFront; // follows behind mPipe.mRear
- size_t mFramesOverrun;
- size_t mOverruns;
-};
-
-} // namespace android
-
-#endif // ANDROID_AUDIO_PIPE_READER_H
diff --git a/services/audioflinger/Soaker.h b/services/audioflinger/Soaker.h
deleted file mode 100644
index 43d9d2f3..00000000
--- a/services/audioflinger/Soaker.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 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 _ANDROID_AUDIO_SOAKER_H
-#define _ANDROID_AUDIO_SOAKER_H
-
-#include <utils/Thread.h>
-
-namespace android {
-
-class Soaker : public Thread {
-public:
- Soaker() : Thread() { }
- virtual ~Soaker() { }
-protected:
- virtual bool threadLoop() {
- int j = 0;
- for (;;) {
- for (int i = 0; i < 10000; ++i) {
- j += i * i;
- }
- if (exitPending()) {
- return false;
- }
- }
- return j < 555555;
- }
-};
-
-} // namespace android
-
-#endif // _ANDROID_AUDIO_SOAKER_H
diff --git a/services/audioflinger/SourceAudioBufferProvider.cpp b/services/audioflinger/SourceAudioBufferProvider.cpp
deleted file mode 100644
index e9d6d2c7..00000000
--- a/services/audioflinger/SourceAudioBufferProvider.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define LOG_TAG "SourceAudioBufferProvider"
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include "SourceAudioBufferProvider.h"
-
-namespace android {
-
-SourceAudioBufferProvider::SourceAudioBufferProvider(const sp<NBAIO_Source>& source) :
- mSource(source),
- // mFrameBitShiftFormat below
- mAllocated(NULL), mSize(0), mOffset(0), mRemaining(0), mGetCount(0)
-{
- ALOG_ASSERT(source != 0);
-
- // negotiate with source
- NBAIO_Format counterOffers[1];
- size_t numCounterOffers = 1;
- ssize_t index = source->negotiate(NULL, 0, counterOffers, numCounterOffers);
- ALOG_ASSERT(index == (ssize_t) NEGOTIATE && numCounterOffers > 0);
- numCounterOffers = 0;
- index = source->negotiate(counterOffers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mFrameBitShift = Format_frameBitShift(source->format());
-}
-
-SourceAudioBufferProvider::~SourceAudioBufferProvider()
-{
- free(mAllocated);
-}
-
-status_t SourceAudioBufferProvider::getNextBuffer(Buffer *buffer, int64_t pts)
-{
- ALOG_ASSERT(buffer != NULL && buffer->frameCount > 0 && mGetCount == 0);
- // any leftover data available?
- if (mRemaining > 0) {
- ALOG_ASSERT(mOffset + mRemaining <= mSize);
- if (mRemaining < buffer->frameCount) {
- buffer->frameCount = mRemaining;
- }
- buffer->raw = (char *) mAllocated + (mOffset << mFrameBitShift);
- mGetCount = buffer->frameCount;
- return OK;
- }
- // do we need to reallocate?
- if (buffer->frameCount > mSize) {
- free(mAllocated);
- mAllocated = malloc(buffer->frameCount << mFrameBitShift);
- mSize = buffer->frameCount;
- }
- // read from source
- ssize_t actual = mSource->read(mAllocated, buffer->frameCount);
- if (actual > 0) {
- ALOG_ASSERT((size_t) actual <= buffer->frameCount);
- mOffset = 0;
- mRemaining = actual;
- buffer->raw = mAllocated;
- buffer->frameCount = actual;
- mGetCount = actual;
- return OK;
- }
- buffer->raw = NULL;
- buffer->frameCount = 0;
- mGetCount = 0;
- return NOT_ENOUGH_DATA;
-}
-
-void SourceAudioBufferProvider::releaseBuffer(Buffer *buffer)
-{
- ALOG_ASSERT((buffer != NULL) &&
- (buffer->raw == (char *) mAllocated + (mOffset << mFrameBitShift)) &&
- (buffer->frameCount <= mGetCount) &&
- (mGetCount <= mRemaining) &&
- (mOffset + mRemaining <= mSize));
- mOffset += buffer->frameCount;
- mRemaining -= buffer->frameCount;
- buffer->raw = NULL;
- buffer->frameCount = 0;
- mGetCount = 0;
-}
-
-size_t SourceAudioBufferProvider::framesReady() const
-{
- ssize_t avail = mSource->availableToRead();
- return avail < 0 ? 0 : (size_t) avail;
-}
-
-} // namespace android
diff --git a/services/audioflinger/SourceAudioBufferProvider.h b/services/audioflinger/SourceAudioBufferProvider.h
deleted file mode 100644
index 85ccbb2e..00000000
--- a/services/audioflinger/SourceAudioBufferProvider.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// Implementation of AudioBufferProvider that wraps an NBAIO_Source
-
-#ifndef ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H
-#define ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H
-
-#include "NBAIO.h"
-#include "ExtendedAudioBufferProvider.h"
-
-namespace android {
-
-class SourceAudioBufferProvider : public ExtendedAudioBufferProvider {
-
-public:
- SourceAudioBufferProvider(const sp<NBAIO_Source>& source);
- virtual ~SourceAudioBufferProvider();
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(Buffer *buffer, int64_t pts);
- virtual void releaseBuffer(Buffer *buffer);
-
- // ExtendedAudioBufferProvider interface
- virtual size_t framesReady() const;
-
-private:
- const sp<NBAIO_Source> mSource; // the wrapped source
- /*const*/ size_t mFrameBitShift; // log2(frame size in bytes)
- void* mAllocated; // pointer to base of allocated memory
- size_t mSize; // size of mAllocated in frames
- size_t mOffset; // frame offset within mAllocated of valid data
- size_t mRemaining; // frame count within mAllocated of valid data
- size_t mGetCount; // buffer.frameCount of the most recent getNextBuffer
-};
-
-} // namespace android
-
-#endif // ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H
diff --git a/services/audioflinger/audio-resampler/Android.mk b/services/audioflinger/audio-resampler/Android.mk
new file mode 100644
index 00000000..ba37b198
--- /dev/null
+++ b/services/audioflinger/audio-resampler/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AudioResamplerCoefficients.cpp
+
+LOCAL_MODULE := libaudio-resampler
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libutils liblog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/roundup.h b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp
index 4c3cc257..d45d6972 100644
--- a/services/audioflinger/roundup.h
+++ b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp
@@ -14,18 +14,41 @@
* limitations under the License.
*/
-#ifndef ROUNDUP_H
-#define ROUNDUP_H
+#define LOG_TAG "ResamplerCoefficients"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include "filter_coefficients.h"
+
+const int32_t RESAMPLE_FIR_NUM_COEF = 16;
+const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7;
+
+using namespace android;
#ifdef __cplusplus
extern "C" {
#endif
-// Round up to the next highest power of 2
-unsigned roundup(unsigned v);
+const int32_t* readResamplerCoefficients(bool upSample) {
+
+ ALOGV("readResamplerCoefficients");
+ if (upSample) {
+ return up_sampler_filter_coefficients;
+ } else {
+ return dn_sampler_filter_coefficients;
+ }
+
+}
+
+int32_t readResampleFirNumCoeff() {
+ return RESAMPLE_FIR_NUM_COEF;
+}
+
+int32_t readResampleFirLerpIntBits() {
+ return RESAMPLE_FIR_LERP_INT_BITS;
+}
#ifdef __cplusplus
}
#endif
-
-#endif // ROUNDUP_H
diff --git a/services/audioflinger/audio-resampler/filter_coefficients.h b/services/audioflinger/audio-resampler/filter_coefficients.h
new file mode 100644
index 00000000..bf70c632
--- /dev/null
+++ b/services/audioflinger/audio-resampler/filter_coefficients.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 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 <stdlib.h>
+
+namespace android {
+
+// cmd-line: fir -l 7 -s 48000 -c 23400 -n 16 -b 9.62
+const int32_t up_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = {
+ 0x7ccccccd, 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5,
+ 0x7cc9b757, 0x022ac835, 0xfd7e3a71, 0x026b7da1, 0xfdd2b905, 0x01db7c90, 0xfe7db77c, 0x012aa7bf, 0xff24dc32, 0x0097dfc9, 0xff9d4ae9, 0x003b8742, 0xffdf38e5, 0x00100be5, 0xfff959f5, 0x0002144b,
+ 0x7cc0773c, 0x01354bc1, 0xfdf365e8, 0x0224726d, 0xfe011d2e, 0x01bc908b, 0xfe923a2b, 0x011d528d, 0xff2d426f, 0x0092cbc0, 0xffa035cc, 0x0039f42e, 0xffe00236, 0x000fb0d2, 0xfff97dfa, 0x000208b0,
+ 0x7cb10d52, 0x0043843f, 0xfe67d5a8, 0x01dd92df, 0xfe2f83c1, 0x019d9230, 0xfea6d2e5, 0x010fe901, 0xff35b924, 0x008dab9d, 0xffa328d4, 0x00385c1d, 0xffe0ce46, 0x000f5471, 0xfff9a27f, 0x0001fcf5,
+ 0x7c9b7afd, 0xff557f58, 0xfedb7ae9, 0x0196e8fe, 0xfe5de5e3, 0x017e8635, 0xfebb7e75, 0x01026d40, 0xff3e3eed, 0x0088803e, 0xffa6237a, 0x0036bf58, 0xffe19cec, 0x000ef6d4, 0xfff9c77d, 0x0001f11e,
+ 0x7c7fc22f, 0xfe6b4a44, 0xff4e471d, 0x01507eb8, 0xfe8c3cc3, 0x015f714d, 0xfed039a8, 0x00f4e16f, 0xff46d266, 0x00834a83, 0xffa9253b, 0x00351e2d, 0xffe26e01, 0x000e980f, 0xfff9eceb, 0x0001e52e,
+ 0x7c5de56a, 0xfd84f1c8, 0xffc02bf2, 0x010a5de2, 0xfeba819d, 0x01405821, 0xfee5014c, 0x00e747b0, 0xff4f722b, 0x007e0b4b, 0xffac2d8f, 0x003378e7, 0xffe3415d, 0x000e3834, 0xfffa12c0, 0x0001d927,
+ 0x7c35e7bb, 0xfca28234, 0x00311b54, 0x00c49034, 0xfee8adba, 0x01213f58, 0xfef9d232, 0x00d9a226, 0xff581cd8, 0x0078c375, 0xffaf3bf2, 0x0031cfd1, 0xffe416d8, 0x000dd758, 0xfffa38f5, 0x0001cd0d,
+ 0x7c07ccbe, 0xfbc40766, 0x00a1076e, 0x007f1f4b, 0xff16ba71, 0x01022b90, 0xff0ea931, 0x00cbf2f0, 0xff60d10b, 0x007373de, 0xffb24fde, 0x00302337, 0xffe4ee4b, 0x000d758d, 0xfffa5f81, 0x0001c0e1,
+ 0x7bd3989d, 0xfae98cc5, 0x010fe2ab, 0x003a14a6, 0xff44a128, 0x00e3215e, 0xff238322, 0x00be3c2d, 0xff698d62, 0x006e1d66, 0xffb568ce, 0x002e7363, 0xffe5c78d, 0x000d12e6, 0xfffa865d, 0x0001b4a8,
+ 0x7b99500c, 0xfa131d41, 0x017d9fb8, 0xfff579a3, 0xff725b54, 0x00c42551, 0xff385ce3, 0x00b07ff8, 0xff72507e, 0x0068c0e9, 0xffb8863e, 0x002cc0a2, 0xffe6a277, 0x000caf76, 0xfffaad81, 0x0001a863,
+ 0x7b58f84d, 0xf940c355, 0x01ea3184, 0xffb15783, 0xff9fe27d, 0x00a53bed, 0xff4d3358, 0x00a2c06b, 0xff7b18fe, 0x00635f45, 0xffbba7aa, 0x002b0b3d, 0xffe77ee2, 0x000c4b50, 0xfffad4e4, 0x00019c15,
+ 0x7b12972d, 0xf8728902, 0x02558b43, 0xff6db764, 0xffcd303b, 0x008669ae, 0xff620368, 0x0094ff9b, 0xff83e586, 0x005df954, 0xffbecc8d, 0x00295380, 0xffe85ca7, 0x000be687, 0xfffafc7f, 0x00018fc1,
+ 0x7ac63304, 0xf7a877d4, 0x02bfa06d, 0xff2aa243, 0xfffa3e37, 0x0067b303, 0xff76ca02, 0x00873f9b, 0xff8cb4bb, 0x00588ff1, 0xffc1f465, 0x002799b3, 0xffe93b9e, 0x000b812d, 0xfffb244a, 0x0001836a,
+ 0x7a73d2b5, 0xf6e298db, 0x032864c1, 0xfee820f8, 0x00270631, 0x00491c54, 0xff8b841a, 0x0079827a, 0xff958542, 0x005323f7, 0xffc51eaf, 0x0025de22, 0xffea1ba2, 0x000b1b55, 0xfffb4c3e, 0x00017712,
+ 0x7a1b7daa, 0xf620f4b2, 0x038fcc44, 0xfea63c38, 0x005381fa, 0x002aa9fa, 0xffa02eac, 0x006bca44, 0xff9e55c6, 0x004db63c, 0xffc84ae9, 0x00242115, 0xffeafc8b, 0x000ab510, 0xfffb7452, 0x00016abb,
+ 0x79bd3bd8, 0xf5639376, 0x03f5cb46, 0xfe64fc93, 0x007fab77, 0x000c6043, 0xffb4c6b9, 0x005e1900, 0xffa724f0, 0x00484799, 0xffcb7893, 0x002262d6, 0xffebde33, 0x000a4e72, 0xfffb9c80, 0x00015e68,
+ 0x795915bc, 0xf4aa7cce, 0x045a565c, 0xfe246a72, 0x00ab7ca6, 0xffee4372, 0xffc9494b, 0x005070b0, 0xffaff16f, 0x0042d8e1, 0xffcea72c, 0x0020a3ad, 0xffecc075, 0x0009e78c, 0xfffbc4bf, 0x0001521b,
+ 0x78ef1457, 0xf3f5b7e4, 0x04bd6269, 0xfde48e17, 0x00d6ef99, 0xffd057bb, 0xffddb374, 0x0042d353, 0xffb8b9f3, 0x003d6aea, 0xffd1d635, 0x001ee3e1, 0xffeda32a, 0x00098070, 0xfffbed0a, 0x000145d7,
+ 0x787f4134, 0xf3454b6a, 0x051ee498, 0xfda56f9c, 0x0101fe7a, 0xffb2a145, 0xfff2024e, 0x003542e2, 0xffc17d30, 0x0037fe85, 0xffd50530, 0x001d23b9, 0xffee862e, 0x0009192f, 0xfffc1558, 0x0001399e,
+ 0x7809a65e, 0xf2993d95, 0x057ed264, 0xfd6716f2, 0x012ca389, 0xff952429, 0x000632fa, 0x0027c151, 0xffca39dd, 0x00329483, 0xffd833a0, 0x001b637e, 0xffef695c, 0x0008b1db, 0xfffc3da2, 0x00012d72,
+ 0x778e4e68, 0xf1f19421, 0x05dd218f, 0xfd298be0, 0x0156d920, 0xff77e470, 0x001a42a4, 0x001a508e, 0xffd2eeb3, 0x002d2db0, 0xffdb6109, 0x0019a373, 0xfff04c8f, 0x00084a86, 0xfffc65e2, 0x00012155,
+ 0x770d4466, 0xf14e544f, 0x0639c82d, 0xfcecd602, 0x018099b2, 0xff5ae614, 0x002e2e82, 0x000cf281, 0xffdb9a70, 0x0027cada, 0xffde8cf1, 0x0017e3df, 0xfff12fa3, 0x0007e33f, 0xfffc8e11, 0x0001154a,
+ 0x768693ec, 0xf0af82e4, 0x0694bca0, 0xfcb0fcca, 0x01a9dfcc, 0xff3e2d01, 0x0041f3d2, 0xffffa90e, 0xffe43bd5, 0x00226ccb, 0xffe1b6dd, 0x00162507, 0xfff21275, 0x00077c17, 0xfffcb628, 0x00010952,
+ 0x75fa4911, 0xf015242b, 0x06edf595, 0xfc76077b, 0x01d2a615, 0xff21bd11, 0x00558fdc, 0xfff27611, 0xffecd1a6, 0x001d144a, 0xffe4de56, 0x0014672d, 0xfff2f4e0, 0x00071520, 0xfffcde20, 0x0000fd6f,
+ 0x75687068, 0xef7f3bf5, 0x07456a0e, 0xfc3bfd2e, 0x01fae74e, 0xff059a0e, 0x0068fff3, 0xffe55b60, 0xfff55aae, 0x0017c21c, 0xffe802e6, 0x0012aa95, 0xfff3d6c3, 0x0006ae6a, 0xfffd05f3, 0x0000f1a4,
+ 0x74d11703, 0xeeedcd98, 0x079b1158, 0xfc02e4cc, 0x02229e57, 0xfee9c7af, 0x007c4177, 0xffd85ac9, 0xfffdd5b8, 0x00127704, 0xffeb2416, 0x0010ef82, 0xfff4b7fb, 0x00064804, 0xfffd2d9b, 0x0000e5f3,
+ 0x74344a70, 0xee60dbee, 0x07eee314, 0xfbcac510, 0x0249c629, 0xfece499d, 0x008f51cf, 0xffcb7615, 0x00064197, 0x000d33c3, 0xffee4174, 0x000f3633, 0xfff59866, 0x0005e1fe, 0xfffd5511, 0x0000da5c,
+ 0x739218b8, 0xedd86958, 0x0840d732, 0xfb93a486, 0x027059da, 0xfeb3236b, 0x00a22e71, 0xffbeaf06, 0x000e9d1f, 0x0007f915, 0xfff15a8d, 0x000d7eea, 0xfff677e2, 0x00057c68, 0xfffd7c4f, 0x0000cee3,
+ 0x72ea905a, 0xed5477be, 0x0890e5f7, 0xfb5d898c, 0x029654a0, 0xfe98589b, 0x00b4d4dd, 0xffb20754, 0x0016e72c, 0x0002c7b6, 0xfff46ef1, 0x000bc9e6, 0xfff75650, 0x00051750, 0xfffda350, 0x0000c388,
+ 0x723dc051, 0xecd5088e, 0x08df07f6, 0xfb287a4d, 0x02bbb1cc, 0xfe7dec9c, 0x00c7429f, 0xffa580b1, 0x001f1e9b, 0xfffda05c, 0xfff77e31, 0x000a1765, 0xfff8338e, 0x0004b2c7, 0xfffdca0d, 0x0000b84d,
+ 0x718bb80b, 0xec5a1cbc, 0x092b3617, 0xfaf47cc4, 0x02e06ccf, 0xfe63e2cc, 0x00d97550, 0xff991cc9, 0x00274253, 0xfff883be, 0xfffa87df, 0x000867a5, 0xfff90f7c, 0x00044eda, 0xfffdf080, 0x0000ad34,
+ 0x70d4876b, 0xebe3b4c5, 0x09756994, 0xfac196bb, 0x03048139, 0xfe4a3e70, 0x00eb6a95, 0xff8cdd3c, 0x002f513a, 0xfff3728d, 0xfffd8b92, 0x0006bae1, 0xfff9e9fd, 0x0003eb98, 0xfffe16a6, 0x0000a23f,
+ 0x70183ec5, 0xeb71d0ab, 0x09bd9bfb, 0xfa8fcdca, 0x0327eab8, 0xfe3102bd, 0x00fd2022, 0xff80c3a4, 0x00374a40, 0xffee6d78, 0x000088df, 0x00051157, 0xfffac2f0, 0x0003890e, 0xfffe3c76, 0x0000976e,
+ 0x6f56eee1, 0xeb046ffc, 0x0a03c72b, 0xfa5f2755, 0x034aa51b, 0xfe1832d4, 0x010e93b5, 0xff74d194, 0x003f2c57, 0xffe97529, 0x00037f60, 0x00036b3f, 0xfffb9a38, 0x0003274c, 0xfffe61ee, 0x00008cc4,
+ 0x6e90a8f2, 0xea9b91cc, 0x0a47e559, 0xfa2fa890, 0x036cac52, 0xfdffd1bd, 0x011fc31c, 0xff690894, 0x0046f679, 0xffe48a4a, 0x00066eae, 0x0001c8d2, 0xfffc6fb8, 0x0002c65d, 0xfffe8707, 0x00008241,
+ 0x6dc57e9b, 0xea3734bb, 0x0a89f10c, 0xfa015679, 0x038dfc6c, 0xfde7e26f, 0x0130ac31, 0xff5d6a24, 0x004ea7a3, 0xffdfad7f, 0x00095666, 0x00002a4a, 0xfffd4352, 0x00026650, 0xfffeabbd, 0x000077e8,
+ 0x6cf581e8, 0xe9d756f3, 0x0ac9e521, 0xf9d435dc, 0x03ae919a, 0xfdd067ca, 0x01414cdd, 0xff51f7bb, 0x00563edb, 0xffdadf69, 0x000c3627, 0xfffe8fdc, 0xfffe14eb, 0x00020730, 0xfffed00a, 0x00006db9,
+ 0x6c20c550, 0xe97bf627, 0x0b07bcc6, 0xf9a84b50, 0x03ce682d, 0xfdb96498, 0x0151a317, 0xff46b2c7, 0x005dbb29, 0xffd620a6, 0x000f0d91, 0xfffcf9be, 0xfffee466, 0x0001a90b, 0xfffef3ea, 0x000063b5,
+ 0x6b475bb0, 0xe9250f99, 0x0b437380, 0xf97d9b37, 0x03ed7c9a, 0xfda2db8c, 0x0161ace5, 0xff3b9cad, 0x00651b9c, 0xffd171d1, 0x0011dc47, 0xfffb6825, 0xffffb1aa, 0x00014bed, 0xffff1759, 0x000059dd,
+ 0x6a69584a, 0xe8d2a017, 0x0b7d0525, 0xf95429c0, 0x040bcb77, 0xfd8ccf46, 0x01716859, 0xff30b6c8, 0x006c5f4b, 0xffccd380, 0x0014a1ee, 0xfff9db44, 0x00007c9c, 0x0000efe1, 0xffff3a53, 0x00005033,
+ 0x6986cec4, 0xe884a3fb, 0x0bb46de2, 0xf92bfae4, 0x0429517b, 0xfd77424c, 0x0180d397, 0xff260269, 0x00738551, 0xffc84645, 0x00175e2d, 0xfff8534d, 0x00014521, 0x000094f3, 0xffff5cd2, 0x000046b8,
+ 0x689fd324, 0xe83b1731, 0x0be9aa34, 0xf9051266, 0x04460b81, 0xfd62370e, 0x018fecd1, 0xff1b80da, 0x007a8cd0, 0xffc3cab1, 0x001a10ad, 0xfff6d070, 0x00020b23, 0x00003b2e, 0xffff7ed3, 0x00003d6c,
+ 0x67b479cf, 0xe7f5f531, 0x0c1cb6ef, 0xf8df73d6, 0x0461f688, 0xfd4dafe6, 0x019eb246, 0xff113358, 0x008174ef, 0xffbf614e, 0x001cb91a, 0xfff552de, 0x0002ce87, 0xffffe29d, 0xffffa052, 0x00003450,
+ 0x66c4d787, 0xe7b53908, 0x0c4d913a, 0xf8bb228c, 0x047d0fb1, 0xfd39af17, 0x01ad2249, 0xff071b16, 0x00883cdc, 0xffbb0aa3, 0x001f5723, 0xfff3dac3, 0x00038f37, 0xffff8b4b, 0xffffc14b, 0x00002b66,
+ 0x65d10168, 0xe778dd50, 0x0c7c368d, 0xf89821ac, 0x0497543f, 0xfd2636ca, 0x01bb3b37, 0xfefd3941, 0x008ee3cd, 0xffb6c735, 0x0021ea76, 0xfff2684e, 0x00044d1b, 0xffff3540, 0xffffe1bc, 0x000022ad,
+ 0x64d90ce7, 0xe740dc3c, 0x0ca8a4b7, 0xf8767422, 0x04b0c19a, 0xfd134913, 0x01c8fb81, 0xfef38ef6, 0x009568fc, 0xffb29782, 0x002472c8, 0xfff0fba9, 0x0005081f, 0xfffee088, 0x0000019f, 0x00001a28,
+ 0x63dd0fcd, 0xe70d2f8d, 0x0cd2d9d5, 0xf8561ca7, 0x04c9554e, 0xfd00e7ec, 0x01d661a6, 0xfeea1d4c, 0x009bcbab, 0xffae7c06, 0x0026efcc, 0xffef94fe, 0x0005c02c, 0xfffe8d2c, 0x000020f3, 0x000011d5,
+ 0x62dd2039, 0xe6ddd09f, 0x0cfad45a, 0xf8371dbb, 0x04e10d0a, 0xfcef153a, 0x01e36c34, 0xfee0e54e, 0x00a20b23, 0xffaa7538, 0x0029613a, 0xffee3477, 0x0006752d, 0xfffe3b35, 0x00003fb3, 0x000009b6,
+ 0x61d95497, 0xe6b2b862, 0x0d209309, 0xf81979ab, 0x04f7e6a2, 0xfcddd2c7, 0x01f019cb, 0xfed7e7fd, 0x00a826b2, 0xffa6838c, 0x002bc6cd, 0xffecda3b, 0x0007270f, 0xfffdeaaa, 0x00005ddd, 0x000001cc,
+ 0x60d1c3a6, 0xe68bdf5e, 0x0d4414f9, 0xf7fd328c, 0x050de00d, 0xfccd2246, 0x01fc691b, 0xfecf2650, 0x00ae1dae, 0xffa2a770, 0x002e2040, 0xffeb866f, 0x0007d5bf, 0xfffd9b96, 0x00007b6f, 0xfffffa17,
+ 0x5fc68470, 0xe6693db5, 0x0d65598f, 0xf7e24a3c, 0x0522f766, 0xfcbd0551, 0x020858e2, 0xfec6a130, 0x00b3ef73, 0xff9ee150, 0x00306d52, 0xffea3939, 0x0008812a, 0xfffd4dff, 0x00009865, 0xfffff297,
+ 0x5eb7ae46, 0xe64acb24, 0x0d846084, 0xf7c8c267, 0x05372aee, 0xfcad7d6b, 0x0213e7f0, 0xfebe5980, 0x00b99b65, 0xff9b3192, 0x0032adc4, 0xffe8f2bb, 0x0009293e, 0xfffd01ee, 0x0000b4bd, 0xffffeb4c,
+ 0x5da558c5, 0xe6307f05, 0x0da129df, 0xf7b09c7f, 0x054a7909, 0xfc9e8bfd, 0x021f1526, 0xfeb65015, 0x00bf20ee, 0xff979898, 0x0034e15b, 0xffe7b317, 0x0009cdeb, 0xfffcb769, 0x0000d074, 0xffffe438,
+ 0x5c8f9bcb, 0xe61a504f, 0x0dbbb5f6, 0xf799d9c4, 0x055ce03f, 0xfc903258, 0x0229df75, 0xfeae85bb, 0x00c47f7f, 0xff9416c1, 0x003707dc, 0xffe67a6f, 0x000a6f20, 0xfffc6e78, 0x0000eb89, 0xffffdd5a,
+ 0x5b768f7a, 0xe6083599, 0x0dd40571, 0xf7847b3d, 0x056e5f3d, 0xfc8271b4, 0x023445dd, 0xfea6fb32, 0x00c9b691, 0xff90ac66, 0x00392111, 0xffe548e0, 0x000b0cce, 0xfffc2720, 0x000105f9, 0xffffd6b2,
+ 0x5a5a4c32, 0xe5fa2519, 0x0dea1943, 0xf77081be, 0x057ef4d3, 0xfc754b32, 0x023e4772, 0xfe9fb12e, 0x00cec5a1, 0xff8d59dd, 0x003b2cc5, 0xffe41e88, 0x000ba6e5, 0xfffbe169, 0x00011fc3, 0xffffd041,
+ 0x593aea93, 0xe5f014aa, 0x0dfdf2ae, 0xf75dede5, 0x058e9ff8, 0xfc68bfd7, 0x0247e354, 0xfe98a85b, 0x00d3ac38, 0xff8a1f77, 0x003d2ac6, 0xffe2fb83, 0x000c3d59, 0xfffb9d59, 0x000138e4, 0xffffca06,
+ 0x58188376, 0xe5e9f9ca, 0x0e0f9342, 0xf74cc01c, 0x059d5fc5, 0xfc5cd092, 0x025118b8, 0xfe91e159, 0x00d869e1, 0xff86fd81, 0x003f1ae4, 0xffe1dfec, 0x000cd01b, 0xfffb5af3, 0x0001515c, 0xffffc402,
+ 0x56f32fea, 0xe5e7c99e, 0x0e1efcdb, 0xf73cf898, 0x05ab3377, 0xfc517e38, 0x0259e6e1, 0xfe8b5cba, 0x00dcfe32, 0xff83f443, 0x0040fcf3, 0xffe0cbdc, 0x000d5f1f, 0xfffb1a3f, 0x00016928, 0xffffbe35,
+ 0x55cb0935, 0xe5e978f0, 0x0e2c319d, 0xf72e9758, 0x05b81a70, 0xfc46c987, 0x02624d23, 0xfe851b09, 0x00e168c5, 0xff810401, 0x0042d0c9, 0xffdfbf6b, 0x000dea5a, 0xfffadb40, 0x00018048, 0xffffb89f,
+ 0x54a028d0, 0xe5eefc35, 0x0e3733fc, 0xf7219c2a, 0x05c41435, 0xfc3cb323, 0x026a4ae5, 0xfe7f1cc4, 0x00e5a93c, 0xff7e2cfb, 0x0044963d, 0xffdebaaf, 0x000e71c1, 0xfffa9dfa, 0x000196ba, 0xffffb340,
+ 0x5372a862, 0xe5f8478d, 0x0e4006b2, 0xf71606a6, 0x05cf2070, 0xfc333b97, 0x0271df9c, 0xfe79625e, 0x00e9bf43, 0xff7b6f6c, 0x00464d2b, 0xffddbdbd, 0x000ef549, 0xfffa6273, 0x0001ac7d, 0xffffae17,
+ 0x5242a1c1, 0xe6054ec6, 0x0e46acc4, 0xf70bd632, 0x05d93eee, 0xfc2a6356, 0x02790ace, 0xfe73ec40, 0x00edaa88, 0xff78cb8c, 0x0047f571, 0xffdcc8a9, 0x000f74e9, 0xfffa28ad, 0x0001c191, 0xffffa924,
+ 0x51102eec, 0xe616055a, 0x0e4b297c, 0xf7030a01, 0x05e26f9f, 0xfc222abb, 0x027fcc12, 0xfe6ebac6, 0x00f16ac4, 0xff76418b, 0x00498eed, 0xffdbdb84, 0x000ff098, 0xfff9f0ac, 0x0001d5f4, 0xffffa467,
+ 0x4fdb6a09, 0xe62a5e76, 0x0e4d806f, 0xf6fba113, 0x05eab296, 0xfc1a9208, 0x02862311, 0xfe69ce43, 0x00f4ffb6, 0xff73d199, 0x004b1984, 0xffdaf65e, 0x0010684e, 0xfff9ba73, 0x0001e9a7, 0xffff9fe0,
+ 0x4ea46d66, 0xe6424cf8, 0x0e4db575, 0xf6f59a36, 0x05f20809, 0xfc139968, 0x028c0f83, 0xfe6526fe, 0x00f86924, 0xff717bdf, 0x004c951b, 0xffda1948, 0x0010dc05, 0xfff98604, 0x0001fca8, 0xffff9b8f,
+ 0x4d6b536f, 0xe65dc373, 0x0e4bccac, 0xf6f0f407, 0x05f87053, 0xfc0d40ec, 0x0291912f, 0xfe60c533, 0x00fba6da, 0xff6f4083, 0x004e0199, 0xffd9444e, 0x00114bb4, 0xfff95363, 0x00020ef7, 0xffff9773,
+ 0x4c3036b2, 0xe67cb42f, 0x0e47ca78, 0xf6edacf2, 0x05fdebee, 0xfc07888e, 0x0296a7f0, 0xfe5ca913, 0x00feb8ad, 0xff6d1fa5, 0x004f5ee9, 0xffd8777d, 0x0011b757, 0xfff92290, 0x00022095, 0xffff938c,
+ 0x4af331d9, 0xe69f112f, 0x0e41b37c, 0xf6ebc332, 0x06027b78, 0xfc027031, 0x029b53af, 0xfe58d2c5, 0x01019e78, 0xff6b1961, 0x0050acf7, 0xffd7b2e0, 0x00121ee9, 0xfff8f38e, 0x00023181, 0xffff8fd9,
+ 0x49b45fa8, 0xe6c4cc2e, 0x0e398c9f, 0xf6eb34d4, 0x06061fb2, 0xfbfdf79e, 0x029f9466, 0xfe554265, 0x0104581c, 0xff692dd2, 0x0051ebb4, 0xffd6f67f, 0x00128265, 0xfff8c65d, 0x000241bb, 0xffff8c5a,
+ 0x4873daf7, 0xe6edd6a4, 0x0e2f5b0b, 0xf6ebffb2, 0x0608d97c, 0xfbfa1e88, 0x02a36a1e, 0xfe51f802, 0x0106e583, 0xff675d09, 0x00531b12, 0xffd64264, 0x0012e1c8, 0xfff89b00, 0x00025143, 0xffff890e,
+ 0x4731beb7, 0xe71a21c7, 0x0e232425, 0xf6ee217b, 0x060aa9da, 0xfbf6e48c, 0x02a6d4f0, 0xfe4ef3a4, 0x0109469d, 0xff65a718, 0x00543b04, 0xffd59695, 0x00133d0e, 0xfff87176, 0x0002601b, 0xffff85f5,
+ 0x45ee25e7, 0xe7499e8f, 0x0e14ed93, 0xf6f197ad, 0x060b91ee, 0xfbf4492d, 0x02a9d508, 0xfe4c3546, 0x010b7b61, 0xff640c08, 0x00554b83, 0xffd4f316, 0x00139436, 0xfff849c0, 0x00026e41, 0xffff830e,
+ 0x44a92b96, 0xe77c3db4, 0x0e04bd39, 0xf6f65f9b, 0x060b92ff, 0xfbf24bd9, 0x02ac6a9e, 0xfe49bcd9, 0x010d83cb, 0xff628be3, 0x00564c88, 0xffd457ec, 0x0013e73e, 0xfff823dd, 0x00027bb8, 0xffff805a,
+ 0x4362eadc, 0xe7b1efb4, 0x0df29936, 0xf6fc766a, 0x060aae6e, 0xfbf0ebe7, 0x02ae95fb, 0xfe478a42, 0x010f5fe2, 0xff6126a9, 0x00573e0f, 0xffd3c519, 0x00143626, 0xfff7ffce, 0x0002887f, 0xffff7dd6,
+ 0x421b7edf, 0xe7eaa4d4, 0x0dde87e2, 0xf703d912, 0x0608e5c2, 0xfbf02896, 0x02b05779, 0xfe459d5e, 0x01110faf, 0xff5fdc5b, 0x00582016, 0xffd33a9e, 0x001480ec, 0xfff7dd92, 0x00029497, 0xffff7b82,
+ 0x40d302c5, 0xe8264d21, 0x0dc88fd2, 0xf70c8461, 0x06063a9d, 0xfbf00112, 0x02b1af7f, 0xfe43f5ff, 0x01129344, 0xff5eacf3, 0x0058f29f, 0xffd2b87c, 0x0014c792, 0xfff7bd28, 0x0002a002, 0xffff795f,
+ 0x3f8991bd, 0xe864d874, 0x0db0b7d1, 0xf71674fa, 0x0602aec3, 0xfbf0746e, 0x02b29e84, 0xfe4293ec, 0x0113eabb, 0xff5d9867, 0x0059b5ad, 0xffd23eaf, 0x00150a19, 0xfff79e8f, 0x0002aac0, 0xffff776a,
+ 0x3e3f46f2, 0xe8a63671, 0x0d9706e1, 0xf721a756, 0x05fe4414, 0xfbf181a9, 0x02b3250f, 0xfe4176e2, 0x01151632, 0xff5c9eaa, 0x005a6946, 0xffd1cd37, 0x00154883, 0xfff781c5, 0x0002b4d2, 0xffff75a3,
+ 0x3cf43d8f, 0xe8ea568f, 0x0d7b843b, 0xf72e17c4, 0x05f8fc8f, 0xfbf327ab, 0x02b343b5, 0xfe409e95, 0x011615ce, 0xff5bbfaa, 0x005b0d72, 0xffd1640e, 0x001582d3, 0xfff766c8, 0x0002be3b, 0xffff740a,
+ 0x3ba890b9, 0xe9312813, 0x0d5e3749, 0xf73bc26b, 0x05f2da52, 0xfbf56549, 0x02b2fb1a, 0xfe400aae, 0x0116e9bc, 0xff5afb53, 0x005ba23b, 0xffd1032f, 0x0015b90b, 0xfff74d97, 0x0002c6fa, 0xffff729e,
+ 0x3a5c5b8e, 0xe97a9a17, 0x0d3f27ab, 0xf74aa34c, 0x05ebdf97, 0xfbf83941, 0x02b24bf1, 0xfe3fbacd, 0x0117922f, 0xff5a5189, 0x005c27af, 0xffd0aa93, 0x0015eb2f, 0xfff7362f, 0x0002cf12, 0xffff715d,
+ 0x390fb920, 0xe9c69b8c, 0x0d1e5d32, 0xf75ab63f, 0x05e40eb3, 0xfbfba23f, 0x02b136f9, 0xfe3fae87, 0x01180f5d, 0xff59c230, 0x005c9ddc, 0xffd05a33, 0x00161944, 0xfff7208d, 0x0002d684, 0xffff7047,
+ 0x37c2c474, 0xea151b3a, 0x0cfbdfdd, 0xf76bf6f7, 0x05db6a19, 0xfbff9ed7, 0x02afbd02, 0xfe3fe569, 0x01186187, 0xff594d27, 0x005d04d4, 0xffd01205, 0x0016434f, 0xfff70caf, 0x0002dd53, 0xffff6f5c,
+ 0x36759880, 0xea6607c4, 0x0cd7b7dd, 0xf77e6103, 0x05d1f459, 0xfc042d8e, 0x02addee8, 0xfe405ef6, 0x011888f2, 0xff58f249, 0x005d5cab, 0xffcfd1ff, 0x00166956, 0xfff6fa92, 0x0002e37e, 0xffff6e99,
+ 0x35285026, 0xeab94fa9, 0x0cb1ed8c, 0xf791efcb, 0x05c7b01a, 0xfc094cd2, 0x02ab9d96, 0xfe411aa8, 0x011885e7, 0xff58b16c, 0x005da575, 0xffcf9a15, 0x00168b5e, 0xfff6ea31, 0x0002e90a, 0xffff6dff,
+ 0x33db0631, 0xeb0ee148, 0x0c8a8973, 0xf7a69e96, 0x05bca021, 0xfc0efafe, 0x02a8fa03, 0xfe4217ef, 0x011858b9, 0xff588a65, 0x005ddf4c, 0xffcf6a3b, 0x0016a96f, 0xfff6db89, 0x0002edf6, 0xffff6d8d,
+ 0x328dd556, 0xeb66aae0, 0x0c619444, 0xf7bc6889, 0x05b0c74b, 0xfc15365c, 0x02a5f535, 0xfe435633, 0x011801be, 0xff587d03, 0x005e0a48, 0xffcf4262, 0x0016c390, 0xfff6ce97, 0x0002f246, 0xffff6d40,
+ 0x3140d82e, 0xebc09a94, 0x0c3716da, 0xf7d348a4, 0x05a42890, 0xfc1bfd22, 0x02a2903e, 0xfe44d4d3, 0x01178152, 0xff588913, 0x005e2687, 0xffcf227b, 0x0016d9c9, 0xfff6c356, 0x0002f5fc, 0xffff6d1a,
+ 0x2ff42933, 0xec1c9e6d, 0x0c0b1a37, 0xf7eb39cc, 0x0596c6ff, 0xfc234d75, 0x029ecc3c, 0xfe469325, 0x0116d7d7, 0xff58ae5d, 0x005e3427, 0xffcf0a77, 0x0016ec22, 0xfff6b9c1, 0x0002f919, 0xffff6d17,
+ 0x2ea7e2c0, 0xec7aa45b, 0x0bdda783, 0xf80436c0, 0x0588a5bf, 0xfc2b2567, 0x029aaa5a, 0xfe489077, 0x011605b5, 0xff58eca8, 0x005e3347, 0xffcefa44, 0x0016faa5, 0xfff6b1d5, 0x0002fba0, 0xffff6d38,
+ 0x2d5c1f0e, 0xecda9a39, 0x0baec80a, 0xf81e3a25, 0x0579c812, 0xfc3382fb, 0x02962bd1, 0xfe4acc0e, 0x01150b5a, 0xff5943b4, 0x005e240a, 0xffcef1cf, 0x0017055b, 0xfff6ab8c, 0x0002fd94, 0xffff6d7c,
+ 0x2c10f82d, 0xed3c6dce, 0x0b7e853c, 0xf8393e81, 0x056a314b, 0xfc3c6420, 0x029151e3, 0xfe4d4526, 0x0113e937, 0xff59b340, 0x005e0694, 0xffcef106, 0x00170c4f, 0xfff6a6e2, 0x0002fef6, 0xffff6de2,
+ 0x2ac68807, 0xeda00cd1, 0x0b4ce8a8, 0xf8553e3c, 0x0559e4da, 0xfc45c6b6, 0x028c1de0, 0xfe4ffaf6, 0x01129fc5, 0xff5a3b09, 0x005ddb0b, 0xffcef7d4, 0x00170f8a, 0xfff6a3d0, 0x0002ffc9, 0xffff6e67,
+ 0x297ce85a, 0xee0564e8, 0x0b19fbfe, 0xf87233a4, 0x0548e63f, 0xfc4fa88f, 0x02869122, 0xfe52ecab, 0x01112f81, 0xff5adac6, 0x005da198, 0xffcf0623, 0x00170f18, 0xfff6a252, 0x00030010, 0xffff6f0d,
+ 0x283432b9, 0xee6c63ad, 0x0ae5c90b, 0xf89018eb, 0x05373912, 0xfc5a076a, 0x0280ad0f, 0xfe561969, 0x010f98eb, 0xff5b922d, 0x005d5a62, 0xffcf1bde, 0x00170b04, 0xfff6a262, 0x0002ffcd, 0xffff6fd1,
+ 0x26ec8083, 0xeed4f6b0, 0x0ab059bc, 0xf8aee828, 0x0524e100, 0xfc64e0f9, 0x027a7318, 0xfe598050, 0x010ddc8c, 0xff5c60ee, 0x005d0597, 0xffcf38ec, 0x0017035a, 0xfff6a3f9, 0x0002ff03, 0xffff70b2,
+ 0x25a5eae8, 0xef3f0b78, 0x0a79b814, 0xf8ce9b5d, 0x0511e1c6, 0xfc7032de, 0x0273e4b8, 0xfe5d2075, 0x010bfaee, 0xff5d46bb, 0x005ca363, 0xffcf5d36, 0x0016f828, 0xfff6a713, 0x0002fdb4, 0xffff71b0,
+ 0x24608ae2, 0xefaa8f87, 0x0a41ee32, 0xf8ef2c71, 0x04fe3f39, 0xfc7bfaad, 0x026d0374, 0xfe60f8ea, 0x0109f4a2, 0xff5e433e, 0x005c33f6, 0xffcf88a2, 0x0016e979, 0xfff6aba9, 0x0002fbe4, 0xffff72c9,
+ 0x231c7932, 0xf017705a, 0x0a09064e, 0xf9109535, 0x04e9fd3c, 0xfc8835ed, 0x0265d0dd, 0xfe6508b6, 0x0107ca3c, 0xff5f5621, 0x005bb77f, 0xffcfbb17, 0x0016d75b, 0xfff6b1b4, 0x0002f995, 0xffff73fc,
+ 0x21d9ce63, 0xf0859b6e, 0x09cf0ab4, 0xf932cf65, 0x04d51fc6, 0xfc94e216, 0x025e4e8b, 0xfe694edd, 0x01057c57, 0xff607f0b, 0x005b2e31, 0xffcff478, 0x0016c1dc, 0xfff6b92d, 0x0002f6c9, 0xffff7549,
+ 0x2098a2bf, 0xf0f4fe3d, 0x099405c6, 0xf955d4a7, 0x04bfaadf, 0xfca1fc96, 0x02567e22, 0xfe6dca58, 0x01030b8e, 0xff61bd9f, 0x005a9840, 0xffd034ac, 0x0016a90a, 0xfff6c20f, 0x0002f385, 0xffff76ae,
+ 0x1f590e55, 0xf1658649, 0x095801f8, 0xf9799e8f, 0x04a9a29e, 0xfcaf82ca, 0x024e614c, 0xfe727a1f, 0x01007885, 0xff631180, 0x0059f5e1, 0xffd07b95, 0x00168cf2, 0xfff6cc52, 0x0002efca, 0xffff782a,
+ 0x1e1b28f2, 0xf1d72114, 0x091b09d1, 0xf99e269e, 0x04930b2b, 0xfcbd7206, 0x0245f9bf, 0xfe775d1f, 0x00fdc3e0, 0xff647a4b, 0x0059474a, 0xffd0c915, 0x00166da5, 0xfff6d7f0, 0x0002eb9c, 0xffff79bc,
+ 0x1cdf0a20, 0xf249bc2c, 0x08dd27e6, 0xf9c36642, 0x047be8bc, 0xfccbc793, 0x023d4937, 0xfe7c7243, 0x00faee49, 0xff65f79e, 0x00588cb4, 0xffd11d0f, 0x00164b32, 0xfff6e4e1, 0x0002e6fe, 0xffff7b63,
+ 0x1ba4c923, 0xf2bd4523, 0x089e66dd, 0xf9e956da, 0x04643f95, 0xfcda80ad, 0x0234517a, 0xfe81b86d, 0x00f7f86e, 0xff678912, 0x0057c658, 0xffd17764, 0x001625a7, 0xfff6f31d, 0x0002e1f3, 0xffff7d1f,
+ 0x1a6c7cf9, 0xf331a99b, 0x085ed167, 0xfa0ff1b6, 0x044c1409, 0xfce99a86, 0x022b1455, 0xfe872e7c, 0x00f4e2ff, 0xff692e3f, 0x0056f471, 0xffd1d7f5, 0x0015fd15, 0xfff7029f, 0x0002dc7d, 0xffff7eed,
+ 0x19363c54, 0xf3a6d741, 0x081e7241, 0xfa373017, 0x04336a75, 0xfcf91246, 0x0221939d, 0xfe8cd349, 0x00f1aeb2, 0xff6ae6ba, 0x0056173b, 0xffd23ea1, 0x0015d18b, 0xfff7135d, 0x0002d6a0, 0xffff80cd,
+ 0x18021d9d, 0xf41cbbd3, 0x07dd5430, 0xfa5f0b30, 0x041a4744, 0xfd08e50c, 0x0217d12d, 0xfe92a5a7, 0x00ee5c3e, 0xff6cb218, 0x00552ef3, 0xffd2ab47, 0x0015a31b, 0xfff72551, 0x0002d060, 0xffff82bf,
+ 0x16d036eb, 0xf493451f, 0x079b8203, 0xfa877c29, 0x0400aeec, 0xfd190fed, 0x020dcee8, 0xfe98a466, 0x00eaec5e, 0xff6e8fe9, 0x00543bd8, 0xffd31dc7, 0x001571d5, 0xfff73873, 0x0002c9be, 0xffff84c0,
+ 0x15a09e09, 0xf50a610a, 0x0759068f, 0xfab07c1d, 0x03e6a5ee, 0xfd298ff6, 0x02038eb7, 0xfe9ece4f, 0x00e75fd1, 0xff707fbd, 0x00533e29, 0xffd395fd, 0x00153dca, 0xfff74cba, 0x0002c2be, 0xffff86d0,
+ 0x1473686d, 0xf581fd8b, 0x0715ecae, 0xfada0420, 0x03cc30d4, 0xfd3a622b, 0x01f9128a, 0xfea52227, 0x00e3b758, 0xff728121, 0x00523626, 0xffd413c9, 0x0015070b, 0xfff76220, 0x0002bb64, 0xffff88ee,
+ 0x1348ab3a, 0xf5fa08b5, 0x06d23f3d, 0xfb040d3b, 0x03b15431, 0xfd4b8389, 0x01ee5c55, 0xfeab9eb2, 0x00dff3b7, 0xff7493a2, 0x00512412, 0xffd49705, 0x0014cdab, 0xfff7789c, 0x0002b3b3, 0xffff8b19,
+ 0x12207b3e, 0xf67270b1, 0x068e091c, 0xfb2e906f, 0x039614a1, 0xfd5cf105, 0x01e36e14, 0xfeb242ac, 0x00dc15b4, 0xff76b6ca, 0x0050082f, 0xffd51f90, 0x001491b9, 0xfff79026, 0x0002abad, 0xffff8d50,
+ 0x10faecee, 0xf6eb23c6, 0x0649552a, 0xfb5986b6, 0x037a76c7, 0xfd6ea790, 0x01d849c7, 0xfeb90cce, 0x00d81e1a, 0xff78ea20, 0x004ee2c1, 0xffd5ad44, 0x00145349, 0xfff7a8b6, 0x0002a357, 0xffff8f92,
+ 0x0fd81464, 0xf7641059, 0x06042e45, 0xfb84e906, 0x035e7f4e, 0xfd80a411, 0x01ccf173, 0xfebffbd0, 0x00d40db3, 0xff7b2d2d, 0x004db40c, 0xffd63ffe, 0x0014126c, 0xfff7c245, 0x00029ab2, 0xffff91de,
+ 0x0eb80562, 0xf7dd24ef, 0x05be9f49, 0xfbb0b04e, 0x034232e6, 0xfd92e36c, 0x01c16720, 0xfec70e64, 0x00cfe54f, 0xff7d7f76, 0x004c7c55, 0xffd6d798, 0x0013cf36, 0xfff7dcc8, 0x000291c3, 0xffff9434,
+ 0x0d9ad348, 0xf856502d, 0x0578b30e, 0xfbdcd57a, 0x03259644, 0xfda5627e, 0x01b5acdd, 0xfece433a, 0x00cba5bc, 0xff7fe07f, 0x004b3be3, 0xffd773ed, 0x001389b7, 0xfff7f83a, 0x0002888c, 0xffff9691,
+ 0x0c80911b, 0xf8cf80de, 0x05327467, 0xfc095174, 0x0308ae24, 0xfdb81e22, 0x01a9c4bc, 0xfed598fe, 0x00c74fce, 0xff824fca, 0x0049f2fc, 0xffd814d7, 0x00134204, 0xfff81490, 0x00027f11, 0xffff98f5,
+ 0x0b69517e, 0xf948a5f0, 0x04ebee1c, 0xfc361d25, 0x02eb7f44, 0xfdcb132d, 0x019db0d0, 0xfedd0e5c, 0x00c2e457, 0xff84ccdb, 0x0048a1e7, 0xffd8ba31, 0x0012f82e, 0xfff831c3, 0x00027555, 0xffff9b60,
+ 0x0a5526b0, 0xf9c1ae7b, 0x04a52af2, 0xfc633173, 0x02ce0e67, 0xfdde3e6f, 0x01917334, 0xfee4a1fa, 0x00be642f, 0xff875731, 0x004748ed, 0xffd963d4, 0x0012ac48, 0xfff84fcb, 0x00026b5b, 0xffff9dd0,
+ 0x0944228e, 0xfa3a89be, 0x045e359f, 0xfc908746, 0x02b0604f, 0xfdf19cb9, 0x01850e00, 0xfeec527e, 0x00b9d02b, 0xff89ee4d, 0x0045e856, 0xffda1199, 0x00125e66, 0xfff86e9e, 0x00026126, 0xffffa045,
+ 0x08365690, 0xfab32723, 0x041718d2, 0xfcbe1789, 0x029279c4, 0xfe052ad4, 0x01788354, 0xfef41e8c, 0x00b52925, 0xff8c91ad, 0x0044806c, 0xffdac35a, 0x00120e9b, 0xfff88e35, 0x000256b9, 0xffffa2be,
+ 0x072bd3c5, 0xfb2b7641, 0x03cfdf29, 0xfcebdb26, 0x02745f8c, 0xfe18e58c, 0x016bd54f, 0xfefc04c6, 0x00b06ff7, 0xff8f40d0, 0x00431177, 0xffdb78ef, 0x0011bcf9, 0xfff8ae88, 0x00024c18, 0xffffa539,
+ 0x0624aad6, 0xfba366df, 0x03889336, 0xfd19cb0e, 0x02561670, 0xfe2cc9a7, 0x015f0612, 0xff0403cc, 0x00aba57c, 0xff91fb31, 0x00419bc2, 0xffdc3231, 0x00116994, 0xfff8cf8d, 0x00024146, 0xffffa7b7,
+ 0x0520ec00, 0xfc1ae8f2, 0x03413f7b, 0xfd47e035, 0x0237a337, 0xfe40d3ed, 0x015217c0, 0xff0c1a3c, 0x00a6ca90, 0xff94c04f, 0x00401f98, 0xffdceef9, 0x00111480, 0xfff8f13c, 0x00023645, 0xffffaa35,
+ 0x0420a716, 0xfc91eca1, 0x02f9ee68, 0xfd761395, 0x02190aa6, 0xfe550124, 0x01450c7f, 0xff1446b5, 0x00a1e00f, 0xff978fa6, 0x003e9d42, 0xffddaf1e, 0x0010bdcf, 0xfff9138e, 0x00022b19, 0xffffacb4,
+ 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5, 0xffffaf33,
+};
+
+// cmd-line: fir -l 7 -s 44100 -c 19876 -n 16 -b 9.62
+const int32_t dn_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = {
+ 0x736144b5, 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9,
+ 0x735ed3aa, 0x0b433de8, 0xf560f0f3, 0x091282c4, 0xf8dd5ccf, 0x0525cb66, 0xfca23e3d, 0x01f33960, 0xff0bc9c2, 0x00586127, 0xfff68603, 0xffecbad5, 0x0015ab8b, 0xfff17c10, 0x0006f71a, 0xfffdbc2f,
+ 0x735780bb, 0x0a55a98f, 0xf5c5b2a1, 0x08e1ea27, 0xf8f25767, 0x0520366d, 0xfc9ff262, 0x01f89c98, 0xff0620a4, 0x005cf349, 0xfff36c0d, 0xffee8913, 0x0014c5dc, 0xfff1db1a, 0x0006d7d7, 0xfffdc3db,
+ 0x734b4c77, 0x096a8a51, 0xf62adb7c, 0x08b086aa, 0xf9081629, 0x051a030f, 0xfc9e186a, 0x01fdb637, 0xff00a1d8, 0x00617065, 0xfff05a84, 0xfff0552d, 0x0013dfed, 0xfff23ada, 0x0006b817, 0xfffdcbba,
+ 0x733a37d2, 0x0881ed1f, 0xf6905e79, 0x087e5fd7, 0xf91e9521, 0x05133308, 0xfc9cafe0, 0x0202860e, 0xfefb4dc7, 0x0065d80c, 0xffed51bc, 0xfff21ee8, 0x0012f9de, 0xfff29b40, 0x000697e0, 0xfffdd3ca,
+ 0x7324441e, 0x079bdea7, 0xf6f62e9d, 0x084b7d43, 0xf935d048, 0x050bc828, 0xfc9bb83e, 0x02070bf9, 0xfef624d8, 0x006a29d6, 0xffea520a, 0xfff3e60f, 0x001213d0, 0xfff2fc3d, 0x00067739, 0xfffddc07,
+ 0x7309730f, 0x06b86b52, 0xf75c3eff, 0x0817e68c, 0xf94dc388, 0x0503c44d, 0xfc9b30f3, 0x020b47dd, 0xfef12766, 0x006e655c, 0xffe75bbe, 0xfff5aa69, 0x00112de1, 0xfff35dc1, 0x00065629, 0xfffde470,
+ 0x72e9c6b8, 0x05d79f40, 0xf7c282cb, 0x07e3a35a, 0xf9666ab7, 0x04fb2969, 0xfc9b195f, 0x020f39ab, 0xfeec55cc, 0x00728a3d, 0xffe46f2a, 0xfff76bc2, 0x00104831, 0xfff3bfbc, 0x000634b6, 0xfffded03,
+ 0x72c5418e, 0x04f98649, 0xf828ed43, 0x07aebb5d, 0xf97fc19e, 0x04f1f97c, 0xfc9b70d6, 0x0212e15c, 0xfee7b059, 0x0076981a, 0xffe18c9a, 0xfff929e3, 0x000f62de, 0xfff4221f, 0x000612e8, 0xfffdf5bc,
+ 0x729be665, 0x041e2bfe, 0xf88f71bf, 0x0779364a, 0xf999c3f4, 0x04e83697, 0xfc9c369c, 0x02163ef1, 0xfee33759, 0x007a8e98, 0xffdeb45b, 0xfffae49b, 0x000e7e08, 0xfff484db, 0x0005f0c4, 0xfffdfe9b,
+ 0x726db871, 0x03459ba4, 0xf8f603ae, 0x07431bdf, 0xf9b46d64, 0x04dde2da, 0xfc9d69eb, 0x02195278, 0xfedeeb11, 0x007e6d61, 0xffdbe6b6, 0xfffc9bb4, 0x000d99cc, 0xfff4e7e1, 0x0005ce51, 0xfffe079b,
+ 0x723abb44, 0x026fe039, 0xf95c9699, 0x070c73dd, 0xf9cfb988, 0x04d30074, 0xfc9f09ee, 0x021c1c06, 0xfedacbbf, 0x00823422, 0xffd923f4, 0xfffe4efd, 0x000cb647, 0xfff54b20, 0x0005ab95, 0xfffe10bc,
+ 0x7202f2d3, 0x019d046d, 0xf9c31e22, 0x06d5460b, 0xf9eba3ef, 0x04c791a4, 0xfca115c5, 0x021e9bbb, 0xfed6d99c, 0x0085e28b, 0xffd66c59, 0xfffffe46, 0x000bd397, 0xfff5ae8c, 0x00058898, 0xfffe19fa,
+ 0x71c6636d, 0x00cd12a4, 0xfa298e07, 0x069d9a31, 0xfa082817, 0x04bb98b5, 0xfca38c83, 0x0220d1bf, 0xfed314da, 0x00897851, 0xffd3c02a, 0x0001a95d, 0x000af1d9, 0xfff61214, 0x0005655e, 0xfffe2354,
+ 0x718511c2, 0x000014f8, 0xfa8fda21, 0x0665781b, 0xfa254176, 0x04af1804, 0xfca66d2e, 0x0222be45, 0xfecf7da3, 0x008cf52d, 0xffd11fa9, 0x00035015, 0x000a1129, 0xfff675ab, 0x000541f0, 0xfffe2cc8,
+ 0x713f02e0, 0xff361534, 0xfaf5f669, 0x062ce795, 0xfa42eb75, 0x04a211f8, 0xfca9b6bf, 0x02246187, 0xfecc141d, 0x009058da, 0xffce8b13, 0x0004f23e, 0x000931a3, 0xfff6d942, 0x00051e52, 0xfffe3652,
+ 0x70f43c32, 0xfe6f1cd7, 0xfb5bd6f4, 0x05f3f06b, 0xfa61216f, 0x04948906, 0xfcad6827, 0x0225bbca, 0xfec8d867, 0x0093a31a, 0xffcc02a8, 0x00068fad, 0x00085362, 0xfff73ccb, 0x0004fa8b, 0xfffe3ff2,
+ 0x70a4c37f, 0xfdab350f, 0xfbc16ff6, 0x05ba9a6b, 0xfa7fdeba, 0x04867fb3, 0xfcb18047, 0x0226cd5b, 0xfec5ca9a, 0x0096d3af, 0xffc986a1, 0x00082835, 0x00077681, 0xfff7a037, 0x0004d6a1, 0xfffe49a4,
+ 0x70509eec, 0xfcea66be, 0xfc26b5c5, 0x0580ed5f, 0xfa9f1e9e, 0x0477f88d, 0xfcb5fdf7, 0x02279691, 0xfec2eaca, 0x0099ea62, 0xffc71738, 0x0009bbab, 0x00069b1b, 0xfff8037a, 0x0004b29a, 0xfffe5367,
+ 0x6ff7d4f8, 0xfc2cba75, 0xfc8b9cda, 0x0546f10f, 0xfabedc5a, 0x0468f62e, 0xfcbae002, 0x022817ca, 0xfec03901, 0x009ce6fe, 0xffc4b4a4, 0x000b49e6, 0x0005c149, 0xfff86686, 0x00048e7c, 0xfffe5d38,
+ 0x6f9a6c7f, 0xfb723876, 0xfcf019cd, 0x050cad3f, 0xfadf1328, 0x04597b40, 0xfcc0252b, 0x0228516f, 0xfebdb547, 0x009fc954, 0xffc25f1a, 0x000cd2bd, 0x0004e926, 0xfff8c94c, 0x00046a4c, 0xfffe6716,
+ 0x6f386cb6, 0xfabae8b2, 0xfd54215c, 0x04d229b1, 0xfaffbe36, 0x04498a72, 0xfcc5cc26, 0x022843f0, 0xfebb5f9b, 0x00a29136, 0xffc016cb, 0x000e5609, 0x000412c9, 0xfff92bc0, 0x00044612, 0xfffe70ff,
+ 0x6ed1dd2e, 0xfa06d2ca, 0xfdb7a869, 0x04976e20, 0xfb20d8ad, 0x04392684, 0xfccbd3a0, 0x0227efc6, 0xfeb937f9, 0x00a53e7b, 0xffbddbe8, 0x000fd3a3, 0x00033e4c, 0xfff98dd6, 0x000421d2, 0xfffe7aef,
+ 0x6e66c5ce, 0xf955fe0c, 0xfe1aa3fc, 0x045c8240, 0xfb425db0, 0x0428523d, 0xfcd23a3a, 0x02275572, 0xfeb73e54, 0x00a7d0ff, 0xffbbae9f, 0x00114b67, 0x00026bc6, 0xfff9ef80, 0x0003fd92, 0xfffe84e7,
+ 0x6df72ed9, 0xf8a87178, 0xfe7d0942, 0x04216dc0, 0xfb64485b, 0x0417106e, 0xfcd8fe8b, 0x0226757e, 0xfeb5729b, 0x00aa48a0, 0xffb98f1c, 0x0012bd30, 0x00019b4e, 0xfffa50b1, 0x0003d957, 0xfffe8ee3,
+ 0x6d8320e6, 0xf7fe33ba, 0xfedecd90, 0x03e63846, 0xfb8693c6, 0x040563f4, 0xfce01f21, 0x0225507c, 0xfeb3d4b7, 0x00aca542, 0xffb77d88, 0x001428db, 0x0000ccfc, 0xfffab15e, 0x0003b527, 0xfffe98e2,
+ 0x6d0aa4e6, 0xf7574b2b, 0xff3fe663, 0x03aae970, 0xfba93b01, 0x03f34fb2, 0xfce79a7f, 0x0223e706, 0xfeb26489, 0x00aee6ca, 0xffb57a0b, 0x00158e47, 0x000000e6, 0xfffb117a, 0x00039108, 0xfffea2e1,
+ 0x6c8dc41f, 0xf6b3bdd3, 0xffa04963, 0x036f88d2, 0xfbcc391d, 0x03e0d697, 0xfcef6f20, 0x022239bc, 0xfeb121ee, 0x00b10d23, 0xffb384ca, 0x0016ed53, 0xffff3721, 0xfffb70fa, 0x00036cfe, 0xfffeacdf,
+ 0x6c0c882a, 0xf6139169, 0xffffec5f, 0x03341df4, 0xfbef8924, 0x03cdfb99, 0xfcf79b75, 0x02204949, 0xfeb00cbf, 0x00b3183c, 0xffb19de7, 0x001845e0, 0xfffe6fc3, 0xfffbcfd2, 0x00034910, 0xfffeb6db,
+ 0x6b86faf8, 0xf576cb4e, 0x005ec552, 0x02f8b055, 0xfc13261f, 0x03bac1b4, 0xfd001de8, 0x021e165d, 0xfeaf24cc, 0x00b50805, 0xffafc584, 0x001997d0, 0xfffdaadf, 0xfffc2df6, 0x00032541, 0xfffec0d2,
+ 0x6afd26cb, 0xf4dd7092, 0x00bcca63, 0x02bd4768, 0xfc370b14, 0x03a72bf0, 0xfd08f4d6, 0x021ba1b2, 0xfeae69e1, 0x00b6dc75, 0xffadfbbe, 0x001ae306, 0xfffce88b, 0xfffc8b5c, 0x00030196, 0xfffecac3,
+ 0x6a6f1638, 0xf44785f1, 0x0119f1e4, 0x0281ea90, 0xfc5b3309, 0x03933d58, 0xfd121e99, 0x0218ec06, 0xfeaddbc4, 0x00b89584, 0xffac40b3, 0x001c2765, 0xfffc28d9, 0xfffce7f8, 0x0002de16, 0xfffed4ab,
+ 0x69dcd425, 0xf3b50fd6, 0x01763256, 0x0246a125, 0xfc7f9902, 0x037ef900, 0xfd1b9980, 0x0215f621, 0xfead7a37, 0x00ba3330, 0xffaa947c, 0x001d64d5, 0xfffb6bdd, 0xfffd43c1, 0x0002bac4, 0xfffede8a,
+ 0x69466bc8, 0xf3261255, 0x01d18265, 0x020b726f, 0xfca43803, 0x036a6201, 0xfd2563d3, 0x0212c0d2, 0xfead44f4, 0x00bbb579, 0xffa8f730, 0x001e9b3a, 0xfffab1a8, 0xfffd9eab, 0x000297a5, 0xfffee85e,
+ 0x68abe8a8, 0xf29a9133, 0x022bd8ee, 0x01d065a8, 0xfcc90b12, 0x03557b7a, 0xfd2f7bd1, 0x020f4cec, 0xfead3bb2, 0x00bd1c63, 0xffa768e6, 0x001fca7d, 0xfff9fa4d, 0xfffdf8ae, 0x000274be, 0xfffef225,
+ 0x680d5698, 0xf2128fde, 0x02852cfc, 0x019581f9, 0xfcee0d33, 0x03404890, 0xfd39dfb4, 0x020b9b4c, 0xfead5e22, 0x00be67f6, 0xffa5e9b1, 0x0020f288, 0xfff945dc, 0xfffe51be, 0x00025214, 0xfffefbde,
+ 0x676ac1bb, 0xf18e1174, 0x02dd75ca, 0x015ace79, 0xfd133970, 0x032acc6d, 0xfd448dae, 0x0207acd4, 0xfeadabef, 0x00bf983d, 0xffa479a2, 0x00221344, 0xfff89465, 0xfffea9d2, 0x00022fa9, 0xffff0587,
+ 0x66c4367d, 0xf10d18bd, 0x0334aac4, 0x0120522f, 0xfd388ad1, 0x03150a3f, 0xfd4f83eb, 0x0203826c, 0xfeae24c1, 0x00c0ad48, 0xffa318c7, 0x00232c9d, 0xfff7e5f9, 0xffff00e1, 0x00020d84, 0xffff0f1f,
+ 0x6619c197, 0xf08fa82f, 0x038ac385, 0x00e6140f, 0xfd5dfc63, 0x02ff0538, 0xfd5ac08e, 0x01ff1d04, 0xfeaec838, 0x00c1a728, 0xffa1c72f, 0x00243e7f, 0xfff73aa7, 0xffff56e3, 0x0001eba8, 0xffff18a4,
+ 0x656b700a, 0xf015c1ee, 0x03dfb7dd, 0x00ac1af9, 0xfd838938, 0x02e8c08e, 0xfd6641b8, 0x01fa7d91, 0xfeaf95f2, 0x00c285f4, 0xffa084e3, 0x002548d9, 0xfff6927e, 0xffffabcd, 0x0001ca18, 0xffff2215,
+ 0x64b94f22, 0xef9f67cb, 0x04337fcb, 0x00726dbb, 0xfda92c63, 0x02d23f7a, 0xfd720581, 0x01f5a50d, 0xfeb08d86, 0x00c349c4, 0xff9f51eb, 0x00264b9a, 0xfff5ed8b, 0xffffff99, 0x0001a8da, 0xffff2b70,
+ 0x64036c6f, 0xef2c9b43, 0x04861383, 0x0039130c, 0xfdcee0ff, 0x02bb8537, 0xfd7e09fc, 0x01f0947a, 0xfeb1ae87, 0x00c3f2b6, 0xff9e2e50, 0x002746b2, 0xfff54bdc, 0x0000523d, 0x000187f0, 0xffff34b6,
+ 0x6349d5c9, 0xeebd5d81, 0x04d76b6b, 0x00001191, 0xfdf4a22a, 0x02a49505, 0xfd8a4d37, 0x01eb4cde, 0xfeb2f884, 0x00c480e9, 0xff9d1a14, 0x00283a12, 0xfff4ad7e, 0x0000a3b3, 0x0001675f, 0xffff3de3,
+ 0x628c994c, 0xee51af5f, 0x0527801d, 0xffc76fd5, 0xfe1a6b08, 0x028d7223, 0xfd96cd3d, 0x01e5cf44, 0xfeb46b07, 0x00c4f480, 0xff9c1539, 0x002925ae, 0xfff4127d, 0x0000f3f1, 0x00014729, 0xffff46f7,
+ 0x61cbc559, 0xede99165, 0x05764a68, 0xff8f344f, 0xfe4036c5, 0x02761fd3, 0xfda3880f, 0x01e01cbe, 0xfeb60596, 0x00c54da2, 0xff9b1fc1, 0x002a0979, 0xfff37ae4, 0x000142f1, 0x00012754, 0xffff4ff1,
+ 0x61076890, 0xed8503c7, 0x05c3c34e, 0xff576560, 0xfe660094, 0x025ea157, 0xfdb07bb0, 0x01da3661, 0xfeb7c7b0, 0x00c58c79, 0xff9a39a9, 0x002ae568, 0xfff2e6bf, 0x000190ac, 0x000107e1, 0xffff58d0,
+ 0x603f91d5, 0xed24066b, 0x060fe408, 0xff20094d, 0xfe8bc3ad, 0x0246f9f3, 0xfdbda61a, 0x01d41d4a, 0xfeb9b0d3, 0x00c5b132, 0xff9962ec, 0x002bb971, 0xfff25619, 0x0001dd1b, 0x0000e8d4, 0xffff6192,
+ 0x5f745049, 0xecc698e6, 0x065aa604, 0xfee92646, 0xfeb17b53, 0x022f2cea, 0xfdcb0546, 0x01cdd297, 0xfebbc078, 0x00c5bbfc, 0xff989b85, 0x002c858d, 0xfff1c8fa, 0x00022837, 0x0000ca30, 0xffff6a38,
+ 0x5ea5b34c, 0xec6cba79, 0x06a402e4, 0xfeb2c261, 0xfed722d0, 0x02173d81, 0xfdd89727, 0x01c7576d, 0xfebdf613, 0x00c5ad0a, 0xff97e36c, 0x002d49b4, 0xfff13f6c, 0x000271fa, 0x0000abf8, 0xffff72be,
+ 0x5dd3ca7a, 0xec166a19, 0x06ebf483, 0xfe7ce399, 0xfefcb57a, 0x01ff2ef9, 0xfde659af, 0x01c0acf5, 0xfec05114, 0x00c58494, 0xff973a96, 0x002e05df, 0xfff0b977, 0x0002ba5f, 0x00008e30, 0xffff7b26,
+ 0x5cfea5aa, 0xebc3a669, 0x073274f1, 0xfe478fd2, 0xff222eac, 0x01e70494, 0xfdf44acc, 0x01b9d45b, 0xfec2d0e8, 0x00c542d1, 0xff96a0f8, 0x002eba0a, 0xfff03724, 0x0003015f, 0x000070d9, 0xffff836d,
+ 0x5c2654ed, 0xeb746dbe, 0x07777e74, 0xfe12ccd1, 0xff4789d1, 0x01cec194, 0xfe026869, 0x01b2ced1, 0xfec574f9, 0x00c4e7fe, 0xff961684, 0x002f6630, 0xffefb87a, 0x000346f6, 0x000053f7, 0xffff8b93,
+ 0x5b4ae88d, 0xeb28be1f, 0x07bb0b8b, 0xfddea042, 0xff6cc25a, 0x01b66936, 0xfe10b06f, 0x01ab9d8b, 0xfec83caa, 0x00c47459, 0xff959b29, 0x00300a4f, 0xffef3d7f, 0x00038b1d, 0x0000378c, 0xffff9398,
+ 0x5a6c7108, 0xeae09544, 0x07fd16eb, 0xfdab0fb6, 0xff91d3c6, 0x019dfeb6, 0xfe1f20c5, 0x01a441c2, 0xfecb275e, 0x00c3e824, 0xff952ed7, 0x0030a665, 0xffeec63a, 0x0003cdd1, 0x00001b9a, 0xffff9b7a,
+ 0x598aff13, 0xea9bf097, 0x083d9b81, 0xfd7820a0, 0xffb6b99f, 0x0185854f, 0xfe2db74f, 0x019cbcb1, 0xfece3472, 0x00c343a4, 0xff94d178, 0x00313a72, 0xffee52b1, 0x00040f0d, 0x00000024, 0xffffa339,
+ 0x58a6a397, 0xea5acd38, 0x087c9471, 0xfd45d856, 0xffdb6f7c, 0x016d0037, 0xfe3c71f1, 0x01950f98, 0xfed16342, 0x00c2871f, 0xff9482f8, 0x0031c677, 0xffede2e7, 0x00044ecb, 0xffffe52d, 0xffffaad3,
+ 0x57bf6fae, 0xea1d27f7, 0x08b9fd18, 0xfd143c12, 0xfffff100, 0x015472a1, 0xfe4b4e8c, 0x018d3bb8, 0xfed4b325, 0x00c1b2e0, 0xff944340, 0x00324a74, 0xffed76e3, 0x00048d0a, 0xffffcab5, 0xffffb249,
+ 0x56d574a2, 0xe9e2fd5b, 0x08f5d10a, 0xfce350f0, 0x002439db, 0x013bdfbc, 0xfe5a4b03, 0x01854258, 0xfed82370, 0x00c0c731, 0xff941236, 0x0032c66e, 0xffed0ea7, 0x0004c9c4, 0xffffb0bf, 0xffffb99a,
+ 0x55e8c3ee, 0xe9ac49a0, 0x09300c14, 0xfcb31bec, 0x004845cc, 0x01234ab4, 0xfe696534, 0x017d24bf, 0xfedbb373, 0x00bfc463, 0xff93efbf, 0x00333a67, 0xffecaa36, 0x000504f6, 0xffff974d, 0xffffc0c5,
+ 0x54f96f37, 0xe97908b8, 0x0968aa3b, 0xfc83a1e5, 0x006c10a0, 0x010ab6b0, 0xfe789b01, 0x0174e437, 0xfedf627d, 0x00beaac6, 0xff93dbc0, 0x0033a665, 0xffec4994, 0x00053e9e, 0xffff7e61, 0xffffc7ca,
+ 0x54078851, 0xe9493649, 0x099fa7bb, 0xfc54e79a, 0x008f9631, 0x00f226d0, 0xfe87ea47, 0x016c820d, 0xfee32fdb, 0x00bd7aae, 0xff93d618, 0x00340a6d, 0xffebecc2, 0x000576b8, 0xffff65fc, 0xffffcea8,
+ 0x53132138, 0xe91ccdb5, 0x09d5010b, 0xfc26f1ad, 0x00b2d26b, 0x00d99e31, 0xfe9750e8, 0x0163ff90, 0xfee71ad4, 0x00bc3470, 0xff93deaa, 0x00346687, 0xffeb93c3, 0x0005ad41, 0xffff4e20, 0xffffd55f,
+ 0x521c4c10, 0xe8f3ca12, 0x0a08b2d9, 0xfbf9c49d, 0x00d5c147, 0x00c11feb, 0xfea6ccc3, 0x015b5e11, 0xfeeb22af, 0x00bad866, 0xff93f552, 0x0034babb, 0xffeb3e96, 0x0005e238, 0xffff36ce, 0xffffdbee,
+ 0x51231b26, 0xe8ce2631, 0x0a3aba09, 0xfbcd64ca, 0x00f85ecf, 0x00a8af0c, 0xfeb65bb9, 0x01529ee3, 0xfeef46b0, 0x00b966e9, 0xff9419ef, 0x00350711, 0xffeaed3c, 0x00061599, 0xffff2007, 0xffffe255,
+ 0x5027a0e9, 0xe8abdc9d, 0x0a6b13bc, 0xfba1d673, 0x011aa71d, 0x00904ea0, 0xfec5fbac, 0x0149c35a, 0xfef3861a, 0x00b7e055, 0xff944c5a, 0x00354b94, 0xffea9fb6, 0x00064764, 0xffff09ce, 0xffffe894,
+ 0x4f29efed, 0xe88ce79a, 0x0a99bd47, 0xfb771db9, 0x013c965b, 0x007801aa, 0xfed5aa7e, 0x0140cccb, 0xfef7e02a, 0x00b6450a, 0xff948c6e, 0x0035884f, 0xffea5602, 0x00067797, 0xfffef421, 0xffffeeaa,
+ 0x4e2a1ae8, 0xe871412a, 0x0ac6b43a, 0xfb4d3e97, 0x015e28c7, 0x005fcb26, 0xfee56614, 0x0137bc8f, 0xfefc541e, 0x00b49568, 0xff94da03, 0x0035bd4e, 0xffea1020, 0x0006a630, 0xfffedf04, 0xfffff498,
+ 0x4d2834b0, 0xe858e30a, 0x0af1f65d, 0xfb243cea, 0x017f5aad, 0x0047ae09, 0xfef52c54, 0x012e93fc, 0xff00e133, 0x00b2d1d1, 0xff9534f0, 0x0035ea9d, 0xffe9ce0d, 0x0006d32f, 0xfffeca76, 0xfffffa5d,
+ 0x4c245038, 0xe843c6b5, 0x0b1b81ad, 0xfafc1c6e, 0x01a0286c, 0x002fad3f, 0xff04fb25, 0x0125546c, 0xff0586a0, 0x00b0faaa, 0xff959d0a, 0x0036104b, 0xffe98fc8, 0x0006fe92, 0xfffeb678, 0xfffffff8,
+ 0x4b1e8091, 0xe831e563, 0x0b435462, 0xfad4e0b9, 0x01c08e78, 0x0017cbae, 0xff14d073, 0x011bff38, 0xff0a439e, 0x00af1059, 0xff961224, 0x00362e66, 0xffe9554c, 0x00072859, 0xfffea30b, 0x0000056a,
+ 0x4a16d8e5, 0xe823380d, 0x0b696ceb, 0xfaae8d43, 0x01e08952, 0x00000c33, 0xff24aa2a, 0x011295bb, 0xff0f1762, 0x00ad1346, 0xff969412, 0x003644fd, 0xffe91e99, 0x00075084, 0xfffe9030, 0x00000ab3,
+ 0x490d6c79, 0xe817b76c, 0x0b8dc9ed, 0xfa89255f, 0x02001593, 0xffe871a0, 0xff348639, 0x0109194f, 0xff140121, 0x00ab03da, 0xff9722a5, 0x00365422, 0xffe8eba8, 0x00077712, 0xfffe7de7, 0x00000fd2,
+ 0x48024ea7, 0xe80f5bfb, 0x0bb06a47, 0xfa64ac3f, 0x021f2fe5, 0xffd0fec1, 0xff446293, 0x00ff8b4f, 0xff19000e, 0x00a8e282, 0xff97bdac, 0x00365be6, 0xffe8bc77, 0x00079c04, 0xfffe6c2f, 0x000014c8,
+ 0x46f592e2, 0xe80a1df5, 0x0bd14d0b, 0xfa4124f2, 0x023dd505, 0xffb9b656, 0xff543d2e, 0x00f5ed15, 0xff1e135b, 0x00a6afa8, 0xff9864f6, 0x00365c5b, 0xffe89101, 0x0007bf5b, 0xfffe5b0b, 0x00001994,
+ 0x45e74cad, 0xe807f55b, 0x0bf07186, 0xfa1e9262, 0x025c01c5, 0xffa29b18, 0xff641402, 0x00ec3ffc, 0xff233a39, 0x00a46bbc, 0xff991851, 0x00365594, 0xffe8693f, 0x0007e116, 0xfffe4a79, 0x00001e37,
+ 0x44d78fa0, 0xe808d9f1, 0x0c0dd738, 0xf9fcf758, 0x0279b30b, 0xff8bafb3, 0xff73e50e, 0x00e2855d, 0xff2873d6, 0x00a2172d, 0xff99d789, 0x003647a5, 0xffe8452d, 0x00080137, 0xfffe3a79, 0x000022b1,
+ 0x43c66f62, 0xe80cc342, 0x0c297dd9, 0xf9dc567b, 0x0296e5d0, 0xff74f6cc, 0xff83ae52, 0x00d8be92, 0xff2dbf61, 0x009fb26c, 0xff9aa268, 0x003632a2, 0xffe824c5, 0x00081fbf, 0xfffe2b0d, 0x00002701,
+ 0x42b3ffa9, 0xe813a89f, 0x0c436557, 0xf9bcb24a, 0x02b39724, 0xff5e72fb, 0xff936dd2, 0x00ceecf5, 0xff331c08, 0x009d3deb, 0xff9b78ba, 0x003616a2, 0xffe807ff, 0x00083cb0, 0xfffe1c32, 0x00002b28,
+ 0x41a05437, 0xe81d8122, 0x0c5b8dd4, 0xf99e0d26, 0x02cfc429, 0xff4826cf, 0xffa3219a, 0x00c511dc, 0xff3888f8, 0x009aba1d, 0xff9c5a47, 0x0035f3b9, 0xffe7eed5, 0x0008580a, 0xfffe0dea, 0x00002f26,
+ 0x408b80d9, 0xe82a43ac, 0x0c71f7a9, 0xf980694a, 0x02eb6a18, 0xff3214c9, 0xffb2c7b6, 0x00bb2e9f, 0xff3e055d, 0x00982778, 0xff9d46d6, 0x0035ca00, 0xffe7d93f, 0x000871cf, 0xfffe0034, 0x000032fb,
+ 0x3f759967, 0xe839e6e9, 0x0c86a361, 0xf963c8cc, 0x03068640, 0xff1c3f63, 0xffc25e3b, 0x00b14493, 0xff439064, 0x0095866f, 0xff9e3e30, 0x0035998d, 0xffe7c735, 0x00088a02, 0xfffdf310, 0x000036a8,
+ 0x3e5eb1bd, 0xe84c6152, 0x0c9991be, 0xf9482da0, 0x03211603, 0xff06a907, 0xffd1e340, 0x00a7550c, 0xff492937, 0x0092d77b, 0xff9f4019, 0x00356279, 0xffe7b8af, 0x0008a0a5, 0xfffde67c, 0x00003a2d,
+ 0x3d46ddc1, 0xe861a92b, 0x0caac3b5, 0xf92d9997, 0x033b16dc, 0xfef15417, 0xffe154e3, 0x009d615d, 0xff4ecf02, 0x00901b11, 0xffa04c57, 0x003524dd, 0xffe7ada5, 0x0008b5ba, 0xfffdda79, 0x00003d89,
+ 0x3c2e315a, 0xe879b487, 0x0cba3a6d, 0xf9140e5e, 0x03548659, 0xfedc42e7, 0xfff0b148, 0x00936ad6, 0xff5480f0, 0x008d51ab, 0xffa162ae, 0x0034e0d3, 0xffe7a60d, 0x0008c944, 0xfffdcf05, 0x000040be,
+ 0x3b14c072, 0xe8947947, 0x0cc7f742, 0xf8fb8d7d, 0x036d621f, 0xfec777be, 0xfffff697, 0x008972c7, 0xff5a3e2c, 0x008a7bc1, 0xffa282e1, 0x00349674, 0xffe7a1de, 0x0008db46, 0xfffdc421, 0x000043cc,
+ 0x39fa9ef3, 0xe8b1ed1c, 0x0cd3fbc0, 0xf8e4185a, 0x0385a7eb, 0xfeb2f4d9, 0x000f22fe, 0x007f7a7c, 0xff6005e1, 0x008799cd, 0xffa3acb4, 0x003445dc, 0xffe7a10d, 0x0008ebc1, 0xfffdb9cb, 0x000046b2,
+ 0x38dfe0c6, 0xe8d2058b, 0x0cde49a8, 0xf8cdb036, 0x039d558e, 0xfe9ebc66, 0x001e34b4, 0x00758341, 0xff65d73a, 0x0084ac48, 0xffa4dfe8, 0x0033ef25, 0xffe7a391, 0x0008fabb, 0xfffdb002, 0x00004972,
+ 0x37c499d0, 0xe8f4b7e9, 0x0ce6e2ea, 0xf8b85631, 0x03b468f1, 0xfe8ad087, 0x002d29f3, 0x006b8e5c, 0xff6bb163, 0x0081b3af, 0xffa61c3e, 0x0033926d, 0xffe7a95f, 0x00090836, 0xfffda6c5, 0x00004c0b,
+ 0x36a8ddf3, 0xe919f961, 0x0cedc9a7, 0xf8a40b44, 0x03cae014, 0xfe773351, 0x003c00fd, 0x00619d15, 0xff719388, 0x007eb07b, 0xffa76176, 0x00332fcf, 0xffe7b26c, 0x00091435, 0xfffd9e13, 0x00004e7f,
+ 0x358cc109, 0xe941bef3, 0x0cf30031, 0xf890d048, 0x03e0b90d, 0xfe63e6cb, 0x004ab81b, 0x0057b0ae, 0xff777cd6, 0x007ba32a, 0xffa8af51, 0x0032c769, 0xffe7bead, 0x00091ebd, 0xfffd95eb, 0x000050cd,
+ 0x347056e3, 0xe96bfd76, 0x0cf6890a, 0xf87ea5f1, 0x03f5f20a, 0xfe50ecf0, 0x00594d9d, 0x004dca68, 0xff7d6c79, 0x00788c36, 0xffaa058d, 0x00325958, 0xffe7ce16, 0x000927d1, 0xfffd8e4d, 0x000052f7,
+ 0x3353b349, 0xe998a999, 0x0cf866e1, 0xf86d8cd1, 0x040a894e, 0xfe3e47ac, 0x0067bfd8, 0x0043eb7f, 0xff83619f, 0x00756c1d, 0xffab63ea, 0x0031e5ba, 0xffe7e09c, 0x00092f75, 0xfffd8735, 0x000054fc,
+ 0x3236e9f7, 0xe9c7b7e3, 0x0cf89c96, 0xf85d8555, 0x041e7d34, 0xfe2bf8de, 0x00760d2a, 0x003a152f, 0xff895b77, 0x0072435b, 0xffacca25, 0x00316cae, 0xffe7f631, 0x000935ad, 0xfffd80a4, 0x000056dd,
+ 0x311a0e9b, 0xe9f91cb9, 0x0cf72d34, 0xf84e8fc9, 0x0431cc31, 0xfe1a0256, 0x008433f9, 0x003048ae, 0xff8f5930, 0x006f126b, 0xffae37fd, 0x0030ee53, 0xffe80eca, 0x00093a7f, 0xfffd7a98, 0x0000589b,
+ 0x2ffd34d4, 0xea2ccc59, 0x0cf41bf7, 0xf840ac57, 0x044474ce, 0xfe0865d7, 0x009232b2, 0x0026872f, 0xff9559fb, 0x006bd9cd, 0xffafad2e, 0x00306ac8, 0xffe82a59, 0x00093ded, 0xfffd750f, 0x00005a36,
+ 0x2ee07030, 0xea62bae0, 0x0cef6c43, 0xf833db04, 0x045675ab, 0xfdf72515, 0x00a007c9, 0x001cd1e4, 0xff9b5d0a, 0x006899fb, 0xffb12976, 0x002fe22c, 0xffe848d3, 0x00093ffe, 0xfffd7008, 0x00005baf,
+ 0x2dc3d429, 0xea9adc49, 0x0ce921ab, 0xf8281bb6, 0x0467cd83, 0xfde641b7, 0x00adb1bb, 0x001329f7, 0xffa16190, 0x00655372, 0xffb2ac90, 0x002f54a1, 0xffe86a29, 0x000940b6, 0xfffd6b81, 0x00005d06,
+ 0x2ca77428, 0xead52471, 0x0ce13feb, 0xf81d6e2e, 0x04787b24, 0xfdd5bd53, 0x00bb2f0b, 0x00099093, 0xffa766c0, 0x006206b1, 0xffb4363a, 0x002ec246, 0xffe88e4d, 0x00094019, 0xfffd6779, 0x00005e3d,
+ 0x2b8b637b, 0xeb118714, 0x0cd7caec, 0xf813d20d, 0x04887d76, 0xfdc59972, 0x00c87e47, 0x000006db, 0xffad6bd0, 0x005eb431, 0xffb5c630, 0x002e2b3c, 0xffe8b532, 0x00093e2e, 0xfffd63ed, 0x00005f52,
+ 0x2a6fb55e, 0xeb4ff7d4, 0x0cccc6bc, 0xf80b46d3, 0x0497d378, 0xfdb5d78f, 0x00d59e03, 0xfff68df1, 0xffb36ff9, 0x005b5c71, 0xffb75c2c, 0x002d8fa4, 0xffe8decb, 0x00093af8, 0xfffd60dd, 0x00006048,
+ 0x29547ced, 0xeb906a35, 0x0cc03797, 0xf803cbdc, 0x04a67c41, 0xfda67913, 0x00e28cdd, 0xffed26f0, 0xffb97271, 0x0057ffec, 0xffb8f7ea, 0x002cefa1, 0xffe90b08, 0x0009367e, 0xfffd5e46, 0x0000611f,
+ 0x2839cd30, 0xebd2d1a1, 0x0cb221de, 0xf7fd6065, 0x04b476fe, 0xfd977f5d, 0x00ef497a, 0xffe3d2f2, 0xffbf7274, 0x00549f1c, 0xffba9927, 0x002c4b53, 0xffe939db, 0x000930c4, 0xfffd5c26, 0x000061d8,
+ 0x271fb90d, 0xec17216b, 0x0ca28a1a, 0xf7f8038c, 0x04c1c2f3, 0xfd88ebb9, 0x00fbd28a, 0xffda930a, 0xffc56f3e, 0x00513a7e, 0xffbc3f9d, 0x002ba2dc, 0xffe96b35, 0x000929d1, 0xfffd5a7c, 0x00006272,
+ 0x2606534e, 0xec5d4ccd, 0x0c9174fa, 0xf7f3b44b, 0x04ce5f7d, 0xfd7abf64, 0x010826c4, 0xffd16848, 0xffcb680e, 0x004dd28c, 0xffbdeb07, 0x002af65f, 0xffe99f08, 0x000921aa, 0xfffd5945, 0x000062f0,
+ 0x24edae9c, 0xeca546eb, 0x0c7ee754, 0xf7f0717e, 0x04da4c10, 0xfd6cfb8e, 0x011444e7, 0xffc853b6, 0xffd15c22, 0x004a67c0, 0xffbf9b21, 0x002a45fe, 0xffe9d545, 0x00091854, 0xfffd5880, 0x00006351,
+ 0x23d5dd81, 0xecef02d5, 0x0c6ae622, 0xf7ee39e2, 0x04e58836, 0xfd5fa157, 0x01202bbe, 0xffbf565a, 0xffd74abe, 0x0046fa93, 0xffc14fa5, 0x002991db, 0xffea0ddc, 0x00090dd6, 0xfffd582a, 0x00006396,
+ 0x22bef262, 0xed3a7388, 0x0c557681, 0xf7ed0c12, 0x04f01392, 0xfd52b1cf, 0x012bda1b, 0xffb67137, 0xffdd3325, 0x00438b7e, 0xffc3084f, 0x0028da1a, 0xffea48be, 0x00090236, 0xfffd5842, 0x000063c0,
+ 0x21a8ff7e, 0xed878bf0, 0x0c3e9db5, 0xf7ece68c, 0x04f9edda, 0xfd462df6, 0x01374eda, 0xffada547, 0xffe3149e, 0x00401af9, 0xffc4c4da, 0x00281edd, 0xffea85dc, 0x0008f57a, 0xfffd58c5, 0x000063d0,
+ 0x209416f2, 0xedd63ee5, 0x0c26611f, 0xf7edc7af, 0x050316e0, 0xfd3a16c0, 0x014288e0, 0xffa4f383, 0xffe8ee72, 0x003ca97b, 0xffc68502, 0x00276046, 0xffeac525, 0x0008e7a7, 0xfffd59b2, 0x000063c6,
+ 0x1f804ab0, 0xee267f35, 0x0c0cc646, 0xf7efadbd, 0x050b8e8a, 0xfd2e6d0d, 0x014d871b, 0xff9c5cdc, 0xffeebfec, 0x0039377a, 0xffc84881, 0x00269e7a, 0xffeb068a, 0x0008d8c4, 0xfffd5b05, 0x000063a3,
+ 0x1e6dac83, 0xee783f9e, 0x0bf1d2d0, 0xf7f296d7, 0x051354d5, 0xfd2331b0, 0x01584883, 0xff93e241, 0xfff48859, 0x0035c56c, 0xffca0f14, 0x0025d99b, 0xffeb49fc, 0x0008c8d7, 0xfffd5cbe, 0x00006368,
+ 0x1d5c4e09, 0xeecb72d1, 0x0bd58c81, 0xf7f68103, 0x051a69d4, 0xfd18656f, 0x0162cc19, 0xff8b8498, 0xfffa470a, 0x003253c6, 0xffcbd876, 0x002511cd, 0xffeb8f6a, 0x0008b7e7, 0xfffd5ed8, 0x00006316,
+ 0x1c4c40b6, 0xef200b76, 0x0bb7f940, 0xf7fb6a29, 0x0520cdb1, 0xfd0e08fb, 0x016d10e9, 0xff8344c4, 0xfffffb51, 0x002ee2fa, 0xffcda463, 0x00244733, 0xffebd6c4, 0x0008a5fa, 0xfffd6154, 0x000062ad,
+ 0x1b3d95d1, 0xef75fc2b, 0x0b991f0f, 0xf8015015, 0x052680ae, 0xfd041cfa, 0x01771608, 0xff7b23a1, 0x0005a483, 0x002b737b, 0xffcf7299, 0x002379ef, 0xffec1ffa, 0x00089316, 0xfffd642d, 0x0000622e,
+ 0x1a305e70, 0xefcd3787, 0x0b79040c, 0xf8083077, 0x052b8320, 0xfcfaa200, 0x0180da94, 0xff732209, 0x000b41fa, 0x002805ba, 0xffd142d3, 0x0022aa26, 0xffec6afc, 0x00087f43, 0xfffd6762, 0x0000619a,
+ 0x1924ab7b, 0xf025b01a, 0x0b57ae75, 0xf81008e2, 0x052fd573, 0xfcf19894, 0x018a5db5, 0xff6b40cb, 0x0010d30e, 0x00249a28, 0xffd314cf, 0x0021d7fa, 0xffecb7b9, 0x00086a86, 0xfffd6af1, 0x000060f1,
+ 0x181a8da5, 0xf07f586e, 0x0b3524a0, 0xf818d6cf, 0x0533782a, 0xfce9012c, 0x01939e9e, 0xff6380b5, 0x00165720, 0x00213134, 0xffd4e84a, 0x00210390, 0xffed0621, 0x000854e6, 0xfffd6ed6, 0x00006035,
+ 0x17121573, 0xf0da230b, 0x0b116cff, 0xf822979b, 0x05366bdc, 0xfce0dc2f, 0x019c9c8b, 0xff5be28d, 0x001bcd8e, 0x001dcb4a, 0xffd6bd01, 0x00202d09, 0xffed5624, 0x00083e6a, 0xfffd7310, 0x00005f66,
+ 0x160b5331, 0xf1360276, 0x0aec8e1c, 0xf82d488c, 0x0538b136, 0xfcd929f4, 0x01a556c1, 0xff546713, 0x002135bd, 0x001a68d8, 0xffd892b4, 0x001f5489, 0xffeda7b1, 0x00082718, 0xfffd779d, 0x00005e84,
+ 0x150656f8, 0xf192e932, 0x0ac68e9b, 0xf838e6c9, 0x053a48fa, 0xfcd1eac3, 0x01adcc91, 0xff4d0f02, 0x00268f13, 0x00170a47, 0xffda6921, 0x001e7a33, 0xffedfab8, 0x00080ef7, 0xfffd7c7a, 0x00005d92,
+ 0x140330a9, 0xf1f0c9c5, 0x0a9f7537, 0xf8456f65, 0x053b3400, 0xfccb1ed7, 0x01b5fd54, 0xff45db10, 0x002bd8fa, 0x0013b003, 0xffdc4007, 0x001d9e2a, 0xffee4f29, 0x0007f60f, 0xfffd81a4, 0x00005c8e,
+ 0x1301efed, 0xf24f96b5, 0x0a7748c0, 0xf852df56, 0x053b7332, 0xfcc4c658, 0x01bde86f, 0xff3ecbea, 0x003112e0, 0x00105a72, 0xffde1726, 0x001cc091, 0xffeea4f2, 0x0007dc65, 0xfffd8719, 0x00005b7b,
+ 0x1202a434, 0xf2af428c, 0x0a4e101f, 0xf861337c, 0x053b0791, 0xfcbee162, 0x01c58d50, 0xff37e23b, 0x00363c35, 0x000d09fc, 0xffdfee3f, 0x001be18a, 0xffeefc04, 0x0007c201, 0xfffd8cd7, 0x00005a58,
+ 0x11055cb4, 0xf30fbfd7, 0x0a23d24e, 0xf870689f, 0x0539f231, 0xfcb97001, 0x01cceb6e, 0xff311ea4, 0x003b546b, 0x0009bf05, 0xffe1c511, 0x001b0138, 0xffef544e, 0x0007a6e9, 0xfffd92db, 0x00005927,
+ 0x100a2864, 0xf371012c, 0x09f8965d, 0xf8807b70, 0x0538343a, 0xfcb47232, 0x01d4024c, 0xff2a81c4, 0x00405afa, 0x000679f2, 0xffe39b60, 0x001a1fbc, 0xffefadc0, 0x00078b24, 0xfffd9923, 0x000057e9,
+ 0x0f111603, 0xf3d2f926, 0x09cc636e, 0xf8916889, 0x0535cee9, 0xfcafe7e2, 0x01dad175, 0xff240c2f, 0x00454f5d, 0x00033b23, 0xffe570ed, 0x00193d3a, 0xfff00849, 0x00076eba, 0xfffd9fac, 0x0000569d,
+ 0x0e1a340d, 0xf4359a6a, 0x099f40b5, 0xf8a32c6e, 0x0532c38c, 0xfcabd0f2, 0x01e15880, 0xff1dbe77, 0x004a310f, 0x000002f9, 0xffe7457c, 0x001859d2, 0xfff063d9, 0x000751b0, 0xfffda675, 0x00005545,
+ 0x0d2590c3, 0xf498d7a5, 0x09713575, 0xf8b5c38d, 0x052f1386, 0xfca82d32, 0x01e7970e, 0xff179926, 0x004eff94, 0xfffcd1d3, 0xffe918ce, 0x001775a7, 0xfff0c060, 0x0007340d, 0xfffdad79, 0x000053e2,
+ 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9, 0x00005274,
+};
+}
diff --git a/services/audioflinger/roundup.c b/services/audioflinger/roundup.c
deleted file mode 100644
index 4f9af6aa..00000000
--- a/services/audioflinger/roundup.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 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 "roundup.h"
-
-unsigned roundup(unsigned v)
-{
- // __builtin_clz is undefined for zero input
- if (v == 0) {
- v = 1;
- }
- int lz = __builtin_clz((int) v);
- unsigned rounded = ((unsigned) 0x80000000) >> lz;
- // 0x800000001 and higher are actually rounded _down_ to prevent overflow
- if (v > rounded && lz > 0) {
- rounded <<= 1;
- }
- return rounded;
-}
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
new file mode 100644
index 00000000..7a314cfa
--- /dev/null
+++ b/services/audioflinger/test-resample.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 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 "AudioResampler.h"
+#include <media/AudioBufferProvider.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+
+using namespace android;
+
+struct HeaderWav {
+ HeaderWav(size_t size, int nc, int sr, int bits) {
+ strncpy(RIFF, "RIFF", 4);
+ chunkSize = size + sizeof(HeaderWav);
+ strncpy(WAVE, "WAVE", 4);
+ strncpy(fmt, "fmt ", 4);
+ fmtSize = 16;
+ audioFormat = 1;
+ numChannels = nc;
+ samplesRate = sr;
+ byteRate = sr * numChannels * (bits/8);
+ align = nc*(bits/8);
+ bitsPerSample = bits;
+ strncpy(data, "data", 4);
+ dataSize = size;
+ }
+
+ char RIFF[4]; // RIFF
+ uint32_t chunkSize; // File size
+ char WAVE[4]; // WAVE
+ char fmt[4]; // fmt\0
+ uint32_t fmtSize; // fmt size
+ uint16_t audioFormat; // 1=PCM
+ uint16_t numChannels; // num channels
+ uint32_t samplesRate; // sample rate in hz
+ uint32_t byteRate; // Bps
+ uint16_t align; // 2=16-bit mono, 4=16-bit stereo
+ uint16_t bitsPerSample; // bits per sample
+ char data[4]; // "data"
+ uint32_t dataSize; // size
+};
+
+static int usage(const char* name) {
+ fprintf(stderr,"Usage: %s [-p] [-h] [-s] [-q {dq|lq|mq|hq|vhq}] [-i input-sample-rate] "
+ "[-o output-sample-rate] [<input-file>] <output-file>\n", name);
+ fprintf(stderr," -p enable profiling\n");
+ fprintf(stderr," -h create wav file\n");
+ fprintf(stderr," -s stereo\n");
+ fprintf(stderr," -q resampler quality\n");
+ fprintf(stderr," dq : default quality\n");
+ fprintf(stderr," lq : low quality\n");
+ fprintf(stderr," mq : medium quality\n");
+ fprintf(stderr," hq : high quality\n");
+ fprintf(stderr," vhq : very high quality\n");
+ fprintf(stderr," -i input file sample rate\n");
+ fprintf(stderr," -o output file sample rate\n");
+ return -1;
+}
+
+int main(int argc, char* argv[]) {
+
+ const char* const progname = argv[0];
+ bool profiling = false;
+ bool writeHeader = false;
+ int channels = 1;
+ int input_freq = 0;
+ int output_freq = 0;
+ AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
+
+ int ch;
+ while ((ch = getopt(argc, argv, "phsq:i:o:")) != -1) {
+ switch (ch) {
+ case 'p':
+ profiling = true;
+ break;
+ case 'h':
+ writeHeader = true;
+ break;
+ case 's':
+ channels = 2;
+ break;
+ case 'q':
+ if (!strcmp(optarg, "dq"))
+ quality = AudioResampler::DEFAULT_QUALITY;
+ else if (!strcmp(optarg, "lq"))
+ quality = AudioResampler::LOW_QUALITY;
+ else if (!strcmp(optarg, "mq"))
+ quality = AudioResampler::MED_QUALITY;
+ else if (!strcmp(optarg, "hq"))
+ quality = AudioResampler::HIGH_QUALITY;
+ else if (!strcmp(optarg, "vhq"))
+ quality = AudioResampler::VERY_HIGH_QUALITY;
+ else {
+ usage(progname);
+ return -1;
+ }
+ break;
+ case 'i':
+ input_freq = atoi(optarg);
+ break;
+ case 'o':
+ output_freq = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage(progname);
+ return -1;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ const char* file_in = NULL;
+ const char* file_out = NULL;
+ if (argc == 1) {
+ file_out = argv[0];
+ } else if (argc == 2) {
+ file_in = argv[0];
+ file_out = argv[1];
+ } else {
+ usage(progname);
+ return -1;
+ }
+
+ // ----------------------------------------------------------
+
+ size_t input_size;
+ void* input_vaddr;
+ if (argc == 2) {
+ struct stat st;
+ if (stat(file_in, &st) < 0) {
+ fprintf(stderr, "stat: %s\n", strerror(errno));
+ return -1;
+ }
+
+ int input_fd = open(file_in, O_RDONLY);
+ if (input_fd < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return -1;
+ }
+
+ input_size = st.st_size;
+ input_vaddr = mmap(0, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
+ if (input_vaddr == MAP_FAILED ) {
+ fprintf(stderr, "mmap: %s\n", strerror(errno));
+ return -1;
+ }
+ } else {
+ double k = 1000; // Hz / s
+ double time = (input_freq / 2) / k;
+ size_t input_frames = size_t(input_freq * time);
+ input_size = channels * sizeof(int16_t) * input_frames;
+ input_vaddr = malloc(input_size);
+ int16_t* in = (int16_t*)input_vaddr;
+ for (size_t i=0 ; i<input_frames ; i++) {
+ double t = double(i) / input_freq;
+ double y = sin(M_PI * k * t * t);
+ int16_t yi = floor(y * 32767.0 + 0.5);
+ for (size_t j=0 ; j<(size_t)channels ; j++) {
+ in[i*channels + j] = yi / (1+j);
+ }
+ }
+ }
+
+ // ----------------------------------------------------------
+
+ class Provider: public AudioBufferProvider {
+ int16_t* mAddr;
+ size_t mNumFrames;
+ public:
+ Provider(const void* addr, size_t size, int channels) {
+ mAddr = (int16_t*) addr;
+ mNumFrames = size / (channels*sizeof(int16_t));
+ }
+ virtual status_t getNextBuffer(Buffer* buffer,
+ int64_t pts = kInvalidPTS) {
+ buffer->frameCount = mNumFrames;
+ buffer->i16 = mAddr;
+ return NO_ERROR;
+ }
+ virtual void releaseBuffer(Buffer* buffer) {
+ }
+ } provider(input_vaddr, input_size, channels);
+
+ size_t input_frames = input_size / (channels * sizeof(int16_t));
+ size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
+ output_size &= ~7; // always stereo, 32-bits
+
+ void* output_vaddr = malloc(output_size);
+
+ if (profiling) {
+ AudioResampler* resampler = AudioResampler::create(16, channels,
+ output_freq, quality);
+
+ size_t out_frames = output_size/8;
+ resampler->setSampleRate(input_freq);
+ resampler->setVolume(0x1000, 0x1000);
+
+ memset(output_vaddr, 0, output_size);
+ timespec start, end;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
+ int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
+ int64_t time = (end_ns - start_ns)/4;
+ printf("%f Mspl/s\n", out_frames/(time/1e9)/1e6);
+
+ delete resampler;
+ }
+
+ AudioResampler* resampler = AudioResampler::create(16, channels,
+ output_freq, quality);
+ size_t out_frames = output_size/8;
+ resampler->setSampleRate(input_freq);
+ resampler->setVolume(0x1000, 0x1000);
+
+ memset(output_vaddr, 0, output_size);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+
+ // down-mix (we just truncate and keep the left channel)
+ int32_t* out = (int32_t*) output_vaddr;
+ int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
+ for (size_t i = 0; i < out_frames; i++) {
+ for (int j=0 ; j<channels ; j++) {
+ int32_t s = out[i * 2 + j] >> 12;
+ if (s > 32767) s = 32767;
+ else if (s < -32768) s = -32768;
+ convert[i * channels + j] = int16_t(s);
+ }
+ }
+
+ // write output to disk
+ int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (output_fd < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (writeHeader) {
+ HeaderWav wav(out_frames * channels * sizeof(int16_t), channels, output_freq, 16);
+ write(output_fd, &wav, sizeof(wav));
+ }
+
+ write(output_fd, convert, out_frames * channels * sizeof(int16_t));
+ close(output_fd);
+
+ return 0;
+}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 3cae1f52..eff47c84 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -7,7 +7,20 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- CameraService.cpp
+ CameraService.cpp \
+ CameraClient.cpp \
+ Camera2Client.cpp \
+ Camera2Device.cpp \
+ camera2/CameraMetadata.cpp \
+ camera2/Parameters.cpp \
+ camera2/FrameProcessor.cpp \
+ camera2/StreamingProcessor.cpp \
+ camera2/JpegProcessor.cpp \
+ camera2/CallbackProcessor.cpp \
+ camera2/ZslProcessor.cpp \
+ camera2/BurstCapture.cpp \
+ camera2/JpegCompressor.cpp \
+ camera2/CaptureSequencer.cpp
LOCAL_SHARED_LIBRARIES:= \
libui \
@@ -18,7 +31,14 @@ LOCAL_SHARED_LIBRARIES:= \
libmedia_native \
libcamera_client \
libgui \
- libhardware
+ libhardware \
+ libsync \
+ libcamera_metadata \
+ libjpeg
+
+LOCAL_C_INCLUDES += \
+ system/media/camera/include \
+ external/jpeg
LOCAL_MODULE:= libcameraservice
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
new file mode 100644
index 00000000..30335773
--- /dev/null
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -0,0 +1,1636 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <cutils/properties.h>
+#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
+#include "camera2/Parameters.h"
+#include "Camera2Client.h"
+
+#define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
+#define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
+
+namespace android {
+using namespace camera2;
+
+static int getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+static int getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+// Interface used by CameraService
+
+Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid):
+ Client(cameraService, cameraClient,
+ cameraId, cameraFacing, clientPid, servicePid),
+ mSharedCameraClient(cameraClient),
+ mParameters(cameraId, cameraFacing)
+{
+ ATRACE_CALL();
+ ALOGI("Camera %d: Opened", cameraId);
+
+ mDevice = new Camera2Device(cameraId);
+
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.state = Parameters::DISCONNECTED;
+}
+
+status_t Camera2Client::checkPid(const char* checkLocation) const {
+ int callingPid = getCallingPid();
+ if (callingPid == mClientPid) return NO_ERROR;
+
+ ALOGE("%s: attempt to use a locked camera from a different process"
+ " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid);
+ return PERMISSION_DENIED;
+}
+
+status_t Camera2Client::initialize(camera_module_t *module)
+{
+ ATRACE_CALL();
+ ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
+ status_t res;
+
+ res = mDevice->initialize(module);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return NO_INIT;
+ }
+
+ res = mDevice->setNotifyCallback(this);
+
+ SharedParameters::Lock l(mParameters);
+
+ res = l.mParameters.initialize(&(mDevice->info()));
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return NO_INIT;
+ }
+
+ String8 threadName;
+
+ mStreamingProcessor = new StreamingProcessor(this);
+
+ mFrameProcessor = new FrameProcessor(this);
+ threadName = String8::format("C2-%d-FrameProc",
+ mCameraId);
+ mFrameProcessor->run(threadName.string());
+
+ mCaptureSequencer = new CaptureSequencer(this);
+ threadName = String8::format("C2-%d-CaptureSeq",
+ mCameraId);
+ mCaptureSequencer->run(threadName.string());
+
+ mJpegProcessor = new JpegProcessor(this, mCaptureSequencer);
+ threadName = String8::format("C2-%d-JpegProc",
+ mCameraId);
+ mJpegProcessor->run(threadName.string());
+
+ mZslProcessor = new ZslProcessor(this, mCaptureSequencer);
+ threadName = String8::format("C2-%d-ZslProc",
+ mCameraId);
+ mZslProcessor->run(threadName.string());
+
+ mCallbackProcessor = new CallbackProcessor(this);
+ threadName = String8::format("C2-%d-CallbkProc",
+ mCameraId);
+ mCallbackProcessor->run(threadName.string());
+
+ if (gLogLevel >= 1) {
+ ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__,
+ mCameraId);
+ ALOGD("%s", l.mParameters.paramsFlattened.string());
+ }
+
+ return OK;
+}
+
+Camera2Client::~Camera2Client() {
+ ATRACE_CALL();
+
+ mDestructionStarted = true;
+
+ disconnect();
+
+ ALOGI("Camera %d: Closed", mCameraId);
+}
+
+status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+ result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n",
+ mCameraId,
+ getCameraClient()->asBinder().get(),
+ mClientPid);
+ result.append(" State: ");
+#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
+
+ const Parameters& p = mParameters.unsafeAccess();
+
+ result.append(Parameters::getStateName(p.state));
+
+ result.append("\n Current parameters:\n");
+ result.appendFormat(" Preview size: %d x %d\n",
+ p.previewWidth, p.previewHeight);
+ result.appendFormat(" Preview FPS range: %d - %d\n",
+ p.previewFpsRange[0], p.previewFpsRange[1]);
+ result.appendFormat(" Preview HAL pixel format: 0x%x\n",
+ p.previewFormat);
+ result.appendFormat(" Preview transform: %x\n",
+ p.previewTransform);
+ result.appendFormat(" Picture size: %d x %d\n",
+ p.pictureWidth, p.pictureHeight);
+ result.appendFormat(" Jpeg thumbnail size: %d x %d\n",
+ p.jpegThumbSize[0], p.jpegThumbSize[1]);
+ result.appendFormat(" Jpeg quality: %d, thumbnail quality: %d\n",
+ p.jpegQuality, p.jpegThumbQuality);
+ result.appendFormat(" Jpeg rotation: %d\n", p.jpegRotation);
+ result.appendFormat(" GPS tags %s\n",
+ p.gpsEnabled ? "enabled" : "disabled");
+ if (p.gpsEnabled) {
+ result.appendFormat(" GPS lat x long x alt: %f x %f x %f\n",
+ p.gpsCoordinates[0], p.gpsCoordinates[1],
+ p.gpsCoordinates[2]);
+ result.appendFormat(" GPS timestamp: %lld\n",
+ p.gpsTimestamp);
+ result.appendFormat(" GPS processing method: %s\n",
+ p.gpsProcessingMethod.string());
+ }
+
+ result.append(" White balance mode: ");
+ switch (p.wbMode) {
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_AUTO)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_INCANDESCENT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_FLUORESCENT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_DAYLIGHT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_TWILIGHT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_SHADE)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Effect mode: ");
+ switch (p.effectMode) {
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_OFF)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_MONO)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_NEGATIVE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_SOLARIZE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_SEPIA)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_POSTERIZE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_AQUA)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Antibanding mode: ");
+ switch (p.antibandingMode) {
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Scene mode: ");
+ switch (p.sceneMode) {
+ case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED:
+ result.append("AUTO\n"); break;
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_ACTION)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PORTRAIT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_LANDSCAPE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_NIGHT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_THEATRE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_BEACH)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SNOW)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SUNSET)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_FIREWORKS)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SPORTS)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PARTY)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_BARCODE)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Flash mode: ");
+ switch (p.flashMode) {
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_OFF)
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_AUTO)
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_ON)
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_TORCH)
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_RED_EYE)
+ CASE_APPEND_ENUM(Parameters::FLASH_MODE_INVALID)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Focus mode: ");
+ switch (p.focusMode) {
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_AUTO)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_MACRO)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_CONTINUOUS_VIDEO)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_CONTINUOUS_PICTURE)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_EDOF)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_INFINITY)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_FIXED)
+ CASE_APPEND_ENUM(Parameters::FOCUS_MODE_INVALID)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Focus state: ");
+ switch (p.focusState) {
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_INACTIVE)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED)
+ CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED)
+ default: result.append("UNKNOWN\n");
+ }
+
+ result.append(" Focusing areas:\n");
+ for (size_t i = 0; i < p.focusingAreas.size(); i++) {
+ result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n",
+ p.focusingAreas[i].left,
+ p.focusingAreas[i].top,
+ p.focusingAreas[i].right,
+ p.focusingAreas[i].bottom,
+ p.focusingAreas[i].weight);
+ }
+
+ result.appendFormat(" Exposure compensation index: %d\n",
+ p.exposureCompensation);
+
+ result.appendFormat(" AE lock %s, AWB lock %s\n",
+ p.autoExposureLock ? "enabled" : "disabled",
+ p.autoWhiteBalanceLock ? "enabled" : "disabled" );
+
+ result.appendFormat(" Metering areas:\n");
+ for (size_t i = 0; i < p.meteringAreas.size(); i++) {
+ result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n",
+ p.meteringAreas[i].left,
+ p.meteringAreas[i].top,
+ p.meteringAreas[i].right,
+ p.meteringAreas[i].bottom,
+ p.meteringAreas[i].weight);
+ }
+
+ result.appendFormat(" Zoom index: %d\n", p.zoom);
+ result.appendFormat(" Video size: %d x %d\n", p.videoWidth,
+ p.videoHeight);
+
+ result.appendFormat(" Recording hint is %s\n",
+ p.recordingHint ? "set" : "not set");
+
+ result.appendFormat(" Video stabilization is %s\n",
+ p.videoStabilization ? "enabled" : "disabled");
+
+ result.append(" Current streams:\n");
+ result.appendFormat(" Preview stream ID: %d\n",
+ getPreviewStreamId());
+ result.appendFormat(" Capture stream ID: %d\n",
+ getCaptureStreamId());
+ result.appendFormat(" Recording stream ID: %d\n",
+ getRecordingStreamId());
+
+ result.append(" Quirks for this camera:\n");
+ bool haveQuirk = false;
+ if (p.quirks.triggerAfWithAuto) {
+ result.appendFormat(" triggerAfWithAuto\n");
+ haveQuirk = true;
+ }
+ if (p.quirks.useZslFormat) {
+ result.appendFormat(" useZslFormat\n");
+ haveQuirk = true;
+ }
+ if (p.quirks.meteringCropRegion) {
+ result.appendFormat(" meteringCropRegion\n");
+ haveQuirk = true;
+ }
+ if (!haveQuirk) {
+ result.appendFormat(" none\n");
+ }
+
+ write(fd, result.string(), result.size());
+
+ mStreamingProcessor->dump(fd, args);
+
+ mCaptureSequencer->dump(fd, args);
+
+ mFrameProcessor->dump(fd, args);
+
+ mZslProcessor->dump(fd, args);
+
+ result = " Device dump:\n";
+ write(fd, result.string(), result.size());
+
+ status_t res = mDevice->dump(fd, args);
+ if (res != OK) {
+ result = String8::format(" Error dumping device: %s (%d)",
+ strerror(-res), res);
+ write(fd, result.string(), result.size());
+ }
+
+#undef CASE_APPEND_ENUM
+ return NO_ERROR;
+}
+
+// ICamera interface
+
+void Camera2Client::disconnect() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+
+ // Allow both client and the media server to disconnect at all times
+ int callingPid = getCallingPid();
+ if (callingPid != mClientPid && callingPid != mServicePid) return;
+
+ if (mDevice == 0) return;
+
+ ALOGV("Camera %d: Shutting down", mCameraId);
+
+ stopPreviewL();
+
+ {
+ SharedParameters::Lock l(mParameters);
+ if (l.mParameters.state == Parameters::DISCONNECTED) return;
+ l.mParameters.state = Parameters::DISCONNECTED;
+ }
+
+ mStreamingProcessor->deletePreviewStream();
+ mStreamingProcessor->deleteRecordingStream();
+ mJpegProcessor->deleteStream();
+ mCallbackProcessor->deleteStream();
+ mZslProcessor->deleteStream();
+
+ mFrameProcessor->requestExit();
+ mCaptureSequencer->requestExit();
+ mJpegProcessor->requestExit();
+ mZslProcessor->requestExit();
+ mCallbackProcessor->requestExit();
+
+ ALOGV("Camera %d: Waiting for threads", mCameraId);
+
+ mFrameProcessor->join();
+ mCaptureSequencer->join();
+ mJpegProcessor->join();
+ mZslProcessor->join();
+ mCallbackProcessor->join();
+
+ ALOGV("Camera %d: Disconnecting device", mCameraId);
+
+ mDevice->disconnect();
+
+ mDevice.clear();
+
+ CameraService::Client::disconnect();
+}
+
+status_t Camera2Client::connect(const sp<ICameraClient>& client) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+
+ if (mClientPid != 0 && getCallingPid() != mClientPid) {
+ ALOGE("%s: Camera %d: Connection attempt from pid %d; "
+ "current locked to pid %d", __FUNCTION__,
+ mCameraId, getCallingPid(), mClientPid);
+ return BAD_VALUE;
+ }
+
+ mClientPid = getCallingPid();
+
+ mCameraClient = client;
+ mSharedCameraClient = client;
+
+ return OK;
+}
+
+status_t Camera2Client::lock() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d: Lock call from pid %d; current client pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+
+ if (mClientPid == 0) {
+ mClientPid = getCallingPid();
+ return OK;
+ }
+
+ if (mClientPid != getCallingPid()) {
+ ALOGE("%s: Camera %d: Lock call from pid %d; currently locked to pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+ return EBUSY;
+ }
+
+ return OK;
+}
+
+status_t Camera2Client::unlock() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d: Unlock call from pid %d; current client pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+
+ if (mClientPid == getCallingPid()) {
+ SharedParameters::Lock l(mParameters);
+ if (l.mParameters.state == Parameters::RECORD ||
+ l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
+ ALOGD("Not allowed to unlock camera during recording.");
+ return INVALID_OPERATION;
+ }
+ mClientPid = 0;
+ mCameraClient.clear();
+ mSharedCameraClient.clear();
+ return OK;
+ }
+
+ ALOGE("%s: Camera %d: Unlock call from pid %d; currently locked to pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+ return EBUSY;
+}
+
+status_t Camera2Client::setPreviewDisplay(
+ const sp<Surface>& surface) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ sp<IBinder> binder;
+ sp<ANativeWindow> window;
+ if (surface != 0) {
+ binder = surface->asBinder();
+ window = surface;
+ }
+
+ return setPreviewWindowL(binder,window);
+}
+
+status_t Camera2Client::setPreviewTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ sp<IBinder> binder;
+ sp<ANativeWindow> window;
+ if (surfaceTexture != 0) {
+ binder = surfaceTexture->asBinder();
+ window = new SurfaceTextureClient(surfaceTexture);
+ }
+ return setPreviewWindowL(binder, window);
+}
+
+status_t Camera2Client::setPreviewWindowL(const sp<IBinder>& binder,
+ sp<ANativeWindow> window) {
+ ATRACE_CALL();
+ status_t res;
+
+ if (binder == mPreviewSurface) {
+ ALOGV("%s: Camera %d: New window is same as old window",
+ __FUNCTION__, mCameraId);
+ return NO_ERROR;
+ }
+
+ Parameters::State state;
+ {
+ SharedParameters::Lock l(mParameters);
+ state = l.mParameters.state;
+ }
+ switch (state) {
+ case Parameters::DISCONNECTED:
+ case Parameters::RECORD:
+ case Parameters::STILL_CAPTURE:
+ case Parameters::VIDEO_SNAPSHOT:
+ ALOGE("%s: Camera %d: Cannot set preview display while in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(state));
+ return INVALID_OPERATION;
+ case Parameters::STOPPED:
+ case Parameters::WAITING_FOR_PREVIEW_WINDOW:
+ // OK
+ break;
+ case Parameters::PREVIEW:
+ // Already running preview - need to stop and create a new stream
+ mStreamingProcessor->stopStream();
+ state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
+ break;
+ }
+
+ mPreviewSurface = binder;
+ res = mStreamingProcessor->setPreviewWindow(window);
+ if (res != OK) {
+ ALOGE("%s: Unable to set new preview window: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ if (state == Parameters::WAITING_FOR_PREVIEW_WINDOW) {
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.state = state;
+ return startPreviewL(l.mParameters, false);
+ }
+
+ return OK;
+}
+
+void Camera2Client::setPreviewCallbackFlag(int flag) {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d: Flag 0x%x", __FUNCTION__, mCameraId, flag);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( checkPid(__FUNCTION__) != OK) return;
+
+ SharedParameters::Lock l(mParameters);
+ setPreviewCallbackFlagL(l.mParameters, flag);
+}
+
+void Camera2Client::setPreviewCallbackFlagL(Parameters &params, int flag) {
+ status_t res = OK;
+ if (flag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) {
+ ALOGV("%s: setting oneshot", __FUNCTION__);
+ params.previewCallbackOneShot = true;
+ }
+ if (params.previewCallbackFlags != (uint32_t)flag) {
+ params.previewCallbackFlags = flag;
+ switch(params.state) {
+ case Parameters::PREVIEW:
+ res = startPreviewL(params, true);
+ break;
+ case Parameters::RECORD:
+ case Parameters::VIDEO_SNAPSHOT:
+ res = startRecordingL(params, true);
+ break;
+ default:
+ break;
+ }
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to refresh request in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(params.state));
+ }
+ }
+
+}
+
+status_t Camera2Client::startPreview() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+ SharedParameters::Lock l(mParameters);
+ return startPreviewL(l.mParameters, false);
+}
+
+status_t Camera2Client::startPreviewL(Parameters &params, bool restart) {
+ ATRACE_CALL();
+ status_t res;
+
+ ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+
+ if ( (params.state == Parameters::PREVIEW ||
+ params.state == Parameters::RECORD ||
+ params.state == Parameters::VIDEO_SNAPSHOT)
+ && !restart) {
+ // Succeed attempt to re-enter a streaming state
+ ALOGI("%s: Camera %d: Preview already active, ignoring restart",
+ __FUNCTION__, mCameraId);
+ return OK;
+ }
+ if (params.state > Parameters::PREVIEW && !restart) {
+ ALOGE("%s: Can't start preview in state %s",
+ __FUNCTION__,
+ Parameters::getStateName(params.state));
+ return INVALID_OPERATION;
+ }
+
+ if (!mStreamingProcessor->haveValidPreviewWindow()) {
+ params.state = Parameters::WAITING_FOR_PREVIEW_WINDOW;
+ return OK;
+ }
+ params.state = Parameters::STOPPED;
+
+ res = mStreamingProcessor->updatePreviewStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ Vector<uint8_t> outputStreams;
+ bool callbacksEnabled = params.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
+ if (callbacksEnabled) {
+ res = mCallbackProcessor->updateStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ outputStreams.push(getCallbackStreamId());
+ }
+ if (params.zslMode && !params.recordingHint) {
+ res = mZslProcessor->updateStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update ZSL stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ outputStreams.push(getZslStreamId());
+ }
+
+ outputStreams.push(getPreviewStreamId());
+
+ if (!params.recordingHint) {
+ if (!restart) {
+ res = mStreamingProcessor->updatePreviewRequest(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't set up preview request: "
+ "%s (%d)", __FUNCTION__, mCameraId,
+ strerror(-res), res);
+ return res;
+ }
+ }
+ res = mStreamingProcessor->startStream(StreamingProcessor::PREVIEW,
+ outputStreams);
+ } else {
+ // With recording hint set, we're going to be operating under the
+ // assumption that the user will record video. To optimize recording
+ // startup time, create the necessary output streams for recording and
+ // video snapshot now if they don't already exist.
+ res = mJpegProcessor->updateStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't pre-configure still image "
+ "stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ if (!restart) {
+ res = mStreamingProcessor->updateRecordingRequest(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't set up preview request with "
+ "record hint: %s (%d)", __FUNCTION__, mCameraId,
+ strerror(-res), res);
+ return res;
+ }
+ }
+ res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
+ outputStreams);
+ }
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to start streaming preview: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ params.state = Parameters::PREVIEW;
+ return OK;
+}
+
+void Camera2Client::stopPreview() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return;
+ stopPreviewL();
+}
+
+void Camera2Client::stopPreviewL() {
+ ATRACE_CALL();
+ status_t res;
+ const nsecs_t kStopCaptureTimeout = 3000000000LL; // 3 seconds
+ Parameters::State state;
+ {
+ SharedParameters::Lock l(mParameters);
+ state = l.mParameters.state;
+ }
+
+ switch (state) {
+ case Parameters::DISCONNECTED:
+ ALOGE("%s: Camera %d: Call before initialized",
+ __FUNCTION__, mCameraId);
+ break;
+ case Parameters::STOPPED:
+ case Parameters::VIDEO_SNAPSHOT:
+ case Parameters::STILL_CAPTURE:
+ mCaptureSequencer->waitUntilIdle(kStopCaptureTimeout);
+ // no break
+ case Parameters::RECORD:
+ case Parameters::PREVIEW:
+ mStreamingProcessor->stopStream();
+ res = mDevice->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ // no break
+ case Parameters::WAITING_FOR_PREVIEW_WINDOW: {
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.state = Parameters::STOPPED;
+ commandStopFaceDetectionL(l.mParameters);
+ break;
+ }
+ default:
+ ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId,
+ state);
+ }
+}
+
+bool Camera2Client::previewEnabled() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return false;
+
+ SharedParameters::Lock l(mParameters);
+ return l.mParameters.state == Parameters::PREVIEW;
+}
+
+status_t Camera2Client::storeMetaDataInBuffers(bool enabled) {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ SharedParameters::Lock l(mParameters);
+ switch (l.mParameters.state) {
+ case Parameters::RECORD:
+ case Parameters::VIDEO_SNAPSHOT:
+ ALOGE("%s: Camera %d: Can't be called in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(l.mParameters.state));
+ return INVALID_OPERATION;
+ default:
+ // OK
+ break;
+ }
+
+ l.mParameters.storeMetadataInBuffers = enabled;
+
+ return OK;
+}
+
+status_t Camera2Client::startRecording() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+ SharedParameters::Lock l(mParameters);
+
+ return startRecordingL(l.mParameters, false);
+}
+
+status_t Camera2Client::startRecordingL(Parameters &params, bool restart) {
+ status_t res;
+
+ ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+
+ switch (params.state) {
+ case Parameters::STOPPED:
+ res = startPreviewL(params, false);
+ if (res != OK) return res;
+ break;
+ case Parameters::PREVIEW:
+ // Ready to go
+ break;
+ case Parameters::RECORD:
+ case Parameters::VIDEO_SNAPSHOT:
+ // OK to call this when recording is already on, just skip unless
+ // we're looking to restart
+ if (!restart) return OK;
+ break;
+ default:
+ ALOGE("%s: Camera %d: Can't start recording in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(params.state));
+ return INVALID_OPERATION;
+ };
+
+ if (!params.storeMetadataInBuffers) {
+ ALOGE("%s: Camera %d: Recording only supported in metadata mode, but "
+ "non-metadata recording mode requested!", __FUNCTION__,
+ mCameraId);
+ return INVALID_OPERATION;
+ }
+
+ if (!restart) {
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+ mStreamingProcessor->updateRecordingRequest(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = mStreamingProcessor->updateRecordingStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ Vector<uint8_t> outputStreams;
+ bool callbacksEnabled = params.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
+ if (callbacksEnabled) {
+ res = mCallbackProcessor->updateStream(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ outputStreams.push(getCallbackStreamId());
+ }
+ outputStreams.push(getPreviewStreamId());
+ outputStreams.push(getRecordingStreamId());
+
+ res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
+ outputStreams);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to start recording stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ if (params.state < Parameters::RECORD) {
+ params.state = Parameters::RECORD;
+ }
+
+ return OK;
+}
+
+void Camera2Client::stopRecording() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mICameraLock);
+ SharedParameters::Lock l(mParameters);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return;
+
+ switch (l.mParameters.state) {
+ case Parameters::RECORD:
+ // OK to stop
+ break;
+ case Parameters::STOPPED:
+ case Parameters::PREVIEW:
+ case Parameters::STILL_CAPTURE:
+ case Parameters::VIDEO_SNAPSHOT:
+ default:
+ ALOGE("%s: Camera %d: Can't stop recording in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(l.mParameters.state));
+ return;
+ };
+
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+
+ res = startPreviewL(l.mParameters, true);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to return to preview",
+ __FUNCTION__, mCameraId);
+ }
+}
+
+bool Camera2Client::recordingEnabled() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+
+ if ( checkPid(__FUNCTION__) != OK) return false;
+
+ return recordingEnabledL();
+}
+
+bool Camera2Client::recordingEnabledL() {
+ ATRACE_CALL();
+ SharedParameters::Lock l(mParameters);
+
+ return (l.mParameters.state == Parameters::RECORD
+ || l.mParameters.state == Parameters::VIDEO_SNAPSHOT);
+}
+
+void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ if ( checkPid(__FUNCTION__) != OK) return;
+
+ mStreamingProcessor->releaseRecordingFrame(mem);
+}
+
+status_t Camera2Client::autoFocus() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d", __FUNCTION__, mCameraId);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ int triggerId;
+ bool notifyImmediately = false;
+ bool notifySuccess = false;
+ {
+ SharedParameters::Lock l(mParameters);
+ if (l.mParameters.state < Parameters::PREVIEW) {
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * If the camera does not support auto-focus, it is a no-op and
+ * onAutoFocus(boolean, Camera) callback will be called immediately
+ * with a fake value of success set to true.
+ */
+ if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
+ notifyImmediately = true;
+ notifySuccess = true;
+ }
+ /**
+ * If we're in CAF mode, and AF has already been locked, just fire back
+ * the callback right away; the HAL would not send a notification since
+ * no state change would happen on a AF trigger.
+ */
+ if ( (l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_PICTURE ||
+ l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_VIDEO) &&
+ l.mParameters.focusState == ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED ) {
+ notifyImmediately = true;
+ notifySuccess = true;
+ }
+ /**
+ * Send immediate notification back to client
+ */
+ if (notifyImmediately) {
+ SharedCameraClient::Lock l(mSharedCameraClient);
+ if (l.mCameraClient != 0) {
+ l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS,
+ notifySuccess ? 1 : 0, 0);
+ }
+ return OK;
+ }
+ /**
+ * Handle quirk mode for AF in scene modes
+ */
+ if (l.mParameters.quirks.triggerAfWithAuto &&
+ l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED &&
+ l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO &&
+ !l.mParameters.focusingAreas[0].isEmpty()) {
+ ALOGV("%s: Quirk: Switching from focusMode %d to AUTO",
+ __FUNCTION__, l.mParameters.focusMode);
+ l.mParameters.shadowFocusMode = l.mParameters.focusMode;
+ l.mParameters.focusMode = Parameters::FOCUS_MODE_AUTO;
+ updateRequests(l.mParameters);
+ }
+
+ l.mParameters.currentAfTriggerId = ++l.mParameters.afTriggerCounter;
+ triggerId = l.mParameters.currentAfTriggerId;
+ }
+ syncWithDevice();
+
+ mDevice->triggerAutofocus(triggerId);
+
+ return OK;
+}
+
+status_t Camera2Client::cancelAutoFocus() {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d", __FUNCTION__, mCameraId);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ int triggerId;
+ {
+ SharedParameters::Lock l(mParameters);
+ triggerId = ++l.mParameters.afTriggerCounter;
+
+ // When using triggerAfWithAuto quirk, may need to reset focus mode to
+ // the real state at this point. No need to cancel explicitly if
+ // changing the AF mode.
+ if (l.mParameters.shadowFocusMode != Parameters::FOCUS_MODE_INVALID) {
+ ALOGV("%s: Quirk: Restoring focus mode to %d", __FUNCTION__,
+ l.mParameters.shadowFocusMode);
+ l.mParameters.focusMode = l.mParameters.shadowFocusMode;
+ l.mParameters.shadowFocusMode = Parameters::FOCUS_MODE_INVALID;
+ updateRequests(l.mParameters);
+
+ return OK;
+ }
+ }
+ syncWithDevice();
+
+ mDevice->triggerCancelAutofocus(triggerId);
+
+ return OK;
+}
+
+status_t Camera2Client::takePicture(int msgType) {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ {
+ SharedParameters::Lock l(mParameters);
+ switch (l.mParameters.state) {
+ case Parameters::DISCONNECTED:
+ case Parameters::STOPPED:
+ case Parameters::WAITING_FOR_PREVIEW_WINDOW:
+ ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
+ __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ case Parameters::PREVIEW:
+ // Good to go for takePicture
+ res = commandStopFaceDetectionL(l.mParameters);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
+ __FUNCTION__, mCameraId);
+ return res;
+ }
+ l.mParameters.state = Parameters::STILL_CAPTURE;
+ break;
+ case Parameters::RECORD:
+ // Good to go for video snapshot
+ l.mParameters.state = Parameters::VIDEO_SNAPSHOT;
+ break;
+ case Parameters::STILL_CAPTURE:
+ case Parameters::VIDEO_SNAPSHOT:
+ ALOGE("%s: Camera %d: Already taking a picture",
+ __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+
+ ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
+
+ res = mJpegProcessor->updateStream(l.mParameters);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Need HAL to have correct settings before (possibly) triggering precapture
+ syncWithDevice();
+
+ res = mCaptureSequencer->startCapture(msgType);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to start capture: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+
+ return res;
+}
+
+status_t Camera2Client::setParameters(const String8& params) {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d", __FUNCTION__, mCameraId);
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ SharedParameters::Lock l(mParameters);
+
+ res = l.mParameters.set(params);
+ if (res != OK) return res;
+
+ res = updateRequests(l.mParameters);
+
+ return res;
+}
+
+String8 Camera2Client::getParameters() const {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d", __FUNCTION__, mCameraId);
+ Mutex::Autolock icl(mICameraLock);
+ if ( checkPid(__FUNCTION__) != OK) return String8();
+
+ SharedParameters::ReadLock l(mParameters);
+
+ return l.mParameters.get();
+}
+
+status_t Camera2Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
+ ATRACE_CALL();
+ Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ ALOGV("%s: Camera %d: Command %d (%d, %d)", __FUNCTION__, mCameraId,
+ cmd, arg1, arg2);
+
+ switch (cmd) {
+ case CAMERA_CMD_START_SMOOTH_ZOOM:
+ return commandStartSmoothZoomL();
+ case CAMERA_CMD_STOP_SMOOTH_ZOOM:
+ return commandStopSmoothZoomL();
+ case CAMERA_CMD_SET_DISPLAY_ORIENTATION:
+ return commandSetDisplayOrientationL(arg1);
+ case CAMERA_CMD_ENABLE_SHUTTER_SOUND:
+ return commandEnableShutterSoundL(arg1 == 1);
+ case CAMERA_CMD_PLAY_RECORDING_SOUND:
+ return commandPlayRecordingSoundL();
+ case CAMERA_CMD_START_FACE_DETECTION:
+ return commandStartFaceDetectionL(arg1);
+ case CAMERA_CMD_STOP_FACE_DETECTION: {
+ SharedParameters::Lock l(mParameters);
+ return commandStopFaceDetectionL(l.mParameters);
+ }
+ case CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG:
+ return commandEnableFocusMoveMsgL(arg1 == 1);
+ case CAMERA_CMD_PING:
+ return commandPingL();
+ case CAMERA_CMD_SET_VIDEO_BUFFER_COUNT:
+ return commandSetVideoBufferCountL(arg1);
+ default:
+ ALOGE("%s: Unknown command %d (arguments %d, %d)",
+ __FUNCTION__, cmd, arg1, arg2);
+ return BAD_VALUE;
+ }
+}
+
+status_t Camera2Client::commandStartSmoothZoomL() {
+ ALOGE("%s: Unimplemented!", __FUNCTION__);
+ return OK;
+}
+
+status_t Camera2Client::commandStopSmoothZoomL() {
+ ALOGE("%s: Unimplemented!", __FUNCTION__);
+ return OK;
+}
+
+status_t Camera2Client::commandSetDisplayOrientationL(int degrees) {
+ int transform = Parameters::degToTransform(degrees,
+ mCameraFacing == CAMERA_FACING_FRONT);
+ if (transform == -1) {
+ ALOGE("%s: Camera %d: Error setting %d as display orientation value",
+ __FUNCTION__, mCameraId, degrees);
+ return BAD_VALUE;
+ }
+ SharedParameters::Lock l(mParameters);
+ if (transform != l.mParameters.previewTransform &&
+ getPreviewStreamId() != NO_STREAM) {
+ mDevice->setStreamTransform(getPreviewStreamId(), transform);
+ }
+ l.mParameters.previewTransform = transform;
+ return OK;
+}
+
+status_t Camera2Client::commandEnableShutterSoundL(bool enable) {
+ SharedParameters::Lock l(mParameters);
+ if (enable) {
+ l.mParameters.playShutterSound = true;
+ return OK;
+ }
+
+ // Disabling shutter sound may not be allowed. In that case only
+ // allow the mediaserver process to disable the sound.
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.camera.sound.forced", value, "0");
+ if (strncmp(value, "0", 2) != 0) {
+ // Disabling shutter sound is not allowed. Deny if the current
+ // process is not mediaserver.
+ if (getCallingPid() != getpid()) {
+ ALOGE("Failed to disable shutter sound. Permission denied (pid %d)",
+ getCallingPid());
+ return PERMISSION_DENIED;
+ }
+ }
+
+ l.mParameters.playShutterSound = false;
+ return OK;
+}
+
+status_t Camera2Client::commandPlayRecordingSoundL() {
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+ return OK;
+}
+
+status_t Camera2Client::commandStartFaceDetectionL(int type) {
+ ALOGV("%s: Camera %d: Starting face detection",
+ __FUNCTION__, mCameraId);
+ status_t res;
+ SharedParameters::Lock l(mParameters);
+ switch (l.mParameters.state) {
+ case Parameters::DISCONNECTED:
+ case Parameters::STOPPED:
+ case Parameters::WAITING_FOR_PREVIEW_WINDOW:
+ case Parameters::STILL_CAPTURE:
+ ALOGE("%s: Camera %d: Cannot start face detection without preview active",
+ __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ case Parameters::PREVIEW:
+ case Parameters::RECORD:
+ case Parameters::VIDEO_SNAPSHOT:
+ // Good to go for starting face detect
+ break;
+ }
+ // Ignoring type
+ if (l.mParameters.fastInfo.bestFaceDetectMode ==
+ ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) {
+ ALOGE("%s: Camera %d: Face detection not supported",
+ __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+ if (l.mParameters.enableFaceDetect) return OK;
+
+ l.mParameters.enableFaceDetect = true;
+
+ res = updateRequests(l.mParameters);
+
+ return res;
+}
+
+status_t Camera2Client::commandStopFaceDetectionL(Parameters &params) {
+ status_t res = OK;
+ ALOGV("%s: Camera %d: Stopping face detection",
+ __FUNCTION__, mCameraId);
+
+ if (!params.enableFaceDetect) return OK;
+
+ params.enableFaceDetect = false;
+
+ if (params.state == Parameters::PREVIEW
+ || params.state == Parameters::RECORD
+ || params.state == Parameters::VIDEO_SNAPSHOT) {
+ res = updateRequests(params);
+ }
+
+ return res;
+}
+
+status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) {
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.enableFocusMoveMessages = enable;
+
+ return OK;
+}
+
+status_t Camera2Client::commandPingL() {
+ // Always ping back if access is proper and device is alive
+ SharedParameters::Lock l(mParameters);
+ if (l.mParameters.state != Parameters::DISCONNECTED) {
+ return OK;
+ } else {
+ return NO_INIT;
+ }
+}
+
+status_t Camera2Client::commandSetVideoBufferCountL(size_t count) {
+ if (recordingEnabledL()) {
+ ALOGE("%s: Camera %d: Error setting video buffer count after "
+ "recording was started", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+
+ return mStreamingProcessor->setRecordingBufferCount(count);
+}
+
+/** Device-related methods */
+
+void Camera2Client::notifyError(int errorCode, int arg1, int arg2) {
+ ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, arg1, arg2);
+}
+
+void Camera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) {
+ ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
+ frameNumber, timestamp);
+}
+
+void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
+ ALOGV("%s: Autofocus state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+ bool sendCompletedMessage = false;
+ bool sendMovingMessage = false;
+
+ bool success = false;
+ bool afInMotion = false;
+ {
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.focusState = newState;
+ switch (l.mParameters.focusMode) {
+ case Parameters::FOCUS_MODE_AUTO:
+ case Parameters::FOCUS_MODE_MACRO:
+ // Don't send notifications upstream if they're not for the current AF
+ // trigger. For example, if cancel was called in between, or if we
+ // already sent a notification about this AF call.
+ if (triggerId != l.mParameters.currentAfTriggerId) break;
+ switch (newState) {
+ case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+ success = true;
+ // no break
+ case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+ sendCompletedMessage = true;
+ l.mParameters.currentAfTriggerId = -1;
+ break;
+ case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+ // Just starting focusing, ignore
+ break;
+ case ANDROID_CONTROL_AF_STATE_INACTIVE:
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+ default:
+ // Unexpected in AUTO/MACRO mode
+ ALOGE("%s: Unexpected AF state transition in AUTO/MACRO mode: %d",
+ __FUNCTION__, newState);
+ break;
+ }
+ break;
+ case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO:
+ case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE:
+ switch (newState) {
+ case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+ success = true;
+ // no break
+ case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+ // Don't send notifications upstream if they're not for
+ // the current AF trigger. For example, if cancel was
+ // called in between, or if we already sent a
+ // notification about this AF call.
+ // Send both a 'AF done' callback and a 'AF move' callback
+ if (triggerId != l.mParameters.currentAfTriggerId) break;
+ sendCompletedMessage = true;
+ afInMotion = false;
+ if (l.mParameters.enableFocusMoveMessages &&
+ l.mParameters.afInMotion) {
+ sendMovingMessage = true;
+ }
+ l.mParameters.currentAfTriggerId = -1;
+ break;
+ case ANDROID_CONTROL_AF_STATE_INACTIVE:
+ // Cancel was called, or we switched state; care if
+ // currently moving
+ afInMotion = false;
+ if (l.mParameters.enableFocusMoveMessages &&
+ l.mParameters.afInMotion) {
+ sendMovingMessage = true;
+ }
+ break;
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+ // Start passive scan, inform upstream
+ afInMotion = true;
+ // no break
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+ // Stop passive scan, inform upstream
+ if (l.mParameters.enableFocusMoveMessages) {
+ sendMovingMessage = true;
+ }
+ break;
+ }
+ l.mParameters.afInMotion = afInMotion;
+ break;
+ case Parameters::FOCUS_MODE_EDOF:
+ case Parameters::FOCUS_MODE_INFINITY:
+ case Parameters::FOCUS_MODE_FIXED:
+ default:
+ if (newState != ANDROID_CONTROL_AF_STATE_INACTIVE) {
+ ALOGE("%s: Unexpected AF state change %d "
+ "(ID %d) in focus mode %d",
+ __FUNCTION__, newState, triggerId,
+ l.mParameters.focusMode);
+ }
+ }
+ }
+ if (sendMovingMessage) {
+ SharedCameraClient::Lock l(mSharedCameraClient);
+ if (l.mCameraClient != 0) {
+ l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+ afInMotion ? 1 : 0, 0);
+ }
+ }
+ if (sendCompletedMessage) {
+ SharedCameraClient::Lock l(mSharedCameraClient);
+ if (l.mCameraClient != 0) {
+ l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS,
+ success ? 1 : 0, 0);
+ }
+ }
+}
+
+void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
+ ALOGV("%s: Autoexposure state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+ mCaptureSequencer->notifyAutoExposure(newState, triggerId);
+}
+
+void Camera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) {
+ ALOGV("%s: Auto-whitebalance state now %d, last trigger %d",
+ __FUNCTION__, newState, triggerId);
+}
+
+int Camera2Client::getCameraId() const {
+ return mCameraId;
+}
+
+const sp<Camera2Device>& Camera2Client::getCameraDevice() {
+ return mDevice;
+}
+
+const sp<CameraService>& Camera2Client::getCameraService() {
+ return mCameraService;
+}
+
+camera2::SharedParameters& Camera2Client::getParameters() {
+ return mParameters;
+}
+
+int Camera2Client::getPreviewStreamId() const {
+ return mStreamingProcessor->getPreviewStreamId();
+}
+
+int Camera2Client::getCaptureStreamId() const {
+ return mJpegProcessor->getStreamId();
+}
+
+int Camera2Client::getCallbackStreamId() const {
+ return mCallbackProcessor->getStreamId();
+}
+
+int Camera2Client::getRecordingStreamId() const {
+ return mStreamingProcessor->getRecordingStreamId();
+}
+
+int Camera2Client::getZslStreamId() const {
+ return mZslProcessor->getStreamId();
+}
+
+status_t Camera2Client::registerFrameListener(int32_t minId, int32_t maxId,
+ wp<camera2::FrameProcessor::FilteredListener> listener) {
+ return mFrameProcessor->registerListener(minId, maxId, listener);
+}
+
+status_t Camera2Client::removeFrameListener(int32_t minId, int32_t maxId,
+ wp<camera2::FrameProcessor::FilteredListener> listener) {
+ return mFrameProcessor->removeListener(minId, maxId, listener);
+}
+
+status_t Camera2Client::stopStream() {
+ return mStreamingProcessor->stopStream();
+}
+
+Camera2Client::SharedCameraClient::Lock::Lock(SharedCameraClient &client):
+ mCameraClient(client.mCameraClient),
+ mSharedClient(client) {
+ mSharedClient.mCameraClientLock.lock();
+}
+
+Camera2Client::SharedCameraClient::Lock::~Lock() {
+ mSharedClient.mCameraClientLock.unlock();
+}
+
+Camera2Client::SharedCameraClient::SharedCameraClient(const sp<ICameraClient>&client):
+ mCameraClient(client) {
+}
+
+Camera2Client::SharedCameraClient& Camera2Client::SharedCameraClient::operator=(
+ const sp<ICameraClient>&client) {
+ Mutex::Autolock l(mCameraClientLock);
+ mCameraClient = client;
+ return *this;
+}
+
+void Camera2Client::SharedCameraClient::clear() {
+ Mutex::Autolock l(mCameraClientLock);
+ mCameraClient.clear();
+}
+
+const int32_t Camera2Client::kPreviewRequestIdStart;
+const int32_t Camera2Client::kPreviewRequestIdEnd;
+const int32_t Camera2Client::kRecordingRequestIdStart;
+const int32_t Camera2Client::kRecordingRequestIdEnd;
+const int32_t Camera2Client::kCaptureRequestIdStart;
+const int32_t Camera2Client::kCaptureRequestIdEnd;
+
+/** Utility methods */
+
+status_t Camera2Client::updateRequests(Parameters &params) {
+ status_t res;
+
+ ALOGV("%s: Camera %d: state = %d", __FUNCTION__, getCameraId(), params.state);
+
+ res = mStreamingProcessor->incrementStreamingIds();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to increment request IDs: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ res = mStreamingProcessor->updatePreviewRequest(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ res = mStreamingProcessor->updateRecordingRequest(params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ if (params.state == Parameters::PREVIEW) {
+ res = startPreviewL(params, true);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error streaming new preview request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ } else if (params.state == Parameters::RECORD ||
+ params.state == Parameters::VIDEO_SNAPSHOT) {
+ res = startRecordingL(params, true);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+ return res;
+}
+
+
+size_t Camera2Client::calculateBufferSize(int width, int height,
+ int format, int stride) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP: // NV16
+ return width * height * 2;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21
+ return width * height * 3 / 2;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I: // YUY2
+ return width * height * 2;
+ case HAL_PIXEL_FORMAT_YV12: { // YV12
+ size_t ySize = stride * height;
+ size_t uvStride = (stride / 2 + 0xF) & ~0xF;
+ size_t uvSize = uvStride * height / 2;
+ return ySize + uvSize * 2;
+ }
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return width * height * 2;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return width * height * 4;
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ return width * height * 2;
+ default:
+ ALOGE("%s: Unknown preview format: %x",
+ __FUNCTION__, format);
+ return 0;
+ }
+}
+
+status_t Camera2Client::syncWithDevice() {
+ ATRACE_CALL();
+ const nsecs_t kMaxSyncTimeout = 500000000; // 500 ms
+ status_t res;
+
+ int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
+ if (activeRequestId == 0) return OK;
+
+ res = mDevice->waitUntilRequestReceived(activeRequestId, kMaxSyncTimeout);
+ if (res == TIMED_OUT) {
+ ALOGE("%s: Camera %d: Timed out waiting sync with HAL",
+ __FUNCTION__, mCameraId);
+ } else if (res != OK) {
+ ALOGE("%s: Camera %d: Error while waiting to sync with HAL",
+ __FUNCTION__, mCameraId);
+ }
+ return res;
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
new file mode 100644
index 00000000..55ead024
--- /dev/null
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_H
+
+#include "Camera2Device.h"
+#include "CameraService.h"
+#include "camera2/Parameters.h"
+#include "camera2/FrameProcessor.h"
+#include "camera2/StreamingProcessor.h"
+#include "camera2/JpegProcessor.h"
+#include "camera2/ZslProcessor.h"
+#include "camera2/CaptureSequencer.h"
+#include "camera2/CallbackProcessor.h"
+
+namespace android {
+
+class IMemory;
+/**
+ * Implements the android.hardware.camera API on top of
+ * camera device HAL version 2.
+ */
+class Camera2Client :
+ public CameraService::Client,
+ public Camera2Device::NotificationListener
+{
+public:
+ /**
+ * ICamera interface (see ICamera for details)
+ */
+
+ virtual void disconnect();
+ virtual status_t connect(const sp<ICameraClient>& client);
+ virtual status_t lock();
+ virtual status_t unlock();
+ virtual status_t setPreviewDisplay(const sp<Surface>& surface);
+ virtual status_t setPreviewTexture(
+ const sp<ISurfaceTexture>& surfaceTexture);
+ virtual void setPreviewCallbackFlag(int flag);
+ virtual status_t startPreview();
+ virtual void stopPreview();
+ virtual bool previewEnabled();
+ virtual status_t storeMetaDataInBuffers(bool enabled);
+ virtual status_t startRecording();
+ virtual void stopRecording();
+ virtual bool recordingEnabled();
+ virtual void releaseRecordingFrame(const sp<IMemory>& mem);
+ virtual status_t autoFocus();
+ virtual status_t cancelAutoFocus();
+ virtual status_t takePicture(int msgType);
+ virtual status_t setParameters(const String8& params);
+ virtual String8 getParameters() const;
+ virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+
+ /**
+ * Interface used by CameraService
+ */
+
+ Camera2Client(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+ virtual ~Camera2Client();
+
+ status_t initialize(camera_module_t *module);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ /**
+ * Interface used by Camera2Device
+ */
+
+ virtual void notifyError(int errorCode, int arg1, int arg2);
+ virtual void notifyShutter(int frameNumber, nsecs_t timestamp);
+ virtual void notifyAutoFocus(uint8_t newState, int triggerId);
+ virtual void notifyAutoExposure(uint8_t newState, int triggerId);
+ virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId);
+
+ /**
+ * Interface used by independent components of Camera2Client.
+ */
+
+ int getCameraId() const;
+ const sp<Camera2Device>& getCameraDevice();
+ const sp<CameraService>& getCameraService();
+ camera2::SharedParameters& getParameters();
+
+ int getPreviewStreamId() const;
+ int getCaptureStreamId() const;
+ int getCallbackStreamId() const;
+ int getRecordingStreamId() const;
+ int getZslStreamId() const;
+
+ status_t registerFrameListener(int32_t minId, int32_t maxId,
+ wp<camera2::FrameProcessor::FilteredListener> listener);
+ status_t removeFrameListener(int32_t minId, int32_t maxId,
+ wp<camera2::FrameProcessor::FilteredListener> listener);
+
+ status_t stopStream();
+
+ // Simple class to ensure that access to ICameraClient is serialized by
+ // requiring mCameraClientLock to be locked before access to mCameraClient
+ // is possible.
+ class SharedCameraClient {
+ public:
+ class Lock {
+ public:
+ Lock(SharedCameraClient &client);
+ ~Lock();
+ sp<ICameraClient> &mCameraClient;
+ private:
+ SharedCameraClient &mSharedClient;
+ };
+ SharedCameraClient(const sp<ICameraClient>& client);
+ SharedCameraClient& operator=(const sp<ICameraClient>& client);
+ void clear();
+ private:
+ sp<ICameraClient> mCameraClient;
+ mutable Mutex mCameraClientLock;
+ } mSharedCameraClient;
+
+ static size_t calculateBufferSize(int width, int height,
+ int format, int stride);
+
+ static const int32_t kPreviewRequestIdStart = 10000000;
+ static const int32_t kPreviewRequestIdEnd = 20000000;
+
+ static const int32_t kRecordingRequestIdStart = 20000000;
+ static const int32_t kRecordingRequestIdEnd = 30000000;
+
+ static const int32_t kCaptureRequestIdStart = 30000000;
+ static const int32_t kCaptureRequestIdEnd = 40000000;
+
+private:
+ /** ICamera interface-related private members */
+
+ // Mutex that must be locked by methods implementing the ICamera interface.
+ // Ensures serialization between incoming ICamera calls. All methods below
+ // that append 'L' to the name assume that mICameraLock is locked when
+ // they're called
+ mutable Mutex mICameraLock;
+
+ typedef camera2::Parameters Parameters;
+ typedef camera2::CameraMetadata CameraMetadata;
+
+ status_t setPreviewWindowL(const sp<IBinder>& binder,
+ sp<ANativeWindow> window);
+ status_t startPreviewL(Parameters &params, bool restart);
+ void stopPreviewL();
+ status_t startRecordingL(Parameters &params, bool restart);
+ bool recordingEnabledL();
+
+ // Individual commands for sendCommand()
+ status_t commandStartSmoothZoomL();
+ status_t commandStopSmoothZoomL();
+ status_t commandSetDisplayOrientationL(int degrees);
+ status_t commandEnableShutterSoundL(bool enable);
+ status_t commandPlayRecordingSoundL();
+ status_t commandStartFaceDetectionL(int type);
+ status_t commandStopFaceDetectionL(Parameters &params);
+ status_t commandEnableFocusMoveMsgL(bool enable);
+ status_t commandPingL();
+ status_t commandSetVideoBufferCountL(size_t count);
+
+ // Current camera device configuration
+ camera2::SharedParameters mParameters;
+
+ /** Camera device-related private members */
+
+ void setPreviewCallbackFlagL(Parameters &params, int flag);
+ status_t updateRequests(Parameters &params);
+
+ // Used with stream IDs
+ static const int NO_STREAM = -1;
+
+ sp<camera2::FrameProcessor> mFrameProcessor;
+
+ /* Preview/Recording related members */
+
+ sp<IBinder> mPreviewSurface;
+ sp<camera2::StreamingProcessor> mStreamingProcessor;
+
+ /** Preview callback related members */
+
+ sp<camera2::CallbackProcessor> mCallbackProcessor;
+
+ /* Still image capture related members */
+
+ sp<camera2::CaptureSequencer> mCaptureSequencer;
+ sp<camera2::JpegProcessor> mJpegProcessor;
+ sp<camera2::ZslProcessor> mZslProcessor;
+
+ /** Notification-related members */
+
+ bool mAfInMotion;
+
+ /** Camera2Device instance wrapping HAL2 entry */
+
+ sp<Camera2Device> mDevice;
+
+ /** Utility members */
+
+ // Wait until the camera device has received the latest control settings
+ status_t syncWithDevice();
+
+ // Verify that caller is the owner of the camera
+ status_t checkPid(const char *checkLocation) const;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
new file mode 100644
index 00000000..d6445c1b
--- /dev/null
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -0,0 +1,1513 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-Device"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0 // Per-frame verbose logging
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Timers.h>
+#include "Camera2Device.h"
+
+namespace android {
+
+Camera2Device::Camera2Device(int id):
+ mId(id),
+ mDevice(NULL)
+{
+ ATRACE_CALL();
+ ALOGV("%s: Created device for camera %d", __FUNCTION__, id);
+}
+
+Camera2Device::~Camera2Device()
+{
+ ATRACE_CALL();
+ ALOGV("%s: Tearing down for camera id %d", __FUNCTION__, mId);
+ disconnect();
+}
+
+status_t Camera2Device::initialize(camera_module_t *module)
+{
+ ATRACE_CALL();
+ ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
+ if (mDevice != NULL) {
+ ALOGE("%s: Already initialized!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ status_t res;
+ char name[10];
+ snprintf(name, sizeof(name), "%d", mId);
+
+ camera2_device_t *device;
+
+ res = module->common.methods->open(&module->common, name,
+ reinterpret_cast<hw_device_t**>(&device));
+
+ if (res != OK) {
+ ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__,
+ mId, strerror(-res), res);
+ return res;
+ }
+
+ if (device->common.version != CAMERA_DEVICE_API_VERSION_2_0) {
+ ALOGE("%s: Could not open camera %d: "
+ "Camera device is not version %x, reports %x instead",
+ __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_2_0,
+ device->common.version);
+ device->common.close(&device->common);
+ return BAD_VALUE;
+ }
+
+ camera_info info;
+ res = module->get_camera_info(mId, &info);
+ if (res != OK ) return res;
+
+ if (info.device_version != device->common.version) {
+ ALOGE("%s: HAL reporting mismatched camera_info version (%x)"
+ " and device version (%x).", __FUNCTION__,
+ device->common.version, info.device_version);
+ device->common.close(&device->common);
+ return BAD_VALUE;
+ }
+
+ res = mRequestQueue.setConsumerDevice(device);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to connect request queue to device: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ device->common.close(&device->common);
+ return res;
+ }
+ res = mFrameQueue.setProducerDevice(device);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to connect frame queue to device: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ device->common.close(&device->common);
+ return res;
+ }
+
+ res = device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps);
+ if (res != OK ) {
+ ALOGE("%s: Camera %d: Unable to retrieve tag ops from device: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ device->common.close(&device->common);
+ return res;
+ }
+ res = set_camera_metadata_vendor_tag_ops(mVendorTagOps);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ device->common.close(&device->common);
+ return res;
+ }
+ res = device->ops->set_notify_callback(device, notificationCallback,
+ NULL);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to initialize notification callback!",
+ __FUNCTION__, mId);
+ device->common.close(&device->common);
+ return res;
+ }
+
+ mDeviceInfo = info.static_camera_characteristics;
+ mDevice = device;
+
+ return OK;
+}
+
+status_t Camera2Device::disconnect() {
+ ATRACE_CALL();
+ status_t res = OK;
+ if (mDevice) {
+ ALOGV("%s: Closing device for camera %d", __FUNCTION__, mId);
+
+ int inProgressCount = mDevice->ops->get_in_progress_count(mDevice);
+ if (inProgressCount > 0) {
+ ALOGW("%s: Closing camera device %d with %d requests in flight!",
+ __FUNCTION__, mId, inProgressCount);
+ }
+ mReprocessStreams.clear();
+ mStreams.clear();
+ res = mDevice->common.close(&mDevice->common);
+ if (res != OK) {
+ ALOGE("%s: Could not close camera %d: %s (%d)",
+ __FUNCTION__,
+ mId, strerror(-res), res);
+ }
+ mDevice = NULL;
+ ALOGV("%s: Shutdown complete", __FUNCTION__);
+ }
+ return res;
+}
+
+status_t Camera2Device::dump(int fd, const Vector<String16>& args) {
+ ATRACE_CALL();
+ String8 result;
+ int detailLevel = 0;
+ int n = args.size();
+ String16 detailOption("-d");
+ for (int i = 0; i + 1 < n; i++) {
+ if (args[i] == detailOption) {
+ String8 levelStr(args[i+1]);
+ detailLevel = atoi(levelStr.string());
+ }
+ }
+
+ result.appendFormat(" Camera2Device[%d] dump (detail level %d):\n",
+ mId, detailLevel);
+
+ if (detailLevel > 0) {
+ result = " Request queue contents:\n";
+ write(fd, result.string(), result.size());
+ mRequestQueue.dump(fd, args);
+
+ result = " Frame queue contents:\n";
+ write(fd, result.string(), result.size());
+ mFrameQueue.dump(fd, args);
+ }
+
+ result = " Active streams:\n";
+ write(fd, result.string(), result.size());
+ for (StreamList::iterator s = mStreams.begin(); s != mStreams.end(); s++) {
+ (*s)->dump(fd, args);
+ }
+
+ result = " HAL device dump:\n";
+ write(fd, result.string(), result.size());
+
+ status_t res;
+ res = mDevice->ops->dump(mDevice, fd);
+
+ return res;
+}
+
+const camera2::CameraMetadata& Camera2Device::info() const {
+ ALOGVV("%s: E", __FUNCTION__);
+
+ return mDeviceInfo;
+}
+
+status_t Camera2Device::capture(CameraMetadata &request) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+
+ mRequestQueue.enqueue(request.release());
+ return OK;
+}
+
+
+status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ CameraMetadata streamRequest(request);
+ return mRequestQueue.setStreamSlot(streamRequest.release());
+}
+
+status_t Camera2Device::clearStreamingRequest() {
+ ATRACE_CALL();
+ return mRequestQueue.setStreamSlot(NULL);
+}
+
+status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
+ ATRACE_CALL();
+ return mRequestQueue.waitForDequeue(requestId, timeout);
+}
+
+status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
+ uint32_t width, uint32_t height, int format, size_t size, int *id) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: E", __FUNCTION__);
+
+ sp<StreamAdapter> stream = new StreamAdapter(mDevice);
+
+ res = stream->connectToDevice(consumer, width, height, format, size);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create stream (%d x %d, format %x):"
+ "%s (%d)",
+ __FUNCTION__, mId, width, height, format, strerror(-res), res);
+ return res;
+ }
+
+ *id = stream->getId();
+
+ mStreams.push_back(stream);
+ return OK;
+}
+
+status_t Camera2Device::createReprocessStreamFromStream(int outputId, int *id) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: E", __FUNCTION__);
+
+ bool found = false;
+ StreamList::iterator streamI;
+ for (streamI = mStreams.begin();
+ streamI != mStreams.end(); streamI++) {
+ if ((*streamI)->getId() == outputId) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Output stream %d doesn't exist; can't create "
+ "reprocess stream from it!", __FUNCTION__, mId, outputId);
+ return BAD_VALUE;
+ }
+
+ sp<ReprocessStreamAdapter> stream = new ReprocessStreamAdapter(mDevice);
+
+ res = stream->connectToDevice((*streamI));
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create reprocessing stream from "\
+ "stream %d: %s (%d)", __FUNCTION__, mId, outputId,
+ strerror(-res), res);
+ return res;
+ }
+
+ *id = stream->getId();
+
+ mReprocessStreams.push_back(stream);
+ return OK;
+}
+
+
+status_t Camera2Device::getStreamInfo(int id,
+ uint32_t *width, uint32_t *height, uint32_t *format) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ bool found = false;
+ StreamList::iterator streamI;
+ for (streamI = mStreams.begin();
+ streamI != mStreams.end(); streamI++) {
+ if ((*streamI)->getId() == id) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Stream %d does not exist",
+ __FUNCTION__, mId, id);
+ return BAD_VALUE;
+ }
+
+ if (width) *width = (*streamI)->getWidth();
+ if (height) *height = (*streamI)->getHeight();
+ if (format) *format = (*streamI)->getFormat();
+
+ return OK;
+}
+
+status_t Camera2Device::setStreamTransform(int id,
+ int transform) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ bool found = false;
+ StreamList::iterator streamI;
+ for (streamI = mStreams.begin();
+ streamI != mStreams.end(); streamI++) {
+ if ((*streamI)->getId() == id) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Stream %d does not exist",
+ __FUNCTION__, mId, id);
+ return BAD_VALUE;
+ }
+
+ return (*streamI)->setTransform(transform);
+}
+
+status_t Camera2Device::deleteStream(int id) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ bool found = false;
+ for (StreamList::iterator streamI = mStreams.begin();
+ streamI != mStreams.end(); streamI++) {
+ if ((*streamI)->getId() == id) {
+ status_t res = (*streamI)->release();
+ if (res != OK) {
+ ALOGE("%s: Unable to release stream %d from HAL device: "
+ "%s (%d)", __FUNCTION__, id, strerror(-res), res);
+ return res;
+ }
+ mStreams.erase(streamI);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Unable to find stream %d to delete",
+ __FUNCTION__, mId, id);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+status_t Camera2Device::deleteReprocessStream(int id) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ bool found = false;
+ for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin();
+ streamI != mReprocessStreams.end(); streamI++) {
+ if ((*streamI)->getId() == id) {
+ status_t res = (*streamI)->release();
+ if (res != OK) {
+ ALOGE("%s: Unable to release reprocess stream %d from "
+ "HAL device: %s (%d)", __FUNCTION__, id,
+ strerror(-res), res);
+ return res;
+ }
+ mReprocessStreams.erase(streamI);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Unable to find stream %d to delete",
+ __FUNCTION__, mId, id);
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+
+status_t Camera2Device::createDefaultRequest(int templateId,
+ CameraMetadata *request) {
+ ATRACE_CALL();
+ status_t err;
+ ALOGV("%s: E", __FUNCTION__);
+ camera_metadata_t *rawRequest;
+ err = mDevice->ops->construct_default_request(
+ mDevice, templateId, &rawRequest);
+ request->acquire(rawRequest);
+ return err;
+}
+
+status_t Camera2Device::waitUntilDrained() {
+ ATRACE_CALL();
+ static const uint32_t kSleepTime = 50000; // 50 ms
+ static const uint32_t kMaxSleepTime = 10000000; // 10 s
+ ALOGV("%s: Camera %d: Starting wait", __FUNCTION__, mId);
+ if (mRequestQueue.getBufferCount() ==
+ CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS) return INVALID_OPERATION;
+
+ // TODO: Set up notifications from HAL, instead of sleeping here
+ uint32_t totalTime = 0;
+ while (mDevice->ops->get_in_progress_count(mDevice) > 0) {
+ usleep(kSleepTime);
+ totalTime += kSleepTime;
+ if (totalTime > kMaxSleepTime) {
+ ALOGE("%s: Waited %d us, %d requests still in flight", __FUNCTION__,
+ mDevice->ops->get_in_progress_count(mDevice), totalTime);
+ return TIMED_OUT;
+ }
+ }
+ ALOGV("%s: Camera %d: HAL is idle", __FUNCTION__, mId);
+ return OK;
+}
+
+status_t Camera2Device::setNotifyCallback(NotificationListener *listener) {
+ ATRACE_CALL();
+ status_t res;
+ res = mDevice->ops->set_notify_callback(mDevice, notificationCallback,
+ reinterpret_cast<void*>(listener) );
+ if (res != OK) {
+ ALOGE("%s: Unable to set notification callback!", __FUNCTION__);
+ }
+ return res;
+}
+
+void Camera2Device::notificationCallback(int32_t msg_type,
+ int32_t ext1,
+ int32_t ext2,
+ int32_t ext3,
+ void *user) {
+ ATRACE_CALL();
+ NotificationListener *listener = reinterpret_cast<NotificationListener*>(user);
+ ALOGV("%s: Notification %d, arguments %d, %d, %d", __FUNCTION__, msg_type,
+ ext1, ext2, ext3);
+ if (listener != NULL) {
+ switch (msg_type) {
+ case CAMERA2_MSG_ERROR:
+ listener->notifyError(ext1, ext2, ext3);
+ break;
+ case CAMERA2_MSG_SHUTTER: {
+ nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
+ listener->notifyShutter(ext1, timestamp);
+ break;
+ }
+ case CAMERA2_MSG_AUTOFOCUS:
+ listener->notifyAutoFocus(ext1, ext2);
+ break;
+ case CAMERA2_MSG_AUTOEXPOSURE:
+ listener->notifyAutoExposure(ext1, ext2);
+ break;
+ case CAMERA2_MSG_AUTOWB:
+ listener->notifyAutoWhitebalance(ext1, ext2);
+ break;
+ default:
+ ALOGE("%s: Unknown notification %d (arguments %d, %d, %d)!",
+ __FUNCTION__, msg_type, ext1, ext2, ext3);
+ }
+ }
+}
+
+status_t Camera2Device::waitForNextFrame(nsecs_t timeout) {
+ return mFrameQueue.waitForBuffer(timeout);
+}
+
+status_t Camera2Device::getNextFrame(CameraMetadata *frame) {
+ ATRACE_CALL();
+ status_t res;
+ camera_metadata_t *rawFrame;
+ res = mFrameQueue.dequeue(&rawFrame);
+ if (rawFrame == NULL) {
+ return NOT_ENOUGH_DATA;
+ } else if (res == OK) {
+ frame->acquire(rawFrame);
+ }
+ return res;
+}
+
+status_t Camera2Device::triggerAutofocus(uint32_t id) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
+ res = mDevice->ops->trigger_action(mDevice,
+ CAMERA2_TRIGGER_AUTOFOCUS, id, 0);
+ if (res != OK) {
+ ALOGE("%s: Error triggering autofocus (id %d)",
+ __FUNCTION__, id);
+ }
+ return res;
+}
+
+status_t Camera2Device::triggerCancelAutofocus(uint32_t id) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: Canceling autofocus, id %d", __FUNCTION__, id);
+ res = mDevice->ops->trigger_action(mDevice,
+ CAMERA2_TRIGGER_CANCEL_AUTOFOCUS, id, 0);
+ if (res != OK) {
+ ALOGE("%s: Error canceling autofocus (id %d)",
+ __FUNCTION__, id);
+ }
+ return res;
+}
+
+status_t Camera2Device::triggerPrecaptureMetering(uint32_t id) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
+ res = mDevice->ops->trigger_action(mDevice,
+ CAMERA2_TRIGGER_PRECAPTURE_METERING, id, 0);
+ if (res != OK) {
+ ALOGE("%s: Error triggering precapture metering (id %d)",
+ __FUNCTION__, id);
+ }
+ return res;
+}
+
+status_t Camera2Device::pushReprocessBuffer(int reprocessStreamId,
+ buffer_handle_t *buffer, wp<BufferReleasedListener> listener) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ bool found = false;
+ status_t res = OK;
+ for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin();
+ streamI != mReprocessStreams.end(); streamI++) {
+ if ((*streamI)->getId() == reprocessStreamId) {
+ res = (*streamI)->pushIntoStream(buffer, listener);
+ if (res != OK) {
+ ALOGE("%s: Unable to push buffer to reprocess stream %d: %s (%d)",
+ __FUNCTION__, reprocessStreamId, strerror(-res), res);
+ return res;
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ALOGE("%s: Camera %d: Unable to find reprocess stream %d",
+ __FUNCTION__, mId, reprocessStreamId);
+ res = BAD_VALUE;
+ }
+ return res;
+}
+
+/**
+ * Camera2Device::NotificationListener
+ */
+
+Camera2Device::NotificationListener::~NotificationListener() {
+}
+
+/**
+ * Camera2Device::MetadataQueue
+ */
+
+Camera2Device::MetadataQueue::MetadataQueue():
+ mDevice(NULL),
+ mFrameCount(0),
+ mLatestRequestId(0),
+ mCount(0),
+ mStreamSlotCount(0),
+ mSignalConsumer(true)
+{
+ ATRACE_CALL();
+ camera2_request_queue_src_ops::dequeue_request = consumer_dequeue;
+ camera2_request_queue_src_ops::request_count = consumer_buffer_count;
+ camera2_request_queue_src_ops::free_request = consumer_free;
+
+ camera2_frame_queue_dst_ops::dequeue_frame = producer_dequeue;
+ camera2_frame_queue_dst_ops::cancel_frame = producer_cancel;
+ camera2_frame_queue_dst_ops::enqueue_frame = producer_enqueue;
+}
+
+Camera2Device::MetadataQueue::~MetadataQueue() {
+ ATRACE_CALL();
+ Mutex::Autolock l(mMutex);
+ freeBuffers(mEntries.begin(), mEntries.end());
+ freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
+}
+
+// Connect to camera2 HAL as consumer (input requests/reprocessing)
+status_t Camera2Device::MetadataQueue::setConsumerDevice(camera2_device_t *d) {
+ ATRACE_CALL();
+ status_t res;
+ res = d->ops->set_request_queue_src_ops(d,
+ this);
+ if (res != OK) return res;
+ mDevice = d;
+ return OK;
+}
+
+status_t Camera2Device::MetadataQueue::setProducerDevice(camera2_device_t *d) {
+ ATRACE_CALL();
+ status_t res;
+ res = d->ops->set_frame_queue_dst_ops(d,
+ this);
+ return res;
+}
+
+// Real interfaces
+status_t Camera2Device::MetadataQueue::enqueue(camera_metadata_t *buf) {
+ ATRACE_CALL();
+ ALOGVV("%s: E", __FUNCTION__);
+ Mutex::Autolock l(mMutex);
+
+ mCount++;
+ mEntries.push_back(buf);
+
+ return signalConsumerLocked();
+}
+
+int Camera2Device::MetadataQueue::getBufferCount() {
+ ATRACE_CALL();
+ Mutex::Autolock l(mMutex);
+ if (mStreamSlotCount > 0) {
+ return CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS;
+ }
+ return mCount;
+}
+
+status_t Camera2Device::MetadataQueue::dequeue(camera_metadata_t **buf,
+ bool incrementCount)
+{
+ ATRACE_CALL();
+ ALOGVV("%s: E", __FUNCTION__);
+ status_t res;
+ Mutex::Autolock l(mMutex);
+
+ if (mCount == 0) {
+ if (mStreamSlotCount == 0) {
+ ALOGVV("%s: Empty", __FUNCTION__);
+ *buf = NULL;
+ mSignalConsumer = true;
+ return OK;
+ }
+ ALOGVV("%s: Streaming %d frames to queue", __FUNCTION__,
+ mStreamSlotCount);
+
+ for (List<camera_metadata_t*>::iterator slotEntry = mStreamSlot.begin();
+ slotEntry != mStreamSlot.end();
+ slotEntry++ ) {
+ size_t entries = get_camera_metadata_entry_count(*slotEntry);
+ size_t dataBytes = get_camera_metadata_data_count(*slotEntry);
+
+ camera_metadata_t *copy =
+ allocate_camera_metadata(entries, dataBytes);
+ append_camera_metadata(copy, *slotEntry);
+ mEntries.push_back(copy);
+ }
+ mCount = mStreamSlotCount;
+ }
+ ALOGVV("MetadataQueue: deque (%d buffers)", mCount);
+ camera_metadata_t *b = *(mEntries.begin());
+ mEntries.erase(mEntries.begin());
+
+ if (incrementCount) {
+ ATRACE_INT("cam2_request", mFrameCount);
+ camera_metadata_entry_t frameCount;
+ res = find_camera_metadata_entry(b,
+ ANDROID_REQUEST_FRAME_COUNT,
+ &frameCount);
+ if (res != OK) {
+ ALOGE("%s: Unable to add frame count: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ } else {
+ *frameCount.data.i32 = mFrameCount;
+ }
+ mFrameCount++;
+ }
+
+ // Check for request ID, and if present, signal waiters.
+ camera_metadata_entry_t requestId;
+ res = find_camera_metadata_entry(b,
+ ANDROID_REQUEST_ID,
+ &requestId);
+ if (res == OK) {
+ mLatestRequestId = requestId.data.i32[0];
+ mNewRequestId.signal();
+ }
+
+ *buf = b;
+ mCount--;
+
+ return OK;
+}
+
+status_t Camera2Device::MetadataQueue::waitForBuffer(nsecs_t timeout)
+{
+ Mutex::Autolock l(mMutex);
+ status_t res;
+ while (mCount == 0) {
+ res = notEmpty.waitRelative(mMutex,timeout);
+ if (res != OK) return res;
+ }
+ return OK;
+}
+
+status_t Camera2Device::MetadataQueue::waitForDequeue(int32_t id,
+ nsecs_t timeout) {
+ Mutex::Autolock l(mMutex);
+ status_t res;
+ while (mLatestRequestId != id) {
+ nsecs_t startTime = systemTime();
+
+ res = mNewRequestId.waitRelative(mMutex, timeout);
+ if (res != OK) return res;
+
+ timeout -= (systemTime() - startTime);
+ }
+
+ return OK;
+}
+
+status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf)
+{
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock l(mMutex);
+ if (buf == NULL) {
+ freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
+ mStreamSlotCount = 0;
+ return OK;
+ }
+ camera_metadata_t *buf2 = clone_camera_metadata(buf);
+ if (!buf2) {
+ ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__);
+ return NO_MEMORY;
+ }
+
+ if (mStreamSlotCount > 1) {
+ List<camera_metadata_t*>::iterator deleter = ++mStreamSlot.begin();
+ freeBuffers(++mStreamSlot.begin(), mStreamSlot.end());
+ mStreamSlotCount = 1;
+ }
+ if (mStreamSlotCount == 1) {
+ free_camera_metadata( *(mStreamSlot.begin()) );
+ *(mStreamSlot.begin()) = buf2;
+ } else {
+ mStreamSlot.push_front(buf2);
+ mStreamSlotCount = 1;
+ }
+ return signalConsumerLocked();
+}
+
+status_t Camera2Device::MetadataQueue::setStreamSlot(
+ const List<camera_metadata_t*> &bufs)
+{
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock l(mMutex);
+ status_t res;
+
+ if (mStreamSlotCount > 0) {
+ freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
+ }
+ mStreamSlotCount = 0;
+ for (List<camera_metadata_t*>::const_iterator r = bufs.begin();
+ r != bufs.end(); r++) {
+ camera_metadata_t *r2 = clone_camera_metadata(*r);
+ if (!r2) {
+ ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ mStreamSlot.push_back(r2);
+ mStreamSlotCount++;
+ }
+ return signalConsumerLocked();
+}
+
+status_t Camera2Device::MetadataQueue::dump(int fd,
+ const Vector<String16>& args) {
+ ATRACE_CALL();
+ String8 result;
+ status_t notLocked;
+ notLocked = mMutex.tryLock();
+ if (notLocked) {
+ result.append(" (Unable to lock queue mutex)\n");
+ }
+ result.appendFormat(" Current frame number: %d\n", mFrameCount);
+ if (mStreamSlotCount == 0) {
+ result.append(" Stream slot: Empty\n");
+ write(fd, result.string(), result.size());
+ } else {
+ result.appendFormat(" Stream slot: %d entries\n",
+ mStreamSlot.size());
+ int i = 0;
+ for (List<camera_metadata_t*>::iterator r = mStreamSlot.begin();
+ r != mStreamSlot.end(); r++) {
+ result = String8::format(" Stream slot buffer %d:\n", i);
+ write(fd, result.string(), result.size());
+ dump_indented_camera_metadata(*r, fd, 2, 10);
+ i++;
+ }
+ }
+ if (mEntries.size() == 0) {
+ result = " Main queue is empty\n";
+ write(fd, result.string(), result.size());
+ } else {
+ result = String8::format(" Main queue has %d entries:\n",
+ mEntries.size());
+ int i = 0;
+ for (List<camera_metadata_t*>::iterator r = mEntries.begin();
+ r != mEntries.end(); r++) {
+ result = String8::format(" Queue entry %d:\n", i);
+ write(fd, result.string(), result.size());
+ dump_indented_camera_metadata(*r, fd, 2, 10);
+ i++;
+ }
+ }
+
+ if (notLocked == 0) {
+ mMutex.unlock();
+ }
+
+ return OK;
+}
+
+status_t Camera2Device::MetadataQueue::signalConsumerLocked() {
+ ATRACE_CALL();
+ status_t res = OK;
+ notEmpty.signal();
+ if (mSignalConsumer && mDevice != NULL) {
+ mSignalConsumer = false;
+
+ mMutex.unlock();
+ ALOGV("%s: Signaling consumer", __FUNCTION__);
+ res = mDevice->ops->notify_request_queue_not_empty(mDevice);
+ mMutex.lock();
+ }
+ return res;
+}
+
+status_t Camera2Device::MetadataQueue::freeBuffers(
+ List<camera_metadata_t*>::iterator start,
+ List<camera_metadata_t*>::iterator end)
+{
+ ATRACE_CALL();
+ while (start != end) {
+ free_camera_metadata(*start);
+ start = mStreamSlot.erase(start);
+ }
+ return OK;
+}
+
+Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance(
+ const camera2_request_queue_src_ops_t *q)
+{
+ const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
+ return const_cast<MetadataQueue*>(cmq);
+}
+
+Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance(
+ const camera2_frame_queue_dst_ops_t *q)
+{
+ const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q);
+ return const_cast<MetadataQueue*>(cmq);
+}
+
+int Camera2Device::MetadataQueue::consumer_buffer_count(
+ const camera2_request_queue_src_ops_t *q)
+{
+ MetadataQueue *queue = getInstance(q);
+ return queue->getBufferCount();
+}
+
+int Camera2Device::MetadataQueue::consumer_dequeue(
+ const camera2_request_queue_src_ops_t *q,
+ camera_metadata_t **buffer)
+{
+ MetadataQueue *queue = getInstance(q);
+ return queue->dequeue(buffer, true);
+}
+
+int Camera2Device::MetadataQueue::consumer_free(
+ const camera2_request_queue_src_ops_t *q,
+ camera_metadata_t *old_buffer)
+{
+ ATRACE_CALL();
+ MetadataQueue *queue = getInstance(q);
+ free_camera_metadata(old_buffer);
+ return OK;
+}
+
+int Camera2Device::MetadataQueue::producer_dequeue(
+ const camera2_frame_queue_dst_ops_t *q,
+ size_t entries, size_t bytes,
+ camera_metadata_t **buffer)
+{
+ ATRACE_CALL();
+ camera_metadata_t *new_buffer =
+ allocate_camera_metadata(entries, bytes);
+ if (new_buffer == NULL) return NO_MEMORY;
+ *buffer = new_buffer;
+ return OK;
+}
+
+int Camera2Device::MetadataQueue::producer_cancel(
+ const camera2_frame_queue_dst_ops_t *q,
+ camera_metadata_t *old_buffer)
+{
+ ATRACE_CALL();
+ free_camera_metadata(old_buffer);
+ return OK;
+}
+
+int Camera2Device::MetadataQueue::producer_enqueue(
+ const camera2_frame_queue_dst_ops_t *q,
+ camera_metadata_t *filled_buffer)
+{
+ MetadataQueue *queue = getInstance(q);
+ return queue->enqueue(filled_buffer);
+}
+
+/**
+ * Camera2Device::StreamAdapter
+ */
+
+#ifndef container_of
+#define container_of(ptr, type, member) \
+ (type *)((char*)(ptr) - offsetof(type, member))
+#endif
+
+Camera2Device::StreamAdapter::StreamAdapter(camera2_device_t *d):
+ mState(RELEASED),
+ mDevice(d),
+ mId(-1),
+ mWidth(0), mHeight(0), mFormat(0), mSize(0), mUsage(0),
+ mMaxProducerBuffers(0), mMaxConsumerBuffers(0),
+ mTotalBuffers(0),
+ mFormatRequested(0),
+ mActiveBuffers(0),
+ mFrameCount(0),
+ mLastTimestamp(0)
+{
+ camera2_stream_ops::dequeue_buffer = dequeue_buffer;
+ camera2_stream_ops::enqueue_buffer = enqueue_buffer;
+ camera2_stream_ops::cancel_buffer = cancel_buffer;
+ camera2_stream_ops::set_crop = set_crop;
+}
+
+Camera2Device::StreamAdapter::~StreamAdapter() {
+ ATRACE_CALL();
+ if (mState != RELEASED) {
+ release();
+ }
+}
+
+status_t Camera2Device::StreamAdapter::connectToDevice(
+ sp<ANativeWindow> consumer,
+ uint32_t width, uint32_t height, int format, size_t size) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: E", __FUNCTION__);
+
+ if (mState != RELEASED) return INVALID_OPERATION;
+ if (consumer == NULL) {
+ ALOGE("%s: Null consumer passed to stream adapter", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ ALOGV("%s: New stream parameters %d x %d, format 0x%x, size %d",
+ __FUNCTION__, width, height, format, size);
+
+ mConsumerInterface = consumer;
+ mWidth = width;
+ mHeight = height;
+ mSize = (format == HAL_PIXEL_FORMAT_BLOB) ? size : 0;
+ mFormatRequested = format;
+
+ // Allocate device-side stream interface
+
+ uint32_t id;
+ uint32_t formatActual;
+ uint32_t usage;
+ uint32_t maxBuffers = 2;
+ res = mDevice->ops->allocate_stream(mDevice,
+ mWidth, mHeight, mFormatRequested, getStreamOps(),
+ &id, &formatActual, &usage, &maxBuffers);
+ if (res != OK) {
+ ALOGE("%s: Device stream allocation failed: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ ALOGV("%s: Allocated stream id %d, actual format 0x%x, "
+ "usage 0x%x, producer wants %d buffers", __FUNCTION__,
+ id, formatActual, usage, maxBuffers);
+
+ mId = id;
+ mFormat = formatActual;
+ mUsage = usage;
+ mMaxProducerBuffers = maxBuffers;
+
+ mState = ALLOCATED;
+
+ // Configure consumer-side ANativeWindow interface
+ res = native_window_api_connect(mConsumerInterface.get(),
+ NATIVE_WINDOW_API_CAMERA);
+ if (res != OK) {
+ ALOGE("%s: Unable to connect to native window for stream %d",
+ __FUNCTION__, mId);
+
+ return res;
+ }
+
+ mState = CONNECTED;
+
+ res = native_window_set_usage(mConsumerInterface.get(), mUsage);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure usage %08x for stream %d",
+ __FUNCTION__, mUsage, mId);
+ return res;
+ }
+
+ res = native_window_set_scaling_mode(mConsumerInterface.get(),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure stream scaling: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ res = setTransform(0);
+ if (res != OK) {
+ return res;
+ }
+
+ if (mFormat == HAL_PIXEL_FORMAT_BLOB) {
+ res = native_window_set_buffers_geometry(mConsumerInterface.get(),
+ mSize, 1, mFormat);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure compressed stream buffer geometry"
+ " %d x %d, size %d for stream %d",
+ __FUNCTION__, mWidth, mHeight, mSize, mId);
+ return res;
+ }
+ } else {
+ res = native_window_set_buffers_geometry(mConsumerInterface.get(),
+ mWidth, mHeight, mFormat);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure stream buffer geometry"
+ " %d x %d, format 0x%x for stream %d",
+ __FUNCTION__, mWidth, mHeight, mFormat, mId);
+ return res;
+ }
+ }
+
+ int maxConsumerBuffers;
+ res = mConsumerInterface->query(mConsumerInterface.get(),
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
+ if (res != OK) {
+ ALOGE("%s: Unable to query consumer undequeued"
+ " buffer count for stream %d", __FUNCTION__, mId);
+ return res;
+ }
+ mMaxConsumerBuffers = maxConsumerBuffers;
+
+ ALOGV("%s: Consumer wants %d buffers", __FUNCTION__,
+ mMaxConsumerBuffers);
+
+ mTotalBuffers = mMaxConsumerBuffers + mMaxProducerBuffers;
+ mActiveBuffers = 0;
+ mFrameCount = 0;
+ mLastTimestamp = 0;
+
+ res = native_window_set_buffer_count(mConsumerInterface.get(),
+ mTotalBuffers);
+ if (res != OK) {
+ ALOGE("%s: Unable to set buffer count for stream %d",
+ __FUNCTION__, mId);
+ return res;
+ }
+
+ // Register allocated buffers with HAL device
+ buffer_handle_t *buffers = new buffer_handle_t[mTotalBuffers];
+ ANativeWindowBuffer **anwBuffers = new ANativeWindowBuffer*[mTotalBuffers];
+ uint32_t bufferIdx = 0;
+ for (; bufferIdx < mTotalBuffers; bufferIdx++) {
+ res = native_window_dequeue_buffer_and_wait(mConsumerInterface.get(),
+ &anwBuffers[bufferIdx]);
+ if (res != OK) {
+ ALOGE("%s: Unable to dequeue buffer %d for initial registration for "
+ "stream %d", __FUNCTION__, bufferIdx, mId);
+ goto cleanUpBuffers;
+ }
+
+ buffers[bufferIdx] = anwBuffers[bufferIdx]->handle;
+ ALOGV("%s: Buffer %p allocated", __FUNCTION__, (void*)buffers[bufferIdx]);
+ }
+
+ ALOGV("%s: Registering %d buffers with camera HAL", __FUNCTION__, mTotalBuffers);
+ res = mDevice->ops->register_stream_buffers(mDevice,
+ mId,
+ mTotalBuffers,
+ buffers);
+ if (res != OK) {
+ ALOGE("%s: Unable to register buffers with HAL device for stream %d",
+ __FUNCTION__, mId);
+ } else {
+ mState = ACTIVE;
+ }
+
+cleanUpBuffers:
+ ALOGV("%s: Cleaning up %d buffers", __FUNCTION__, bufferIdx);
+ for (uint32_t i = 0; i < bufferIdx; i++) {
+ res = mConsumerInterface->cancelBuffer(mConsumerInterface.get(),
+ anwBuffers[i], -1);
+ if (res != OK) {
+ ALOGE("%s: Unable to cancel buffer %d after registration",
+ __FUNCTION__, i);
+ }
+ }
+ delete[] anwBuffers;
+ delete[] buffers;
+
+ return res;
+}
+
+status_t Camera2Device::StreamAdapter::release() {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: Releasing stream %d", __FUNCTION__, mId);
+ if (mState >= ALLOCATED) {
+ res = mDevice->ops->release_stream(mDevice, mId);
+ if (res != OK) {
+ ALOGE("%s: Unable to release stream %d",
+ __FUNCTION__, mId);
+ return res;
+ }
+ }
+ if (mState >= CONNECTED) {
+ res = native_window_api_disconnect(mConsumerInterface.get(),
+ NATIVE_WINDOW_API_CAMERA);
+
+ /* this is not an error. if client calling process dies,
+ the window will also die and all calls to it will return
+ DEAD_OBJECT, thus it's already "disconnected" */
+ if (res == DEAD_OBJECT) {
+ ALOGW("%s: While disconnecting stream %d from native window, the"
+ " native window died from under us", __FUNCTION__, mId);
+ }
+ else if (res != OK) {
+ ALOGE("%s: Unable to disconnect stream %d from native window (error %d %s)",
+ __FUNCTION__, mId, res, strerror(-res));
+ return res;
+ }
+ }
+ mId = -1;
+ mState = RELEASED;
+ return OK;
+}
+
+status_t Camera2Device::StreamAdapter::setTransform(int transform) {
+ ATRACE_CALL();
+ status_t res;
+ if (mState < CONNECTED) {
+ ALOGE("%s: Cannot set transform on unconnected stream", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ res = native_window_set_buffers_transform(mConsumerInterface.get(),
+ transform);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure stream transform to %x: %s (%d)",
+ __FUNCTION__, transform, strerror(-res), res);
+ }
+ return res;
+}
+
+status_t Camera2Device::StreamAdapter::dump(int fd,
+ const Vector<String16>& args) {
+ ATRACE_CALL();
+ String8 result = String8::format(" Stream %d: %d x %d, format 0x%x\n",
+ mId, mWidth, mHeight, mFormat);
+ result.appendFormat(" size %d, usage 0x%x, requested format 0x%x\n",
+ mSize, mUsage, mFormatRequested);
+ result.appendFormat(" total buffers: %d, dequeued buffers: %d\n",
+ mTotalBuffers, mActiveBuffers);
+ result.appendFormat(" frame count: %d, last timestamp %lld\n",
+ mFrameCount, mLastTimestamp);
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+const camera2_stream_ops *Camera2Device::StreamAdapter::getStreamOps() {
+ return static_cast<camera2_stream_ops *>(this);
+}
+
+ANativeWindow* Camera2Device::StreamAdapter::toANW(
+ const camera2_stream_ops_t *w) {
+ return static_cast<const StreamAdapter*>(w)->mConsumerInterface.get();
+}
+
+int Camera2Device::StreamAdapter::dequeue_buffer(const camera2_stream_ops_t *w,
+ buffer_handle_t** buffer) {
+ ATRACE_CALL();
+ int res;
+ StreamAdapter* stream =
+ const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
+ if (stream->mState != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
+ return INVALID_OPERATION;
+ }
+
+ ANativeWindow *a = toANW(w);
+ ANativeWindowBuffer* anb;
+ res = native_window_dequeue_buffer_and_wait(a, &anb);
+ if (res != OK) {
+ ALOGE("Stream %d dequeue: Error from native_window: %s (%d)", stream->mId,
+ strerror(-res), res);
+ return res;
+ }
+
+ *buffer = &(anb->handle);
+ stream->mActiveBuffers++;
+
+ ALOGVV("Stream %d dequeue: Buffer %p dequeued", stream->mId, (void*)(**buffer));
+ return res;
+}
+
+int Camera2Device::StreamAdapter::enqueue_buffer(const camera2_stream_ops_t* w,
+ int64_t timestamp,
+ buffer_handle_t* buffer) {
+ ATRACE_CALL();
+ StreamAdapter *stream =
+ const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
+ stream->mFrameCount++;
+ ALOGVV("Stream %d enqueue: Frame %d (%p) captured at %lld ns",
+ stream->mId, stream->mFrameCount, (void*)(*buffer), timestamp);
+ int state = stream->mState;
+ if (state != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
+ return INVALID_OPERATION;
+ }
+ ANativeWindow *a = toANW(w);
+ status_t err;
+
+ err = native_window_set_buffers_timestamp(a, timestamp);
+ if (err != OK) {
+ ALOGE("%s: Error setting timestamp on native window: %s (%d)",
+ __FUNCTION__, strerror(-err), err);
+ return err;
+ }
+ err = a->queueBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle), -1);
+ if (err != OK) {
+ ALOGE("%s: Error queueing buffer to native window: %s (%d)",
+ __FUNCTION__, strerror(-err), err);
+ return err;
+ }
+
+ stream->mActiveBuffers--;
+ stream->mLastTimestamp = timestamp;
+ return OK;
+}
+
+int Camera2Device::StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w,
+ buffer_handle_t* buffer) {
+ ATRACE_CALL();
+ StreamAdapter *stream =
+ const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w));
+ ALOGVV("Stream %d cancel: Buffer %p",
+ stream->mId, (void*)(*buffer));
+ if (stream->mState != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
+ return INVALID_OPERATION;
+ }
+
+ ANativeWindow *a = toANW(w);
+ int err = a->cancelBuffer(a,
+ container_of(buffer, ANativeWindowBuffer, handle), -1);
+ if (err != OK) {
+ ALOGE("%s: Error canceling buffer to native window: %s (%d)",
+ __FUNCTION__, strerror(-err), err);
+ return err;
+ }
+
+ stream->mActiveBuffers--;
+ return OK;
+}
+
+int Camera2Device::StreamAdapter::set_crop(const camera2_stream_ops_t* w,
+ int left, int top, int right, int bottom) {
+ ATRACE_CALL();
+ int state = static_cast<const StreamAdapter*>(w)->mState;
+ if (state != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
+ return INVALID_OPERATION;
+ }
+ ANativeWindow *a = toANW(w);
+ android_native_rect_t crop = { left, top, right, bottom };
+ return native_window_set_crop(a, &crop);
+}
+
+/**
+ * Camera2Device::ReprocessStreamAdapter
+ */
+
+#ifndef container_of
+#define container_of(ptr, type, member) \
+ (type *)((char*)(ptr) - offsetof(type, member))
+#endif
+
+Camera2Device::ReprocessStreamAdapter::ReprocessStreamAdapter(camera2_device_t *d):
+ mState(RELEASED),
+ mDevice(d),
+ mId(-1),
+ mWidth(0), mHeight(0), mFormat(0),
+ mActiveBuffers(0),
+ mFrameCount(0)
+{
+ ATRACE_CALL();
+ camera2_stream_in_ops::acquire_buffer = acquire_buffer;
+ camera2_stream_in_ops::release_buffer = release_buffer;
+}
+
+Camera2Device::ReprocessStreamAdapter::~ReprocessStreamAdapter() {
+ ATRACE_CALL();
+ if (mState != RELEASED) {
+ release();
+ }
+}
+
+status_t Camera2Device::ReprocessStreamAdapter::connectToDevice(
+ const sp<StreamAdapter> &outputStream) {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: E", __FUNCTION__);
+
+ if (mState != RELEASED) return INVALID_OPERATION;
+ if (outputStream == NULL) {
+ ALOGE("%s: Null base stream passed to reprocess stream adapter",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ mBaseStream = outputStream;
+ mWidth = outputStream->getWidth();
+ mHeight = outputStream->getHeight();
+ mFormat = outputStream->getFormat();
+
+ ALOGV("%s: New reprocess stream parameters %d x %d, format 0x%x",
+ __FUNCTION__, mWidth, mHeight, mFormat);
+
+ // Allocate device-side stream interface
+
+ uint32_t id;
+ res = mDevice->ops->allocate_reprocess_stream_from_stream(mDevice,
+ outputStream->getId(), getStreamOps(),
+ &id);
+ if (res != OK) {
+ ALOGE("%s: Device reprocess stream allocation failed: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ ALOGV("%s: Allocated reprocess stream id %d based on stream %d",
+ __FUNCTION__, id, outputStream->getId());
+
+ mId = id;
+
+ mState = ACTIVE;
+
+ return OK;
+}
+
+status_t Camera2Device::ReprocessStreamAdapter::release() {
+ ATRACE_CALL();
+ status_t res;
+ ALOGV("%s: Releasing stream %d", __FUNCTION__, mId);
+ if (mState >= ACTIVE) {
+ res = mDevice->ops->release_reprocess_stream(mDevice, mId);
+ if (res != OK) {
+ ALOGE("%s: Unable to release stream %d",
+ __FUNCTION__, mId);
+ return res;
+ }
+ }
+
+ List<QueueEntry>::iterator s;
+ for (s = mQueue.begin(); s != mQueue.end(); s++) {
+ sp<BufferReleasedListener> listener = s->releaseListener.promote();
+ if (listener != 0) listener->onBufferReleased(s->handle);
+ }
+ for (s = mInFlightQueue.begin(); s != mInFlightQueue.end(); s++) {
+ sp<BufferReleasedListener> listener = s->releaseListener.promote();
+ if (listener != 0) listener->onBufferReleased(s->handle);
+ }
+ mQueue.clear();
+ mInFlightQueue.clear();
+
+ mState = RELEASED;
+ return OK;
+}
+
+status_t Camera2Device::ReprocessStreamAdapter::pushIntoStream(
+ buffer_handle_t *handle, const wp<BufferReleasedListener> &releaseListener) {
+ ATRACE_CALL();
+ // TODO: Some error checking here would be nice
+ ALOGV("%s: Pushing buffer %p to stream", __FUNCTION__, (void*)(*handle));
+
+ QueueEntry entry;
+ entry.handle = handle;
+ entry.releaseListener = releaseListener;
+ mQueue.push_back(entry);
+ return OK;
+}
+
+status_t Camera2Device::ReprocessStreamAdapter::dump(int fd,
+ const Vector<String16>& args) {
+ ATRACE_CALL();
+ String8 result =
+ String8::format(" Reprocess stream %d: %d x %d, fmt 0x%x\n",
+ mId, mWidth, mHeight, mFormat);
+ result.appendFormat(" acquired buffers: %d\n",
+ mActiveBuffers);
+ result.appendFormat(" frame count: %d\n",
+ mFrameCount);
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+const camera2_stream_in_ops *Camera2Device::ReprocessStreamAdapter::getStreamOps() {
+ return static_cast<camera2_stream_in_ops *>(this);
+}
+
+int Camera2Device::ReprocessStreamAdapter::acquire_buffer(
+ const camera2_stream_in_ops_t *w,
+ buffer_handle_t** buffer) {
+ ATRACE_CALL();
+ int res;
+ ReprocessStreamAdapter* stream =
+ const_cast<ReprocessStreamAdapter*>(
+ static_cast<const ReprocessStreamAdapter*>(w));
+ if (stream->mState != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState);
+ return INVALID_OPERATION;
+ }
+
+ if (stream->mQueue.empty()) {
+ *buffer = NULL;
+ return OK;
+ }
+
+ QueueEntry &entry = *(stream->mQueue.begin());
+
+ *buffer = entry.handle;
+
+ stream->mInFlightQueue.push_back(entry);
+ stream->mQueue.erase(stream->mQueue.begin());
+
+ stream->mActiveBuffers++;
+
+ ALOGV("Stream %d acquire: Buffer %p acquired", stream->mId,
+ (void*)(**buffer));
+ return OK;
+}
+
+int Camera2Device::ReprocessStreamAdapter::release_buffer(
+ const camera2_stream_in_ops_t* w,
+ buffer_handle_t* buffer) {
+ ATRACE_CALL();
+ ReprocessStreamAdapter *stream =
+ const_cast<ReprocessStreamAdapter*>(
+ static_cast<const ReprocessStreamAdapter*>(w) );
+ stream->mFrameCount++;
+ ALOGV("Reprocess stream %d release: Frame %d (%p)",
+ stream->mId, stream->mFrameCount, (void*)*buffer);
+ int state = stream->mState;
+ if (state != ACTIVE) {
+ ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state);
+ return INVALID_OPERATION;
+ }
+ stream->mActiveBuffers--;
+
+ List<QueueEntry>::iterator s;
+ for (s = stream->mInFlightQueue.begin(); s != stream->mInFlightQueue.end(); s++) {
+ if ( s->handle == buffer ) break;
+ }
+ if (s == stream->mInFlightQueue.end()) {
+ ALOGE("%s: Can't find buffer %p in in-flight list!", __FUNCTION__,
+ buffer);
+ return INVALID_OPERATION;
+ }
+
+ sp<BufferReleasedListener> listener = s->releaseListener.promote();
+ if (listener != 0) {
+ listener->onBufferReleased(s->handle);
+ } else {
+ ALOGE("%s: Can't free buffer - missing listener", __FUNCTION__);
+ }
+ stream->mInFlightQueue.erase(s);
+
+ return OK;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
new file mode 100644
index 00000000..41df2e4b
--- /dev/null
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2DEVICE_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2DEVICE_H
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "hardware/camera2.h"
+#include "camera2/CameraMetadata.h"
+
+namespace android {
+
+class Camera2Device : public virtual RefBase {
+ public:
+ typedef camera2::CameraMetadata CameraMetadata;
+
+ Camera2Device(int id);
+
+ ~Camera2Device();
+
+ status_t initialize(camera_module_t *module);
+ status_t disconnect();
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ /**
+ * The device's static characteristics metadata buffer
+ */
+ const CameraMetadata& info() const;
+
+ /**
+ * Submit request for capture. The Camera2Device takes ownership of the
+ * passed-in buffer.
+ */
+ status_t capture(CameraMetadata &request);
+
+ /**
+ * Submit request for streaming. The Camera2Device makes a copy of the
+ * passed-in buffer and the caller retains ownership.
+ */
+ status_t setStreamingRequest(const CameraMetadata &request);
+
+ /**
+ * Clear the streaming request slot.
+ */
+ status_t clearStreamingRequest();
+
+ /**
+ * Wait until a request with the given ID has been dequeued by the
+ * HAL. Returns TIMED_OUT if the timeout duration is reached. Returns
+ * immediately if the latest request received by the HAL has this id.
+ */
+ status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout);
+
+ /**
+ * Create an output stream of the requested size and format.
+ *
+ * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects
+ * an appropriate format; it can be queried with getStreamInfo.
+ *
+ * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be
+ * equal to the size in bytes of the buffers to allocate for the stream. For
+ * other formats, the size parameter is ignored.
+ */
+ status_t createStream(sp<ANativeWindow> consumer,
+ uint32_t width, uint32_t height, int format, size_t size,
+ int *id);
+
+ /**
+ * Create an input reprocess stream that uses buffers from an existing
+ * output stream.
+ */
+ status_t createReprocessStreamFromStream(int outputId, int *id);
+
+ /**
+ * Get information about a given stream.
+ */
+ status_t getStreamInfo(int id,
+ uint32_t *width, uint32_t *height, uint32_t *format);
+
+ /**
+ * Set stream gralloc buffer transform
+ */
+ status_t setStreamTransform(int id, int transform);
+
+ /**
+ * Delete stream. Must not be called if there are requests in flight which
+ * reference that stream.
+ */
+ status_t deleteStream(int id);
+
+ /**
+ * Delete reprocess stream. Must not be called if there are requests in
+ * flight which reference that stream.
+ */
+ status_t deleteReprocessStream(int id);
+
+ /**
+ * Create a metadata buffer with fields that the HAL device believes are
+ * best for the given use case
+ */
+ status_t createDefaultRequest(int templateId, CameraMetadata *request);
+
+ /**
+ * Wait until all requests have been processed. Returns INVALID_OPERATION if
+ * the streaming slot is not empty, or TIMED_OUT if the requests haven't
+ * finished processing in 10 seconds.
+ */
+ status_t waitUntilDrained();
+
+ /**
+ * Abstract class for HAL notification listeners
+ */
+ class NotificationListener {
+ public:
+ // Refer to the Camera2 HAL definition for notification definitions
+ virtual void notifyError(int errorCode, int arg1, int arg2) = 0;
+ virtual void notifyShutter(int frameNumber, nsecs_t timestamp) = 0;
+ virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
+ virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
+ virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId) = 0;
+ protected:
+ virtual ~NotificationListener();
+ };
+
+ /**
+ * Connect HAL notifications to a listener. Overwrites previous
+ * listener. Set to NULL to stop receiving notifications.
+ */
+ status_t setNotifyCallback(NotificationListener *listener);
+
+ /**
+ * Wait for a new frame to be produced, with timeout in nanoseconds.
+ * Returns TIMED_OUT when no frame produced within the specified duration
+ */
+ status_t waitForNextFrame(nsecs_t timeout);
+
+ /**
+ * Get next metadata frame from the frame queue. Returns NULL if the queue
+ * is empty; caller takes ownership of the metadata buffer.
+ */
+ status_t getNextFrame(CameraMetadata *frame);
+
+ /**
+ * Trigger auto-focus. The latest ID used in a trigger autofocus or cancel
+ * autofocus call will be returned by the HAL in all subsequent AF
+ * notifications.
+ */
+ status_t triggerAutofocus(uint32_t id);
+
+ /**
+ * Cancel auto-focus. The latest ID used in a trigger autofocus/cancel
+ * autofocus call will be returned by the HAL in all subsequent AF
+ * notifications.
+ */
+ status_t triggerCancelAutofocus(uint32_t id);
+
+ /**
+ * Trigger pre-capture metering. The latest ID used in a trigger pre-capture
+ * call will be returned by the HAL in all subsequent AE and AWB
+ * notifications.
+ */
+ status_t triggerPrecaptureMetering(uint32_t id);
+
+ /**
+ * Abstract interface for clients that want to listen to reprocess buffer
+ * release events
+ */
+ struct BufferReleasedListener: public virtual RefBase {
+ virtual void onBufferReleased(buffer_handle_t *handle) = 0;
+ };
+
+ /**
+ * Push a buffer to be reprocessed into a reprocessing stream, and
+ * provide a listener to call once the buffer is returned by the HAL
+ */
+ status_t pushReprocessBuffer(int reprocessStreamId,
+ buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
+
+ private:
+ const int mId;
+ camera2_device_t *mDevice;
+
+ CameraMetadata mDeviceInfo;
+ vendor_tag_query_ops_t *mVendorTagOps;
+
+ /**
+ * Queue class for both sending requests to a camera2 device, and for
+ * receiving frames from a camera2 device.
+ */
+ class MetadataQueue: public camera2_request_queue_src_ops_t,
+ public camera2_frame_queue_dst_ops_t {
+ public:
+ MetadataQueue();
+ ~MetadataQueue();
+
+ // Interface to camera2 HAL device, either for requests (device is
+ // consumer) or for frames (device is producer)
+ const camera2_request_queue_src_ops_t* getToConsumerInterface();
+ void setFromConsumerInterface(camera2_device_t *d);
+
+ // Connect queue consumer endpoint to a camera2 device
+ status_t setConsumerDevice(camera2_device_t *d);
+ // Connect queue producer endpoint to a camera2 device
+ status_t setProducerDevice(camera2_device_t *d);
+
+ const camera2_frame_queue_dst_ops_t* getToProducerInterface();
+
+ // Real interfaces. On enqueue, queue takes ownership of buffer pointer
+ // On dequeue, user takes ownership of buffer pointer.
+ status_t enqueue(camera_metadata_t *buf);
+ status_t dequeue(camera_metadata_t **buf, bool incrementCount = false);
+ int getBufferCount();
+ status_t waitForBuffer(nsecs_t timeout);
+ // Wait until a buffer with the given ID is dequeued. Will return
+ // immediately if the latest buffer dequeued has that ID.
+ status_t waitForDequeue(int32_t id, nsecs_t timeout);
+
+ // Set repeating buffer(s); if the queue is empty on a dequeue call, the
+ // queue copies the contents of the stream slot into the queue, and then
+ // dequeues the first new entry. The metadata buffers passed in are
+ // copied.
+ status_t setStreamSlot(camera_metadata_t *buf);
+ status_t setStreamSlot(const List<camera_metadata_t*> &bufs);
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ private:
+ status_t signalConsumerLocked();
+ status_t freeBuffers(List<camera_metadata_t*>::iterator start,
+ List<camera_metadata_t*>::iterator end);
+
+ camera2_device_t *mDevice;
+
+ Mutex mMutex;
+ Condition notEmpty;
+
+ int mFrameCount;
+ int32_t mLatestRequestId;
+ Condition mNewRequestId;
+
+ int mCount;
+ List<camera_metadata_t*> mEntries;
+ int mStreamSlotCount;
+ List<camera_metadata_t*> mStreamSlot;
+
+ bool mSignalConsumer;
+
+ static MetadataQueue* getInstance(
+ const camera2_frame_queue_dst_ops_t *q);
+ static MetadataQueue* getInstance(
+ const camera2_request_queue_src_ops_t *q);
+
+ static int consumer_buffer_count(
+ const camera2_request_queue_src_ops_t *q);
+
+ static int consumer_dequeue(const camera2_request_queue_src_ops_t *q,
+ camera_metadata_t **buffer);
+
+ static int consumer_free(const camera2_request_queue_src_ops_t *q,
+ camera_metadata_t *old_buffer);
+
+ static int producer_dequeue(const camera2_frame_queue_dst_ops_t *q,
+ size_t entries, size_t bytes,
+ camera_metadata_t **buffer);
+
+ static int producer_cancel(const camera2_frame_queue_dst_ops_t *q,
+ camera_metadata_t *old_buffer);
+
+ static int producer_enqueue(const camera2_frame_queue_dst_ops_t *q,
+ camera_metadata_t *filled_buffer);
+
+ }; // class MetadataQueue
+
+ MetadataQueue mRequestQueue;
+ MetadataQueue mFrameQueue;
+
+ /**
+ * Adapter from an ANativeWindow interface to camera2 device stream ops.
+ * Also takes care of allocating/deallocating stream in device interface
+ */
+ class StreamAdapter: public camera2_stream_ops, public virtual RefBase {
+ public:
+ StreamAdapter(camera2_device_t *d);
+
+ ~StreamAdapter();
+
+ /**
+ * Create a HAL device stream of the requested size and format.
+ *
+ * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device
+ * selects an appropriate format; it can be queried with getFormat.
+ *
+ * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must
+ * be equal to the size in bytes of the buffers to allocate for the
+ * stream. For other formats, the size parameter is ignored.
+ */
+ status_t connectToDevice(sp<ANativeWindow> consumer,
+ uint32_t width, uint32_t height, int format, size_t size);
+
+ status_t release();
+
+ status_t setTransform(int transform);
+
+ // Get stream parameters.
+ // Only valid after a successful connectToDevice call.
+ int getId() const { return mId; }
+ uint32_t getWidth() const { return mWidth; }
+ uint32_t getHeight() const { return mHeight; }
+ uint32_t getFormat() const { return mFormat; }
+
+ // Dump stream information
+ status_t dump(int fd, const Vector<String16>& args);
+
+ private:
+ enum {
+ ERROR = -1,
+ RELEASED = 0,
+ ALLOCATED,
+ CONNECTED,
+ ACTIVE
+ } mState;
+
+ sp<ANativeWindow> mConsumerInterface;
+ camera2_device_t *mDevice;
+
+ uint32_t mId;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mFormat;
+ size_t mSize;
+ uint32_t mUsage;
+ uint32_t mMaxProducerBuffers;
+ uint32_t mMaxConsumerBuffers;
+ uint32_t mTotalBuffers;
+ int mFormatRequested;
+
+ /** Debugging information */
+ uint32_t mActiveBuffers;
+ uint32_t mFrameCount;
+ int64_t mLastTimestamp;
+
+ const camera2_stream_ops *getStreamOps();
+
+ static ANativeWindow* toANW(const camera2_stream_ops_t *w);
+
+ static int dequeue_buffer(const camera2_stream_ops_t *w,
+ buffer_handle_t** buffer);
+
+ static int enqueue_buffer(const camera2_stream_ops_t* w,
+ int64_t timestamp,
+ buffer_handle_t* buffer);
+
+ static int cancel_buffer(const camera2_stream_ops_t* w,
+ buffer_handle_t* buffer);
+
+ static int set_crop(const camera2_stream_ops_t* w,
+ int left, int top, int right, int bottom);
+ }; // class StreamAdapter
+
+ typedef List<sp<StreamAdapter> > StreamList;
+ StreamList mStreams;
+
+ /**
+ * Adapter from an ANativeWindow interface to camera2 device stream ops.
+ * Also takes care of allocating/deallocating stream in device interface
+ */
+ class ReprocessStreamAdapter: public camera2_stream_in_ops, public virtual RefBase {
+ public:
+ ReprocessStreamAdapter(camera2_device_t *d);
+
+ ~ReprocessStreamAdapter();
+
+ /**
+ * Create a HAL device reprocess stream based on an existing output stream.
+ */
+ status_t connectToDevice(const sp<StreamAdapter> &outputStream);
+
+ status_t release();
+
+ /**
+ * Push buffer into stream for reprocessing. Takes ownership until it notifies
+ * that the buffer has been released
+ */
+ status_t pushIntoStream(buffer_handle_t *handle,
+ const wp<BufferReleasedListener> &releaseListener);
+
+ /**
+ * Get stream parameters.
+ * Only valid after a successful connectToDevice call.
+ */
+ int getId() const { return mId; }
+ uint32_t getWidth() const { return mWidth; }
+ uint32_t getHeight() const { return mHeight; }
+ uint32_t getFormat() const { return mFormat; }
+
+ // Dump stream information
+ status_t dump(int fd, const Vector<String16>& args);
+
+ private:
+ enum {
+ ERROR = -1,
+ RELEASED = 0,
+ ACTIVE
+ } mState;
+
+ sp<ANativeWindow> mConsumerInterface;
+ wp<StreamAdapter> mBaseStream;
+
+ struct QueueEntry {
+ buffer_handle_t *handle;
+ wp<BufferReleasedListener> releaseListener;
+ };
+
+ List<QueueEntry> mQueue;
+
+ List<QueueEntry> mInFlightQueue;
+
+ camera2_device_t *mDevice;
+
+ uint32_t mId;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mFormat;
+
+ /** Debugging information */
+ uint32_t mActiveBuffers;
+ uint32_t mFrameCount;
+ int64_t mLastTimestamp;
+
+ const camera2_stream_in_ops *getStreamOps();
+
+ static int acquire_buffer(const camera2_stream_in_ops_t *w,
+ buffer_handle_t** buffer);
+
+ static int release_buffer(const camera2_stream_in_ops_t* w,
+ buffer_handle_t* buffer);
+
+ }; // class ReprocessStreamAdapter
+
+ typedef List<sp<ReprocessStreamAdapter> > ReprocessStreamList;
+ ReprocessStreamList mReprocessStreams;
+
+ // Receives HAL notifications and routes them to the NotificationListener
+ static void notificationCallback(int32_t msg_type,
+ int32_t ext1,
+ int32_t ext2,
+ int32_t ext3,
+ void *user);
+
+}; // class Camera2Device
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp
new file mode 100644
index 00000000..b930c02f
--- /dev/null
+++ b/services/camera/libcameraservice/CameraClient.cpp
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "CameraClient"
+//#define LOG_NDEBUG 0
+
+#include <cutils/properties.h>
+#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
+
+#include "CameraClient.h"
+#include "CameraHardwareInterface.h"
+#include "CameraService.h"
+
+namespace android {
+
+#define LOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
+#define LOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
+
+static int getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+static int getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+CameraClient::CameraClient(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ int cameraId, int cameraFacing, int clientPid, int servicePid):
+ Client(cameraService, cameraClient,
+ cameraId, cameraFacing, clientPid, servicePid)
+{
+ int callingPid = getCallingPid();
+ LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId);
+
+ mHardware = NULL;
+ mMsgEnabled = 0;
+ mSurface = 0;
+ mPreviewWindow = 0;
+ mDestructionStarted = false;
+
+ // Callback is disabled by default
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
+ mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
+ mPlayShutterSound = true;
+ LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
+}
+
+status_t CameraClient::initialize(camera_module_t *module) {
+ int callingPid = getCallingPid();
+ LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId);
+
+ char camera_device_name[10];
+ status_t res;
+ snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
+
+ mHardware = new CameraHardwareInterface(camera_device_name);
+ res = mHardware->initialize(&module->common);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ mHardware.clear();
+ return NO_INIT;
+ }
+
+ mHardware->setCallbacks(notifyCallback,
+ dataCallback,
+ dataCallbackTimestamp,
+ (void *)mCameraId);
+
+ // Enable zoom, error, focus, and metadata messages by default
+ enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
+ CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);
+
+ LOG1("CameraClient::initialize X (pid %d, id %d)", callingPid, mCameraId);
+ return OK;
+}
+
+
+// tear down the client
+CameraClient::~CameraClient() {
+ // this lock should never be NULL
+ Mutex* lock = mCameraService->getClientLockById(mCameraId);
+ lock->lock();
+ mDestructionStarted = true;
+ // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect
+ lock->unlock();
+ int callingPid = getCallingPid();
+ LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this);
+
+ disconnect();
+ LOG1("CameraClient::~CameraClient X (pid %d, this %p)", callingPid, this);
+}
+
+status_t CameraClient::dump(int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n",
+ mCameraId,
+ getCameraClient()->asBinder().get(),
+ mClientPid);
+ len = (len > SIZE - 1) ? SIZE - 1 : len;
+ write(fd, buffer, len);
+ return mHardware->dump(fd, args);
+}
+
+// ----------------------------------------------------------------------------
+
+status_t CameraClient::checkPid() const {
+ int callingPid = getCallingPid();
+ if (callingPid == mClientPid) return NO_ERROR;
+
+ ALOGW("attempt to use a locked camera from a different process"
+ " (old pid %d, new pid %d)", mClientPid, callingPid);
+ return EBUSY;
+}
+
+status_t CameraClient::checkPidAndHardware() const {
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+ if (mHardware == 0) {
+ ALOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid());
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+status_t CameraClient::lock() {
+ int callingPid = getCallingPid();
+ LOG1("lock (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
+ // lock camera to this client if the the camera is unlocked
+ if (mClientPid == 0) {
+ mClientPid = callingPid;
+ return NO_ERROR;
+ }
+
+ // returns NO_ERROR if the client already owns the camera, EBUSY otherwise
+ return checkPid();
+}
+
+status_t CameraClient::unlock() {
+ int callingPid = getCallingPid();
+ LOG1("unlock (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
+ // allow anyone to use camera (after they lock the camera)
+ status_t result = checkPid();
+ if (result == NO_ERROR) {
+ if (mHardware->recordingEnabled()) {
+ ALOGE("Not allowed to unlock camera during recording.");
+ return INVALID_OPERATION;
+ }
+ mClientPid = 0;
+ LOG1("clear mCameraClient (pid %d)", callingPid);
+ // we need to remove the reference to ICameraClient so that when the app
+ // goes away, the reference count goes to 0.
+ mCameraClient.clear();
+ }
+ return result;
+}
+
+// connect a new client to the camera
+status_t CameraClient::connect(const sp<ICameraClient>& client) {
+ int callingPid = getCallingPid();
+ LOG1("connect E (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
+ if (mClientPid != 0 && checkPid() != NO_ERROR) {
+ ALOGW("Tried to connect to a locked camera (old pid %d, new pid %d)",
+ mClientPid, callingPid);
+ return EBUSY;
+ }
+
+ if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) {
+ LOG1("Connect to the same client");
+ return NO_ERROR;
+ }
+
+ mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
+ mClientPid = callingPid;
+ mCameraClient = client;
+
+ LOG1("connect X (pid %d)", callingPid);
+ return NO_ERROR;
+}
+
+static void disconnectWindow(const sp<ANativeWindow>& window) {
+ if (window != 0) {
+ status_t result = native_window_api_disconnect(window.get(),
+ NATIVE_WINDOW_API_CAMERA);
+ if (result != NO_ERROR) {
+ ALOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result),
+ result);
+ }
+ }
+}
+
+void CameraClient::disconnect() {
+ int callingPid = getCallingPid();
+ LOG1("disconnect E (pid %d)", callingPid);
+ Mutex::Autolock lock(mLock);
+
+ // Allow both client and the media server to disconnect at all times
+ if (callingPid != mClientPid && callingPid != mServicePid) {
+ ALOGW("different client - don't disconnect");
+ return;
+ }
+
+ if (mClientPid <= 0) {
+ LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
+ return;
+ }
+
+ // Make sure disconnect() is done once and once only, whether it is called
+ // from the user directly, or called by the destructor.
+ if (mHardware == 0) return;
+
+ LOG1("hardware teardown");
+ // Before destroying mHardware, we must make sure it's in the
+ // idle state.
+ // Turn off all messages.
+ disableMsgType(CAMERA_MSG_ALL_MSGS);
+ mHardware->stopPreview();
+ mHardware->cancelPicture();
+ // Release the hardware resources.
+ mHardware->release();
+
+ // Release the held ANativeWindow resources.
+ if (mPreviewWindow != 0) {
+ disconnectWindow(mPreviewWindow);
+ mPreviewWindow = 0;
+ mHardware->setPreviewWindow(mPreviewWindow);
+ }
+ mHardware.clear();
+
+ CameraService::Client::disconnect();
+
+ LOG1("disconnect X (pid %d)", callingPid);
+}
+
+// ----------------------------------------------------------------------------
+
+status_t CameraClient::setPreviewWindow(const sp<IBinder>& binder,
+ const sp<ANativeWindow>& window) {
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ // return if no change in surface.
+ if (binder == mSurface) {
+ return NO_ERROR;
+ }
+
+ if (window != 0) {
+ result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
+ if (result != NO_ERROR) {
+ ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
+ result);
+ return result;
+ }
+ }
+
+ // If preview has been already started, register preview buffers now.
+ if (mHardware->previewEnabled()) {
+ if (window != 0) {
+ native_window_set_scaling_mode(window.get(),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ native_window_set_buffers_transform(window.get(), mOrientation);
+ result = mHardware->setPreviewWindow(window);
+ }
+ }
+
+ if (result == NO_ERROR) {
+ // Everything has succeeded. Disconnect the old window and remember the
+ // new window.
+ disconnectWindow(mPreviewWindow);
+ mSurface = binder;
+ mPreviewWindow = window;
+ } else {
+ // Something went wrong after we connected to the new window, so
+ // disconnect here.
+ disconnectWindow(window);
+ }
+
+ return result;
+}
+
+// set the Surface that the preview will use
+status_t CameraClient::setPreviewDisplay(const sp<Surface>& surface) {
+ LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+
+ sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);
+ sp<ANativeWindow> window(surface);
+ return setPreviewWindow(binder, window);
+}
+
+// set the SurfaceTexture that the preview will use
+status_t CameraClient::setPreviewTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) {
+ LOG1("setPreviewTexture(%p) (pid %d)", surfaceTexture.get(),
+ getCallingPid());
+
+ sp<IBinder> binder;
+ sp<ANativeWindow> window;
+ if (surfaceTexture != 0) {
+ binder = surfaceTexture->asBinder();
+ window = new SurfaceTextureClient(surfaceTexture);
+ }
+ return setPreviewWindow(binder, window);
+}
+
+// set the preview callback flag to affect how the received frames from
+// preview are handled.
+void CameraClient::setPreviewCallbackFlag(int callback_flag) {
+ LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
+
+ mPreviewCallbackFlag = callback_flag;
+ if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+ enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ } else {
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
+}
+
+// start preview mode
+status_t CameraClient::startPreview() {
+ LOG1("startPreview (pid %d)", getCallingPid());
+ return startCameraMode(CAMERA_PREVIEW_MODE);
+}
+
+// start recording mode
+status_t CameraClient::startRecording() {
+ LOG1("startRecording (pid %d)", getCallingPid());
+ return startCameraMode(CAMERA_RECORDING_MODE);
+}
+
+// start preview or recording
+status_t CameraClient::startCameraMode(camera_mode mode) {
+ LOG1("startCameraMode(%d)", mode);
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ switch(mode) {
+ case CAMERA_PREVIEW_MODE:
+ if (mSurface == 0 && mPreviewWindow == 0) {
+ LOG1("mSurface is not set yet.");
+ // still able to start preview in this case.
+ }
+ return startPreviewMode();
+ case CAMERA_RECORDING_MODE:
+ if (mSurface == 0 && mPreviewWindow == 0) {
+ ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
+ return startRecordingMode();
+ default:
+ return UNKNOWN_ERROR;
+ }
+}
+
+status_t CameraClient::startPreviewMode() {
+ LOG1("startPreviewMode");
+ status_t result = NO_ERROR;
+
+ // if preview has been enabled, nothing needs to be done
+ if (mHardware->previewEnabled()) {
+ return NO_ERROR;
+ }
+
+ if (mPreviewWindow != 0) {
+ native_window_set_scaling_mode(mPreviewWindow.get(),
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ native_window_set_buffers_transform(mPreviewWindow.get(),
+ mOrientation);
+ }
+ mHardware->setPreviewWindow(mPreviewWindow);
+ result = mHardware->startPreview();
+
+ return result;
+}
+
+status_t CameraClient::startRecordingMode() {
+ LOG1("startRecordingMode");
+ status_t result = NO_ERROR;
+
+ // if recording has been enabled, nothing needs to be done
+ if (mHardware->recordingEnabled()) {
+ return NO_ERROR;
+ }
+
+ // if preview has not been started, start preview first
+ if (!mHardware->previewEnabled()) {
+ result = startPreviewMode();
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+
+ // start recording mode
+ enableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+ result = mHardware->startRecording();
+ if (result != NO_ERROR) {
+ ALOGE("mHardware->startRecording() failed with status %d", result);
+ }
+ return result;
+}
+
+// stop preview mode
+void CameraClient::stopPreview() {
+ LOG1("stopPreview (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
+
+
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ mHardware->stopPreview();
+
+ mPreviewBuffer.clear();
+}
+
+// stop recording mode
+void CameraClient::stopRecording() {
+ LOG1("stopRecording (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
+
+ disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mHardware->stopRecording();
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+
+ mPreviewBuffer.clear();
+}
+
+// release a recording frame
+void CameraClient::releaseRecordingFrame(const sp<IMemory>& mem) {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
+ mHardware->releaseRecordingFrame(mem);
+}
+
+status_t CameraClient::storeMetaDataInBuffers(bool enabled)
+{
+ LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false");
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
+ return mHardware->storeMetaDataInBuffers(enabled);
+}
+
+bool CameraClient::previewEnabled() {
+ LOG1("previewEnabled (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return false;
+ return mHardware->previewEnabled();
+}
+
+bool CameraClient::recordingEnabled() {
+ LOG1("recordingEnabled (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return false;
+ return mHardware->recordingEnabled();
+}
+
+status_t CameraClient::autoFocus() {
+ LOG1("autoFocus (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ return mHardware->autoFocus();
+}
+
+status_t CameraClient::cancelAutoFocus() {
+ LOG1("cancelAutoFocus (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ return mHardware->cancelAutoFocus();
+}
+
+// take a picture - image is returned in callback
+status_t CameraClient::takePicture(int msgType) {
+ LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
+ (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
+ ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
+ " cannot be both enabled");
+ return BAD_VALUE;
+ }
+
+ // We only accept picture related message types
+ // and ignore other types of messages for takePicture().
+ int picMsgType = msgType
+ & (CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_RAW_IMAGE_NOTIFY |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+
+ enableMsgType(picMsgType);
+
+ return mHardware->takePicture();
+}
+
+// set preview/capture parameters - key/value pairs
+status_t CameraClient::setParameters(const String8& params) {
+ LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ CameraParameters p(params);
+ return mHardware->setParameters(p);
+}
+
+// get preview/capture parameters - key/value pairs
+String8 CameraClient::getParameters() const {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return String8();
+
+ String8 params(mHardware->getParameters().flatten());
+ LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string());
+ return params;
+}
+
+// enable shutter sound
+status_t CameraClient::enableShutterSound(bool enable) {
+ LOG1("enableShutterSound (pid %d)", getCallingPid());
+
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ if (enable) {
+ mPlayShutterSound = true;
+ return OK;
+ }
+
+ // Disabling shutter sound may not be allowed. In that case only
+ // allow the mediaserver process to disable the sound.
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.camera.sound.forced", value, "0");
+ if (strcmp(value, "0") != 0) {
+ // Disabling shutter sound is not allowed. Deny if the current
+ // process is not mediaserver.
+ if (getCallingPid() != getpid()) {
+ ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid());
+ return PERMISSION_DENIED;
+ }
+ }
+
+ mPlayShutterSound = false;
+ return OK;
+}
+
+status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
+ LOG1("sendCommand (pid %d)", getCallingPid());
+ int orientation;
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPidAndHardware();
+ if (result != NO_ERROR) return result;
+
+ if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
+ // Mirror the preview if the camera is front-facing.
+ orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT);
+ if (orientation == -1) return BAD_VALUE;
+
+ if (mOrientation != orientation) {
+ mOrientation = orientation;
+ if (mPreviewWindow != 0) {
+ native_window_set_buffers_transform(mPreviewWindow.get(),
+ mOrientation);
+ }
+ }
+ return OK;
+ } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) {
+ switch (arg1) {
+ case 0:
+ return enableShutterSound(false);
+ case 1:
+ return enableShutterSound(true);
+ default:
+ return BAD_VALUE;
+ }
+ return OK;
+ } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
+ mCameraService->playSound(CameraService::SOUND_RECORDING);
+ } else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) {
+ // Silently ignore this command
+ return INVALID_OPERATION;
+ } else if (cmd == CAMERA_CMD_PING) {
+ // If mHardware is 0, checkPidAndHardware will return error.
+ return OK;
+ }
+
+ return mHardware->sendCommand(cmd, arg1, arg2);
+}
+
+// ----------------------------------------------------------------------------
+
+void CameraClient::enableMsgType(int32_t msgType) {
+ android_atomic_or(msgType, &mMsgEnabled);
+ mHardware->enableMsgType(msgType);
+}
+
+void CameraClient::disableMsgType(int32_t msgType) {
+ android_atomic_and(~msgType, &mMsgEnabled);
+ mHardware->disableMsgType(msgType);
+}
+
+#define CHECK_MESSAGE_INTERVAL 10 // 10ms
+bool CameraClient::lockIfMessageWanted(int32_t msgType) {
+ int sleepCount = 0;
+ while (mMsgEnabled & msgType) {
+ if (mLock.tryLock() == NO_ERROR) {
+ if (sleepCount > 0) {
+ LOG1("lockIfMessageWanted(%d): waited for %d ms",
+ msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
+ }
+ return true;
+ }
+ if (sleepCount++ == 0) {
+ LOG1("lockIfMessageWanted(%d): enter sleep", msgType);
+ }
+ usleep(CHECK_MESSAGE_INTERVAL * 1000);
+ }
+ ALOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType);
+ return false;
+}
+
+// Callback messages can be dispatched to internal handlers or pass to our
+// client's callback functions, depending on the message type.
+//
+// notifyCallback:
+// CAMERA_MSG_SHUTTER handleShutter
+// (others) c->notifyCallback
+// dataCallback:
+// CAMERA_MSG_PREVIEW_FRAME handlePreviewData
+// CAMERA_MSG_POSTVIEW_FRAME handlePostview
+// CAMERA_MSG_RAW_IMAGE handleRawPicture
+// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture
+// (others) c->dataCallback
+// dataCallbackTimestamp
+// (others) c->dataCallbackTimestamp
+//
+// NOTE: the *Callback functions grab mLock of the client before passing
+// control to handle* functions. So the handle* functions must release the
+// lock before calling the ICameraClient's callbacks, so those callbacks can
+// invoke methods in the Client class again (For example, the preview frame
+// callback may want to releaseRecordingFrame). The handle* functions must
+// release the lock after all accesses to member variables, so it must be
+// handled very carefully.
+
+void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,
+ int32_t ext2, void* user) {
+ LOG2("notifyCallback(%d)", msgType);
+
+ Mutex* lock = getClientLockFromCookie(user);
+ if (lock == NULL) return;
+ Mutex::Autolock alock(*lock);
+
+ CameraClient* client =
+ static_cast<CameraClient*>(getClientFromCookie(user));
+ if (client == NULL) return;
+
+ if (!client->lockIfMessageWanted(msgType)) return;
+
+ switch (msgType) {
+ case CAMERA_MSG_SHUTTER:
+ // ext1 is the dimension of the yuv picture.
+ client->handleShutter();
+ break;
+ default:
+ client->handleGenericNotify(msgType, ext1, ext2);
+ break;
+ }
+}
+
+void CameraClient::dataCallback(int32_t msgType,
+ const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
+ LOG2("dataCallback(%d)", msgType);
+
+ Mutex* lock = getClientLockFromCookie(user);
+ if (lock == NULL) return;
+ Mutex::Autolock alock(*lock);
+
+ CameraClient* client =
+ static_cast<CameraClient*>(getClientFromCookie(user));
+ if (client == NULL) return;
+
+ if (!client->lockIfMessageWanted(msgType)) return;
+ if (dataPtr == 0 && metadata == NULL) {
+ ALOGE("Null data returned in data callback");
+ client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ return;
+ }
+
+ switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
+ case CAMERA_MSG_PREVIEW_FRAME:
+ client->handlePreviewData(msgType, dataPtr, metadata);
+ break;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ client->handlePostview(dataPtr);
+ break;
+ case CAMERA_MSG_RAW_IMAGE:
+ client->handleRawPicture(dataPtr);
+ break;
+ case CAMERA_MSG_COMPRESSED_IMAGE:
+ client->handleCompressedPicture(dataPtr);
+ break;
+ default:
+ client->handleGenericData(msgType, dataPtr, metadata);
+ break;
+ }
+}
+
+void CameraClient::dataCallbackTimestamp(nsecs_t timestamp,
+ int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
+ LOG2("dataCallbackTimestamp(%d)", msgType);
+
+ Mutex* lock = getClientLockFromCookie(user);
+ if (lock == NULL) return;
+ Mutex::Autolock alock(*lock);
+
+ CameraClient* client =
+ static_cast<CameraClient*>(getClientFromCookie(user));
+ if (client == NULL) return;
+
+ if (!client->lockIfMessageWanted(msgType)) return;
+
+ if (dataPtr == 0) {
+ ALOGE("Null data returned in data with timestamp callback");
+ client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ return;
+ }
+
+ client->handleGenericDataTimestamp(timestamp, msgType, dataPtr);
+}
+
+// snapshot taken callback
+void CameraClient::handleShutter(void) {
+ if (mPlayShutterSound) {
+ mCameraService->playSound(CameraService::SOUND_SHUTTER);
+ }
+
+ sp<ICameraClient> c = mCameraClient;
+ if (c != 0) {
+ mLock.unlock();
+ c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
+ if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return;
+ }
+ disableMsgType(CAMERA_MSG_SHUTTER);
+
+ mLock.unlock();
+}
+
+// preview callback - frame buffer update
+void CameraClient::handlePreviewData(int32_t msgType,
+ const sp<IMemory>& mem,
+ camera_frame_metadata_t *metadata) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+
+ // local copy of the callback flags
+ int flags = mPreviewCallbackFlag;
+
+ // is callback enabled?
+ if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
+ // If the enable bit is off, the copy-out and one-shot bits are ignored
+ LOG2("frame callback is disabled");
+ mLock.unlock();
+ return;
+ }
+
+ // hold a strong pointer to the client
+ sp<ICameraClient> c = mCameraClient;
+
+ // clear callback flags if no client or one-shot mode
+ if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
+ LOG2("Disable preview callback");
+ mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
+
+ if (c != 0) {
+ // Is the received frame copied out or not?
+ if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ LOG2("frame is copied");
+ copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
+ } else {
+ LOG2("frame is forwarded");
+ mLock.unlock();
+ c->dataCallback(msgType, mem, metadata);
+ }
+ } else {
+ mLock.unlock();
+ }
+}
+
+// picture callback - postview image ready
+void CameraClient::handlePostview(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
+
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL);
+ }
+}
+
+// picture callback - raw image ready
+void CameraClient::handleRawPicture(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_RAW_IMAGE);
+
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);
+ }
+}
+
+// picture callback - compressed picture ready
+void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) {
+ disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
+
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL);
+ }
+}
+
+
+void CameraClient::handleGenericNotify(int32_t msgType,
+ int32_t ext1, int32_t ext2) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->notifyCallback(msgType, ext1, ext2);
+ }
+}
+
+void CameraClient::handleGenericData(int32_t msgType,
+ const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallback(msgType, dataPtr, metadata);
+ }
+}
+
+void CameraClient::handleGenericDataTimestamp(nsecs_t timestamp,
+ int32_t msgType, const sp<IMemory>& dataPtr) {
+ sp<ICameraClient> c = mCameraClient;
+ mLock.unlock();
+ if (c != 0) {
+ c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
+ }
+}
+
+void CameraClient::copyFrameAndPostCopiedFrame(
+ int32_t msgType, const sp<ICameraClient>& client,
+ const sp<IMemoryHeap>& heap, size_t offset, size_t size,
+ camera_frame_metadata_t *metadata) {
+ LOG2("copyFrameAndPostCopiedFrame");
+ // It is necessary to copy out of pmem before sending this to
+ // the callback. For efficiency, reuse the same MemoryHeapBase
+ // provided it's big enough. Don't allocate the memory or
+ // perform the copy if there's no callback.
+ // hold the preview lock while we grab a reference to the preview buffer
+ sp<MemoryHeapBase> previewBuffer;
+
+ if (mPreviewBuffer == 0) {
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ } else if (size > mPreviewBuffer->virtualSize()) {
+ mPreviewBuffer.clear();
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ }
+ if (mPreviewBuffer == 0) {
+ ALOGE("failed to allocate space for preview buffer");
+ mLock.unlock();
+ return;
+ }
+ previewBuffer = mPreviewBuffer;
+
+ memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size);
+
+ sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
+ if (frame == 0) {
+ ALOGE("failed to allocate space for frame callback");
+ mLock.unlock();
+ return;
+ }
+
+ mLock.unlock();
+ client->dataCallback(msgType, frame, metadata);
+}
+
+int CameraClient::getOrientation(int degrees, bool mirror) {
+ if (!mirror) {
+ if (degrees == 0) return 0;
+ else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
+ else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
+ else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
+ } else { // Do mirror (horizontal flip)
+ if (degrees == 0) { // FLIP_H and ROT_0
+ return HAL_TRANSFORM_FLIP_H;
+ } else if (degrees == 90) { // FLIP_H and ROT_90
+ return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
+ } else if (degrees == 180) { // FLIP_H and ROT_180
+ return HAL_TRANSFORM_FLIP_V;
+ } else if (degrees == 270) { // FLIP_H and ROT_270
+ return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+ }
+ }
+ ALOGE("Invalid setDisplayOrientation degrees=%d", degrees);
+ return -1;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/CameraClient.h b/services/camera/libcameraservice/CameraClient.h
new file mode 100644
index 00000000..2f31c4e7
--- /dev/null
+++ b/services/camera/libcameraservice/CameraClient.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERACLIENT_H
+#define ANDROID_SERVERS_CAMERA_CAMERACLIENT_H
+
+#include "CameraService.h"
+
+namespace android {
+
+class MemoryHeapBase;
+class CameraHardwareInterface;
+
+class CameraClient : public CameraService::Client
+{
+public:
+ // ICamera interface (see ICamera for details)
+ virtual void disconnect();
+ virtual status_t connect(const sp<ICameraClient>& client);
+ virtual status_t lock();
+ virtual status_t unlock();
+ virtual status_t setPreviewDisplay(const sp<Surface>& surface);
+ virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture);
+ virtual void setPreviewCallbackFlag(int flag);
+ virtual status_t startPreview();
+ virtual void stopPreview();
+ virtual bool previewEnabled();
+ virtual status_t storeMetaDataInBuffers(bool enabled);
+ virtual status_t startRecording();
+ virtual void stopRecording();
+ virtual bool recordingEnabled();
+ virtual void releaseRecordingFrame(const sp<IMemory>& mem);
+ virtual status_t autoFocus();
+ virtual status_t cancelAutoFocus();
+ virtual status_t takePicture(int msgType);
+ virtual status_t setParameters(const String8& params);
+ virtual String8 getParameters() const;
+ virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+
+ // Interface used by CameraService
+ CameraClient(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+ ~CameraClient();
+
+ status_t initialize(camera_module_t *module);
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+private:
+
+ // check whether the calling process matches mClientPid.
+ status_t checkPid() const;
+ status_t checkPidAndHardware() const; // also check mHardware != 0
+
+ // these are internal functions used to set up preview buffers
+ status_t registerPreviewBuffers();
+
+ // camera operation mode
+ enum camera_mode {
+ CAMERA_PREVIEW_MODE = 0, // frame automatically released
+ CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
+ };
+ // these are internal functions used for preview/recording
+ status_t startCameraMode(camera_mode mode);
+ status_t startPreviewMode();
+ status_t startRecordingMode();
+
+ // internal function used by sendCommand to enable/disable shutter sound.
+ status_t enableShutterSound(bool enable);
+
+ // these are static callback functions
+ static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
+ static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
+ camera_frame_metadata_t *metadata, void* user);
+ static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ // handlers for messages
+ void handleShutter(void);
+ void handlePreviewData(int32_t msgType, const sp<IMemory>& mem,
+ camera_frame_metadata_t *metadata);
+ void handlePostview(const sp<IMemory>& mem);
+ void handleRawPicture(const sp<IMemory>& mem);
+ void handleCompressedPicture(const sp<IMemory>& mem);
+ void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
+ void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr,
+ camera_frame_metadata_t *metadata);
+ void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+
+ void copyFrameAndPostCopiedFrame(
+ int32_t msgType,
+ const sp<ICameraClient>& client,
+ const sp<IMemoryHeap>& heap,
+ size_t offset, size_t size,
+ camera_frame_metadata_t *metadata);
+
+ int getOrientation(int orientation, bool mirror);
+
+ status_t setPreviewWindow(
+ const sp<IBinder>& binder,
+ const sp<ANativeWindow>& window);
+
+
+ // these are initialized in the constructor.
+ sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
+ int mPreviewCallbackFlag;
+ int mOrientation; // Current display orientation
+ bool mPlayShutterSound;
+
+ // Ensures atomicity among the public methods
+ mutable Mutex mLock;
+ // This is a binder of Surface or SurfaceTexture.
+ sp<IBinder> mSurface;
+ sp<ANativeWindow> mPreviewWindow;
+
+ // If the user want us to return a copy of the preview frame (instead
+ // of the original one), we allocate mPreviewBuffer and reuse it if possible.
+ sp<MemoryHeapBase> mPreviewBuffer;
+
+ // We need to avoid the deadlock when the incoming command thread and
+ // the CameraHardwareInterface callback thread both want to grab mLock.
+ // An extra flag is used to tell the callback thread that it should stop
+ // trying to deliver the callback messages if the client is not
+ // interested in it anymore. For example, if the client is calling
+ // stopPreview(), the preview frame messages do not need to be delivered
+ // anymore.
+
+ // This function takes the same parameter as the enableMsgType() and
+ // disableMsgType() functions in CameraHardwareInterface.
+ void enableMsgType(int32_t msgType);
+ void disableMsgType(int32_t msgType);
+ volatile int32_t mMsgEnabled;
+
+ // This function keeps trying to grab mLock, or give up if the message
+ // is found to be disabled. It returns true if mLock is grabbed.
+ bool lockIfMessageWanted(int32_t msgType);
+};
+
+}
+
+#endif
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index 87a08020..05ac9fa1 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -569,7 +569,7 @@ private:
int rc;
ANativeWindow *a = anw(w);
ANativeWindowBuffer* anb;
- rc = a->dequeueBuffer(a, &anb);
+ rc = native_window_dequeue_buffer_and_wait(a, &anb);
if (!rc) {
*buffer = &anb->handle;
*stride = anb->stride;
@@ -587,8 +587,7 @@ private:
buffer_handle_t* buffer)
{
ANativeWindow *a = anw(w);
- return a->lockBuffer(a,
- container_of(buffer, ANativeWindowBuffer, handle));
+ return 0;
}
static int __enqueue_buffer(struct preview_stream_ops* w,
@@ -596,7 +595,7 @@ private:
{
ANativeWindow *a = anw(w);
return a->queueBuffer(a,
- container_of(buffer, ANativeWindowBuffer, handle));
+ container_of(buffer, ANativeWindowBuffer, handle), -1);
}
static int __cancel_buffer(struct preview_stream_ops* w,
@@ -604,7 +603,7 @@ private:
{
ANativeWindow *a = anw(w);
return a->cancelBuffer(a,
- container_of(buffer, ANativeWindowBuffer, handle));
+ container_of(buffer, ANativeWindowBuffer, handle), -1);
}
static int __set_buffer_count(struct preview_stream_ops* w, int count)
diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
deleted file mode 100644
index cdfb2f5e..00000000
--- a/services/camera/libcameraservice/CameraHardwareStub.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
-**
-** Copyright 2008, 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.
-*/
-
-#define LOG_TAG "CameraHardwareStub"
-#include <utils/Log.h>
-
-#include "CameraHardwareStub.h"
-#include <utils/threads.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "CannedJpeg.h"
-
-namespace android {
-
-CameraHardwareStub::CameraHardwareStub()
- : mParameters(),
- mPreviewHeap(0),
- mRawHeap(0),
- mFakeCamera(0),
- mPreviewFrameSize(0),
- mNotifyCb(0),
- mDataCb(0),
- mDataCbTimestamp(0),
- mCallbackCookie(0),
- mMsgEnabled(0),
- mCurrentPreviewFrame(0)
-{
- initDefaultParameters();
-}
-
-void CameraHardwareStub::initDefaultParameters()
-{
- CameraParameters p;
-
- p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240");
- p.setPreviewSize(320, 240);
- p.setPreviewFrameRate(15);
- p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
-
- p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240");
- p.setPictureSize(320, 240);
- p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
-
- if (setParameters(p) != NO_ERROR) {
- ALOGE("Failed to set default parameters?!");
- }
-}
-
-void CameraHardwareStub::initHeapLocked()
-{
- // Create raw heap.
- int picture_width, picture_height;
- mParameters.getPictureSize(&picture_width, &picture_height);
- mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2);
-
- int preview_width, preview_height;
- mParameters.getPreviewSize(&preview_width, &preview_height);
- ALOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
-
- // Note that we enforce yuv420sp in setParameters().
- int how_big = preview_width * preview_height * 3 / 2;
-
- // If we are being reinitialized to the same size as before, no
- // work needs to be done.
- if (how_big == mPreviewFrameSize)
- return;
-
- mPreviewFrameSize = how_big;
-
- // Make a new mmap'ed heap that can be shared across processes.
- // use code below to test with pmem
- mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount);
- // Make an IMemory for each frame so that we can reuse them in callbacks.
- for (int i = 0; i < kBufferCount; i++) {
- mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize);
- }
-
- // Recreate the fake camera to reflect the current size.
- delete mFakeCamera;
- mFakeCamera = new FakeCamera(preview_width, preview_height);
-}
-
-CameraHardwareStub::~CameraHardwareStub()
-{
- delete mFakeCamera;
- mFakeCamera = 0; // paranoia
-}
-
-status_t CameraHardwareStub::setPreviewWindow(const sp<ANativeWindow>& buf)
-{
- return NO_ERROR;
-}
-
-sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
-{
- return mRawHeap;
-}
-
-void CameraHardwareStub::setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- void* user)
-{
- Mutex::Autolock lock(mLock);
- mNotifyCb = notify_cb;
- mDataCb = data_cb;
- mDataCbTimestamp = data_cb_timestamp;
- mCallbackCookie = user;
-}
-
-void CameraHardwareStub::enableMsgType(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- mMsgEnabled |= msgType;
-}
-
-void CameraHardwareStub::disableMsgType(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- mMsgEnabled &= ~msgType;
-}
-
-bool CameraHardwareStub::msgTypeEnabled(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- return (mMsgEnabled & msgType);
-}
-
-// ---------------------------------------------------------------------------
-
-int CameraHardwareStub::previewThread()
-{
- mLock.lock();
- // the attributes below can change under our feet...
-
- int previewFrameRate = mParameters.getPreviewFrameRate();
-
- // Find the offset within the heap of the current buffer.
- ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize;
-
- sp<MemoryHeapBase> heap = mPreviewHeap;
-
- // this assumes the internal state of fake camera doesn't change
- // (or is thread safe)
- FakeCamera* fakeCamera = mFakeCamera;
-
- sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame];
-
- mLock.unlock();
-
- // TODO: here check all the conditions that could go wrong
- if (buffer != 0) {
- // Calculate how long to wait between frames.
- int delay = (int)(1000000.0f / float(previewFrameRate));
-
- // This is always valid, even if the client died -- the memory
- // is still mapped in our process.
- void *base = heap->base();
-
- // Fill the current frame with the fake camera.
- uint8_t *frame = ((uint8_t *)base) + offset;
- fakeCamera->getNextFrameAsYuv420(frame);
-
- //ALOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
-
- // Notify the client of a new frame.
- if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
- mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, NULL, mCallbackCookie);
-
- // Advance the buffer pointer.
- mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
-
- // Wait for it...
- usleep(delay);
- }
-
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::startPreview()
-{
- Mutex::Autolock lock(mLock);
- if (mPreviewThread != 0) {
- // already running
- return INVALID_OPERATION;
- }
- mPreviewThread = new PreviewThread(this);
- return NO_ERROR;
-}
-
-void CameraHardwareStub::stopPreview()
-{
- sp<PreviewThread> previewThread;
-
- { // scope for the lock
- Mutex::Autolock lock(mLock);
- previewThread = mPreviewThread;
- }
-
- // don't hold the lock while waiting for the thread to quit
- if (previewThread != 0) {
- previewThread->requestExitAndWait();
- }
-
- Mutex::Autolock lock(mLock);
- mPreviewThread.clear();
-}
-
-bool CameraHardwareStub::previewEnabled() {
- return mPreviewThread != 0;
-}
-
-status_t CameraHardwareStub::startRecording()
-{
- return UNKNOWN_ERROR;
-}
-
-void CameraHardwareStub::stopRecording()
-{
-}
-
-bool CameraHardwareStub::recordingEnabled()
-{
- return false;
-}
-
-void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem)
-{
-}
-
-// ---------------------------------------------------------------------------
-
-int CameraHardwareStub::beginAutoFocusThread(void *cookie)
-{
- CameraHardwareStub *c = (CameraHardwareStub *)cookie;
- return c->autoFocusThread();
-}
-
-int CameraHardwareStub::autoFocusThread()
-{
- if (mMsgEnabled & CAMERA_MSG_FOCUS)
- mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie);
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::autoFocus()
-{
- Mutex::Autolock lock(mLock);
- if (createThread(beginAutoFocusThread, this) == false)
- return UNKNOWN_ERROR;
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::cancelAutoFocus()
-{
- return NO_ERROR;
-}
-
-/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
-{
- CameraHardwareStub *c = (CameraHardwareStub *)cookie;
- return c->pictureThread();
-}
-
-int CameraHardwareStub::pictureThread()
-{
- if (mMsgEnabled & CAMERA_MSG_SHUTTER)
- mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);
-
- if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
- //FIXME: use a canned YUV image!
- // In the meantime just make another fake camera picture.
- int w, h;
- mParameters.getPictureSize(&w, &h);
- sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2);
- FakeCamera cam(w, h);
- cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base());
- mDataCb(CAMERA_MSG_RAW_IMAGE, mem, NULL, mCallbackCookie);
- }
-
- if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
- sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
- sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
- memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
- mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL, mCallbackCookie);
- }
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::takePicture()
-{
- stopPreview();
- if (createThread(beginPictureThread, this) == false)
- return UNKNOWN_ERROR;
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::cancelPicture()
-{
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- AutoMutex lock(&mLock);
- if (mFakeCamera != 0) {
- mFakeCamera->dump(fd);
- mParameters.dump(fd, args);
- snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false");
- result.append(buffer);
- } else {
- result.append("No camera client yet.\n");
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::setParameters(const CameraParameters& params)
-{
- Mutex::Autolock lock(mLock);
- // XXX verify params
-
- if (strcmp(params.getPreviewFormat(),
- CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) {
- ALOGE("Only yuv420sp preview is supported");
- return -1;
- }
-
- if (strcmp(params.getPictureFormat(),
- CameraParameters::PIXEL_FORMAT_JPEG) != 0) {
- ALOGE("Only jpeg still pictures are supported");
- return -1;
- }
-
- int w, h;
- params.getPictureSize(&w, &h);
- if (w != kCannedJpegWidth && h != kCannedJpegHeight) {
- ALOGE("Still picture size must be size of canned JPEG (%dx%d)",
- kCannedJpegWidth, kCannedJpegHeight);
- return -1;
- }
-
- mParameters = params;
- initHeapLocked();
-
- return NO_ERROR;
-}
-
-CameraParameters CameraHardwareStub::getParameters() const
-{
- Mutex::Autolock lock(mLock);
- return mParameters;
-}
-
-status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1,
- int32_t arg2)
-{
- return BAD_VALUE;
-}
-
-void CameraHardwareStub::release()
-{
-}
-
-sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
-{
- return new CameraHardwareStub();
-}
-
-static CameraInfo sCameraInfo[] = {
- {
- CAMERA_FACING_BACK,
- 90, /* orientation */
- }
-};
-
-extern "C" int HAL_getNumberOfCameras()
-{
- return sizeof(sCameraInfo) / sizeof(sCameraInfo[0]);
-}
-
-extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo)
-{
- memcpy(cameraInfo, &sCameraInfo[cameraId], sizeof(CameraInfo));
-}
-
-extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId)
-{
- return CameraHardwareStub::createInstance();
-}
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
deleted file mode 100644
index c6d87567..00000000
--- a/services/camera/libcameraservice/CameraHardwareStub.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
-**
-** Copyright 2008, 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 ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
-#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
-
-#include "FakeCamera.h"
-#include <utils/threads.h>
-#include <camera/CameraHardwareInterface.h>
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class CameraHardwareStub : public CameraHardwareInterface {
-public:
- virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf);
- virtual sp<IMemoryHeap> getRawHeap() const;
-
- virtual void setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- void* user);
-
- virtual void enableMsgType(int32_t msgType);
- virtual void disableMsgType(int32_t msgType);
- virtual bool msgTypeEnabled(int32_t msgType);
-
- virtual status_t startPreview();
- virtual void stopPreview();
- virtual bool previewEnabled();
-
- virtual status_t startRecording();
- virtual void stopRecording();
- virtual bool recordingEnabled();
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
-
- virtual status_t autoFocus();
- virtual status_t cancelAutoFocus();
- virtual status_t takePicture();
- virtual status_t cancelPicture();
- virtual status_t dump(int fd, const Vector<String16>& args) const;
- virtual status_t setParameters(const CameraParameters& params);
- virtual CameraParameters getParameters() const;
- virtual status_t sendCommand(int32_t command, int32_t arg1,
- int32_t arg2);
- virtual void release();
-
- static sp<CameraHardwareInterface> createInstance();
-
-private:
- CameraHardwareStub();
- virtual ~CameraHardwareStub();
-
- static const int kBufferCount = 4;
-
- class PreviewThread : public Thread {
- CameraHardwareStub* mHardware;
- public:
- PreviewThread(CameraHardwareStub* hw) :
- Thread(false), mHardware(hw) { }
- virtual void onFirstRef() {
- run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY);
- }
- virtual bool threadLoop() {
- mHardware->previewThread();
- // loop until we need to quit
- return true;
- }
- };
-
- void initDefaultParameters();
- void initHeapLocked();
-
- int previewThread();
-
- static int beginAutoFocusThread(void *cookie);
- int autoFocusThread();
-
- static int beginPictureThread(void *cookie);
- int pictureThread();
-
- mutable Mutex mLock;
-
- CameraParameters mParameters;
-
- sp<MemoryHeapBase> mPreviewHeap;
- sp<MemoryHeapBase> mRawHeap;
- sp<MemoryBase> mBuffers[kBufferCount];
-
- FakeCamera *mFakeCamera;
- bool mPreviewRunning;
- int mPreviewFrameSize;
-
- // protected by mLock
- sp<PreviewThread> mPreviewThread;
-
- notify_callback mNotifyCb;
- data_callback mDataCb;
- data_callback_timestamp mDataCbTimestamp;
- void *mCallbackCookie;
-
- int32_t mMsgEnabled;
-
- // only used from PreviewThread
- int mCurrentPreviewFrame;
-};
-
-}; // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 385be505..124d24d4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -38,14 +38,15 @@
#include <utils/String16.h>
#include "CameraService.h"
-#include "CameraHardwareInterface.h"
+#include "CameraClient.h"
+#include "Camera2Client.h"
namespace android {
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
// Use "adb shell dumpsys media.camera -v 1" to change it.
-static volatile int32_t gLogLevel = 0;
+volatile int32_t gLogLevel = 0;
#define LOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
#define LOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
@@ -133,7 +134,6 @@ status_t CameraService::getCameraInfo(int cameraId,
sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient, int cameraId) {
int callingPid = getCallingPid();
- sp<CameraHardwareInterface> hardware = NULL;
LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
@@ -186,18 +186,35 @@ sp<ICamera> CameraService::connect(
return NULL;
}
- char camera_device_name[10];
- snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId);
+ int deviceVersion;
+ if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) {
+ deviceVersion = info.device_version;
+ } else {
+ deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
+ }
+
+ switch(deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_1_0:
+ client = new CameraClient(this, cameraClient, cameraId,
+ info.facing, callingPid, getpid());
+ break;
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ client = new Camera2Client(this, cameraClient, cameraId,
+ info.facing, callingPid, getpid());
+ break;
+ default:
+ ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+ return NULL;
+ }
- hardware = new CameraHardwareInterface(camera_device_name);
- if (hardware->initialize(&mModule->common) != OK) {
- hardware.clear();
+ if (client->initialize(mModule) != OK) {
return NULL;
}
- client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);
+ cameraClient->asBinder()->linkToDeath(this);
+
mClient[cameraId] = client;
- LOG1("CameraService::connect X (id %d)", cameraId);
+ LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
return client;
}
@@ -205,12 +222,29 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) {
int callingPid = getCallingPid();
LOG1("CameraService::removeClient E (pid %d)", callingPid);
- for (int i = 0; i < mNumberOfCameras; i++) {
- // Declare this before the lock to make absolutely sure the
- // destructor won't be called with the lock held.
- sp<Client> client;
+ // Declare this before the lock to make absolutely sure the
+ // destructor won't be called with the lock held.
+ Mutex::Autolock lock(mServiceLock);
+
+ int outIndex;
+ sp<Client> client = findClientUnsafe(cameraClient->asBinder(), outIndex);
+
+ if (client != 0) {
+ // Found our camera, clear and leave.
+ LOG1("removeClient: clear camera %d", outIndex);
+ mClient[outIndex].clear();
+
+ client->unlinkToDeath(this);
+ }
- Mutex::Autolock lock(mServiceLock);
+ LOG1("CameraService::removeClient X (pid %d)", callingPid);
+}
+
+sp<CameraService::Client> CameraService::findClientUnsafe(
+ const wp<IBinder>& cameraClient, int& outIndex) {
+ sp<Client> client;
+
+ for (int i = 0; i < mNumberOfCameras; i++) {
// This happens when we have already disconnected (or this is
// just another unused camera).
@@ -220,20 +254,21 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) {
// Client::~Client() -> disconnect() -> removeClient().
client = mClient[i].promote();
- if (client == 0) {
+ // Clean up stale client entry
+ if (client == NULL) {
mClient[i].clear();
continue;
}
- if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
- // Found our camera, clear and leave.
- LOG1("removeClient: clear camera %d", i);
- mClient[i].clear();
- break;
+ if (cameraClient == client->getCameraClient()->asBinder()) {
+ // Found our camera
+ outIndex = i;
+ return client;
}
}
- LOG1("CameraService::removeClient X (pid %d)", callingPid);
+ outIndex = -1;
+ return NULL;
}
CameraService::Client* CameraService::getClientByIdUnsafe(int cameraId) {
@@ -246,6 +281,21 @@ Mutex* CameraService::getClientLockById(int cameraId) {
return &mClientLock[cameraId];
}
+sp<CameraService::Client> CameraService::getClientByRemote(
+ const wp<IBinder>& cameraClient) {
+
+ // Declare this before the lock to make absolutely sure the
+ // destructor won't be called with the lock held.
+ sp<Client> client;
+
+ Mutex::Autolock lock(mServiceLock);
+
+ int outIndex;
+ client = findClientUnsafe(cameraClient, outIndex);
+
+ return client;
+}
+
status_t CameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
// Permission checks
@@ -277,10 +327,14 @@ status_t CameraService::onTransact(
// the hardware first.
void CameraService::setCameraBusy(int cameraId) {
android_atomic_write(1, &mBusy[cameraId]);
+
+ ALOGV("setCameraBusy cameraId=%d", cameraId);
}
void CameraService::setCameraFree(int cameraId) {
android_atomic_write(0, &mBusy[cameraId]);
+
+ ALOGV("setCameraFree cameraId=%d", cameraId);
}
// We share the media players for shutter and recording sound for all clients.
@@ -335,34 +389,18 @@ void CameraService::playSound(sound_kind kind) {
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
- const sp<CameraHardwareInterface>& hardware,
- int cameraId, int cameraFacing, int clientPid) {
+ int cameraId, int cameraFacing, int clientPid, int servicePid) {
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
mCameraService = cameraService;
mCameraClient = cameraClient;
- mHardware = hardware;
mCameraId = cameraId;
mCameraFacing = cameraFacing;
mClientPid = clientPid;
- mMsgEnabled = 0;
- mSurface = 0;
- mPreviewWindow = 0;
+ mServicePid = servicePid;
mDestructionStarted = false;
- mHardware->setCallbacks(notifyCallback,
- dataCallback,
- dataCallbackTimestamp,
- (void *)cameraId);
-
- // Enable zoom, error, focus, and metadata messages by default
- enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
- CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE);
-
- // Callback is disabled by default
- mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
- mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
- mPlayShutterSound = true;
+
cameraService->setCameraBusy(cameraId);
cameraService->loadSound();
LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId);
@@ -370,582 +408,10 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
// tear down the client
CameraService::Client::~Client() {
- // this lock should never be NULL
- Mutex* lock = mCameraService->getClientLockById(mCameraId);
- lock->lock();
- mDestructionStarted = true;
- // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect
- lock->unlock();
- int callingPid = getCallingPid();
- LOG1("Client::~Client E (pid %d, this %p)", callingPid, this);
-
- // set mClientPid to let disconnet() tear down the hardware
- mClientPid = callingPid;
- disconnect();
mCameraService->releaseSound();
- LOG1("Client::~Client X (pid %d, this %p)", callingPid, this);
-}
-
-// ----------------------------------------------------------------------------
-
-status_t CameraService::Client::checkPid() const {
- int callingPid = getCallingPid();
- if (callingPid == mClientPid) return NO_ERROR;
-
- ALOGW("attempt to use a locked camera from a different process"
- " (old pid %d, new pid %d)", mClientPid, callingPid);
- return EBUSY;
-}
-status_t CameraService::Client::checkPidAndHardware() const {
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
- if (mHardware == 0) {
- ALOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid());
- return INVALID_OPERATION;
- }
- return NO_ERROR;
-}
-
-status_t CameraService::Client::lock() {
- int callingPid = getCallingPid();
- LOG1("lock (pid %d)", callingPid);
- Mutex::Autolock ilock(mICameraLock);
- Mutex::Autolock lock(mLock);
-
- // lock camera to this client if the the camera is unlocked
- if (mClientPid == 0) {
- mClientPid = callingPid;
- return NO_ERROR;
- }
-
- // returns NO_ERROR if the client already owns the camera, EBUSY otherwise
- return checkPid();
-}
-
-status_t CameraService::Client::unlock() {
- int callingPid = getCallingPid();
- LOG1("unlock (pid %d)", callingPid);
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
-
- // allow anyone to use camera (after they lock the camera)
- status_t result = checkPid();
- if (result == NO_ERROR) {
- if (mHardware->recordingEnabled()) {
- ALOGE("Not allowed to unlock camera during recording.");
- return INVALID_OPERATION;
- }
- mClientPid = 0;
- LOG1("clear mCameraClient (pid %d)", callingPid);
- // we need to remove the reference to ICameraClient so that when the app
- // goes away, the reference count goes to 0.
- mCameraClient.clear();
- }
- return result;
-}
-
-// connect a new client to the camera
-status_t CameraService::Client::connect(const sp<ICameraClient>& client) {
- int callingPid = getCallingPid();
- LOG1("connect E (pid %d)", callingPid);
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
-
- if (mClientPid != 0 && checkPid() != NO_ERROR) {
- ALOGW("Tried to connect to a locked camera (old pid %d, new pid %d)",
- mClientPid, callingPid);
- return EBUSY;
- }
-
- if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) {
- LOG1("Connect to the same client");
- return NO_ERROR;
- }
-
- mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
- mClientPid = callingPid;
- mCameraClient = client;
-
- LOG1("connect X (pid %d)", callingPid);
- return NO_ERROR;
-}
-
-static void disconnectWindow(const sp<ANativeWindow>& window) {
- if (window != 0) {
- status_t result = native_window_api_disconnect(window.get(),
- NATIVE_WINDOW_API_CAMERA);
- if (result != NO_ERROR) {
- ALOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result),
- result);
- }
- }
-}
-
-void CameraService::Client::disconnect() {
- int callingPid = getCallingPid();
- LOG1("disconnect E (pid %d)", callingPid);
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
-
- if (checkPid() != NO_ERROR) {
- ALOGW("different client - don't disconnect");
- return;
- }
-
- if (mClientPid <= 0) {
- LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
- return;
- }
-
- // Make sure disconnect() is done once and once only, whether it is called
- // from the user directly, or called by the destructor.
- if (mHardware == 0) return;
-
- LOG1("hardware teardown");
- // Before destroying mHardware, we must make sure it's in the
- // idle state.
- // Turn off all messages.
- disableMsgType(CAMERA_MSG_ALL_MSGS);
- mHardware->stopPreview();
- mHardware->cancelPicture();
- // Release the hardware resources.
- mHardware->release();
-
- // Release the held ANativeWindow resources.
- if (mPreviewWindow != 0) {
- disconnectWindow(mPreviewWindow);
- mPreviewWindow = 0;
- mHardware->setPreviewWindow(mPreviewWindow);
- }
- mHardware.clear();
-
- mCameraService->removeClient(mCameraClient);
- mCameraService->setCameraFree(mCameraId);
-
- LOG1("disconnect X (pid %d)", callingPid);
-}
-
-// ----------------------------------------------------------------------------
-
-status_t CameraService::Client::setPreviewWindow(const sp<IBinder>& binder,
- const sp<ANativeWindow>& window) {
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- // return if no change in surface.
- if (binder == mSurface) {
- return NO_ERROR;
- }
-
- if (window != 0) {
- result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
- if (result != NO_ERROR) {
- ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
- result);
- return result;
- }
- }
-
- // If preview has been already started, register preview buffers now.
- if (mHardware->previewEnabled()) {
- if (window != 0) {
- native_window_set_scaling_mode(window.get(),
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- native_window_set_buffers_transform(window.get(), mOrientation);
- result = mHardware->setPreviewWindow(window);
- }
- }
-
- if (result == NO_ERROR) {
- // Everything has succeeded. Disconnect the old window and remember the
- // new window.
- disconnectWindow(mPreviewWindow);
- mSurface = binder;
- mPreviewWindow = window;
- } else {
- // Something went wrong after we connected to the new window, so
- // disconnect here.
- disconnectWindow(window);
- }
-
- return result;
-}
-
-// set the Surface that the preview will use
-status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) {
- LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
-
- sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);
- sp<ANativeWindow> window(surface);
- return setPreviewWindow(binder, window);
-}
-
-// set the SurfaceTexture that the preview will use
-status_t CameraService::Client::setPreviewTexture(
- const sp<ISurfaceTexture>& surfaceTexture) {
- LOG1("setPreviewTexture(%p) (pid %d)", surfaceTexture.get(),
- getCallingPid());
-
- sp<IBinder> binder;
- sp<ANativeWindow> window;
- if (surfaceTexture != 0) {
- binder = surfaceTexture->asBinder();
- window = new SurfaceTextureClient(surfaceTexture);
- }
- return setPreviewWindow(binder, window);
-}
-
-// set the preview callback flag to affect how the received frames from
-// preview are handled.
-void CameraService::Client::setPreviewCallbackFlag(int callback_flag) {
- LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid());
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
- mPreviewCallbackFlag = callback_flag;
- if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
- enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- } else {
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-}
-
-// start preview mode
-status_t CameraService::Client::startPreview() {
- LOG1("startPreview (pid %d)", getCallingPid());
- return startCameraMode(CAMERA_PREVIEW_MODE);
-}
-
-// start recording mode
-status_t CameraService::Client::startRecording() {
- LOG1("startRecording (pid %d)", getCallingPid());
- return startCameraMode(CAMERA_RECORDING_MODE);
-}
-
-// start preview or recording
-status_t CameraService::Client::startCameraMode(camera_mode mode) {
- LOG1("startCameraMode(%d)", mode);
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- switch(mode) {
- case CAMERA_PREVIEW_MODE:
- if (mSurface == 0 && mPreviewWindow == 0) {
- LOG1("mSurface is not set yet.");
- // still able to start preview in this case.
- }
- return startPreviewMode();
- case CAMERA_RECORDING_MODE:
- if (mSurface == 0 && mPreviewWindow == 0) {
- ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
- return INVALID_OPERATION;
- }
- return startRecordingMode();
- default:
- return UNKNOWN_ERROR;
- }
-}
-
-status_t CameraService::Client::startPreviewMode() {
- LOG1("startPreviewMode");
- status_t result = NO_ERROR;
-
- // if preview has been enabled, nothing needs to be done
- if (mHardware->previewEnabled()) {
- return NO_ERROR;
- }
-
- if (mPreviewWindow != 0) {
- native_window_set_scaling_mode(mPreviewWindow.get(),
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- native_window_set_buffers_transform(mPreviewWindow.get(),
- mOrientation);
- }
- mHardware->setPreviewWindow(mPreviewWindow);
- result = mHardware->startPreview();
-
- return result;
-}
-
-status_t CameraService::Client::startRecordingMode() {
- LOG1("startRecordingMode");
- status_t result = NO_ERROR;
-
- // if recording has been enabled, nothing needs to be done
- if (mHardware->recordingEnabled()) {
- return NO_ERROR;
- }
-
- // if preview has not been started, start preview first
- if (!mHardware->previewEnabled()) {
- result = startPreviewMode();
- if (result != NO_ERROR) {
- return result;
- }
- }
-
- // start recording mode
- enableMsgType(CAMERA_MSG_VIDEO_FRAME);
- mCameraService->playSound(SOUND_RECORDING);
- result = mHardware->startRecording();
- if (result != NO_ERROR) {
- ALOGE("mHardware->startRecording() failed with status %d", result);
- }
- return result;
-}
-
-// stop preview mode
-void CameraService::Client::stopPreview() {
- LOG1("stopPreview (pid %d)", getCallingPid());
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
-
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- mHardware->stopPreview();
-
- mPreviewBuffer.clear();
-}
-
-// stop recording mode
-void CameraService::Client::stopRecording() {
- LOG1("stopRecording (pid %d)", getCallingPid());
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
-
- disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- mHardware->stopRecording();
- mCameraService->playSound(SOUND_RECORDING);
-
- mPreviewBuffer.clear();
-}
-
-// release a recording frame
-void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) {
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
- mHardware->releaseRecordingFrame(mem);
-}
-
-status_t CameraService::Client::storeMetaDataInBuffers(bool enabled)
-{
- LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false");
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) {
- return UNKNOWN_ERROR;
- }
- return mHardware->storeMetaDataInBuffers(enabled);
-}
-
-bool CameraService::Client::previewEnabled() {
- LOG1("previewEnabled (pid %d)", getCallingPid());
-
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return false;
- return mHardware->previewEnabled();
-}
-
-bool CameraService::Client::recordingEnabled() {
- LOG1("recordingEnabled (pid %d)", getCallingPid());
-
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return false;
- return mHardware->recordingEnabled();
-}
-
-status_t CameraService::Client::autoFocus() {
- LOG1("autoFocus (pid %d)", getCallingPid());
-
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- return mHardware->autoFocus();
-}
-
-status_t CameraService::Client::cancelAutoFocus() {
- LOG1("cancelAutoFocus (pid %d)", getCallingPid());
-
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- return mHardware->cancelAutoFocus();
-}
-
-// take a picture - image is returned in callback
-status_t CameraService::Client::takePicture(int msgType) {
- LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);
-
- Mutex::Autolock iLock(mICameraLock);
- int picMsgType = 0;
- { // scope for lock
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
- (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
- ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
- " cannot be both enabled");
- return BAD_VALUE;
- }
-
- // We only accept picture related message types
- // and ignore other types of messages for takePicture().
- picMsgType = msgType
- & (CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_RAW_IMAGE_NOTIFY |
- CAMERA_MSG_COMPRESSED_IMAGE);
-
- }
- enableMsgType(picMsgType);
-
- return mHardware->takePicture();
-}
-
-// set preview/capture parameters - key/value pairs
-status_t CameraService::Client::setParameters(const String8& params) {
- LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string());
-
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- CameraParameters p(params);
- return mHardware->setParameters(p);
-}
-
-// get preview/capture parameters - key/value pairs
-String8 CameraService::Client::getParameters() const {
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return String8();
-
- String8 params(mHardware->getParameters().flatten());
- LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string());
- return params;
-}
-
-// enable shutter sound
-status_t CameraService::Client::enableShutterSound(bool enable) {
- LOG1("enableShutterSound (pid %d)", getCallingPid());
-
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if (enable) {
- mPlayShutterSound = true;
- return OK;
- }
-
- // Disabling shutter sound may not be allowed. In that case only
- // allow the mediaserver process to disable the sound.
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.camera.sound.forced", value, "0");
- if (strcmp(value, "0") != 0) {
- // Disabling shutter sound is not allowed. Deny if the current
- // process is not mediaserver.
- if (getCallingPid() != getpid()) {
- ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid());
- return PERMISSION_DENIED;
- }
- }
-
- mPlayShutterSound = false;
- return OK;
-}
-
-status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
- LOG1("sendCommand (pid %d)", getCallingPid());
- int orientation;
- Mutex::Autolock iLock(mICameraLock);
- Mutex::Autolock lock(mLock);
- status_t result = checkPidAndHardware();
- if (result != NO_ERROR) return result;
-
- if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
- // Mirror the preview if the camera is front-facing.
- orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT);
- if (orientation == -1) return BAD_VALUE;
-
- if (mOrientation != orientation) {
- mOrientation = orientation;
- if (mPreviewWindow != 0) {
- native_window_set_buffers_transform(mPreviewWindow.get(),
- mOrientation);
- }
- }
- return OK;
- } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) {
- switch (arg1) {
- case 0:
- enableShutterSound(false);
- break;
- case 1:
- enableShutterSound(true);
- break;
- default:
- return BAD_VALUE;
- }
- return OK;
- } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
- mCameraService->playSound(SOUND_RECORDING);
- } else if (cmd == CAMERA_CMD_PING) {
- // If mHardware is 0, checkPidAndHardware will return error.
- return OK;
- }
-
- return mHardware->sendCommand(cmd, arg1, arg2);
-}
-
-// ----------------------------------------------------------------------------
-
-void CameraService::Client::enableMsgType(int32_t msgType) {
- android_atomic_or(msgType, &mMsgEnabled);
- mHardware->enableMsgType(msgType);
-}
-
-void CameraService::Client::disableMsgType(int32_t msgType) {
- android_atomic_and(~msgType, &mMsgEnabled);
- mHardware->disableMsgType(msgType);
-}
-
-#define CHECK_MESSAGE_INTERVAL 10 // 10ms
-bool CameraService::Client::lockIfMessageWanted(int32_t msgType) {
- int sleepCount = 0;
- while (mMsgEnabled & msgType) {
- if (mLock.tryLock() == NO_ERROR) {
- if (sleepCount > 0) {
- LOG1("lockIfMessageWanted(%d): waited for %d ms",
- msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
- }
- return true;
- }
- if (sleepCount++ == 0) {
- LOG1("lockIfMessageWanted(%d): enter sleep", msgType);
- }
- usleep(CHECK_MESSAGE_INTERVAL * 1000);
- }
- ALOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType);
- return false;
+ // unconditionally disconnect. function is idempotent
+ Client::disconnect();
}
// ----------------------------------------------------------------------------
@@ -967,311 +433,13 @@ CameraService::Client* CameraService::Client::getClientFromCookie(void* user) {
// destruction already started, so should not be accessed
if (client->mDestructionStarted) return NULL;
- // The checks below are not necessary and are for debugging only.
- if (client->mCameraService.get() != gCameraService) {
- ALOGE("mismatch service!");
- return NULL;
- }
-
- if (client->mHardware == 0) {
- ALOGE("mHardware == 0: callback after disconnect()?");
- return NULL;
- }
-
return client;
}
-// Callback messages can be dispatched to internal handlers or pass to our
-// client's callback functions, depending on the message type.
-//
-// notifyCallback:
-// CAMERA_MSG_SHUTTER handleShutter
-// (others) c->notifyCallback
-// dataCallback:
-// CAMERA_MSG_PREVIEW_FRAME handlePreviewData
-// CAMERA_MSG_POSTVIEW_FRAME handlePostview
-// CAMERA_MSG_RAW_IMAGE handleRawPicture
-// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture
-// (others) c->dataCallback
-// dataCallbackTimestamp
-// (others) c->dataCallbackTimestamp
-//
-// NOTE: the *Callback functions grab mLock of the client before passing
-// control to handle* functions. So the handle* functions must release the
-// lock before calling the ICameraClient's callbacks, so those callbacks can
-// invoke methods in the Client class again (For example, the preview frame
-// callback may want to releaseRecordingFrame). The handle* functions must
-// release the lock after all accesses to member variables, so it must be
-// handled very carefully.
-
-void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1,
- int32_t ext2, void* user) {
- LOG2("notifyCallback(%d)", msgType);
-
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- Client* client = getClientFromCookie(user);
- if (client == NULL) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
-
- switch (msgType) {
- case CAMERA_MSG_SHUTTER:
- // ext1 is the dimension of the yuv picture.
- client->handleShutter();
- break;
- default:
- client->handleGenericNotify(msgType, ext1, ext2);
- break;
- }
-}
-
-void CameraService::Client::dataCallback(int32_t msgType,
- const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
- LOG2("dataCallback(%d)", msgType);
-
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- Client* client = getClientFromCookie(user);
- if (client == NULL) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
- if (dataPtr == 0 && metadata == NULL) {
- ALOGE("Null data returned in data callback");
- client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- return;
- }
-
- switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
- case CAMERA_MSG_PREVIEW_FRAME:
- client->handlePreviewData(msgType, dataPtr, metadata);
- break;
- case CAMERA_MSG_POSTVIEW_FRAME:
- client->handlePostview(dataPtr);
- break;
- case CAMERA_MSG_RAW_IMAGE:
- client->handleRawPicture(dataPtr);
- break;
- case CAMERA_MSG_COMPRESSED_IMAGE:
- client->handleCompressedPicture(dataPtr);
- break;
- default:
- client->handleGenericData(msgType, dataPtr, metadata);
- break;
- }
-}
-
-void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
- LOG2("dataCallbackTimestamp(%d)", msgType);
-
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- Client* client = getClientFromCookie(user);
- if (client == NULL) return;
-
- if (!client->lockIfMessageWanted(msgType)) return;
-
- if (dataPtr == 0) {
- ALOGE("Null data returned in data with timestamp callback");
- client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- return;
- }
-
- client->handleGenericDataTimestamp(timestamp, msgType, dataPtr);
-}
-
-// snapshot taken callback
-void CameraService::Client::handleShutter(void) {
- if (mPlayShutterSound) {
- mCameraService->playSound(SOUND_SHUTTER);
- }
-
- sp<ICameraClient> c = mCameraClient;
- if (c != 0) {
- mLock.unlock();
- c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
- if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return;
- }
- disableMsgType(CAMERA_MSG_SHUTTER);
-
- mLock.unlock();
-}
-
-// preview callback - frame buffer update
-void CameraService::Client::handlePreviewData(int32_t msgType,
- const sp<IMemory>& mem,
- camera_frame_metadata_t *metadata) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
- // local copy of the callback flags
- int flags = mPreviewCallbackFlag;
-
- // is callback enabled?
- if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
- // If the enable bit is off, the copy-out and one-shot bits are ignored
- LOG2("frame callback is disabled");
- mLock.unlock();
- return;
- }
-
- // hold a strong pointer to the client
- sp<ICameraClient> c = mCameraClient;
-
- // clear callback flags if no client or one-shot mode
- if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
- LOG2("Disable preview callback");
- mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK);
- disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-
- if (c != 0) {
- // Is the received frame copied out or not?
- if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
- LOG2("frame is copied");
- copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
- } else {
- LOG2("frame is forwarded");
- mLock.unlock();
- c->dataCallback(msgType, mem, metadata);
- }
- } else {
- mLock.unlock();
- }
-}
-
-// picture callback - postview image ready
-void CameraService::Client::handlePostview(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
-
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL);
- }
-}
-
-// picture callback - raw image ready
-void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_RAW_IMAGE);
-
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);
- }
-}
-
-// picture callback - compressed picture ready
-void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) {
- disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
-
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL);
- }
-}
-
-
-void CameraService::Client::handleGenericNotify(int32_t msgType,
- int32_t ext1, int32_t ext2) {
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->notifyCallback(msgType, ext1, ext2);
- }
-}
-
-void CameraService::Client::handleGenericData(int32_t msgType,
- const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) {
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->dataCallback(msgType, dataPtr, metadata);
- }
-}
-
-void CameraService::Client::handleGenericDataTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& dataPtr) {
- sp<ICameraClient> c = mCameraClient;
- mLock.unlock();
- if (c != 0) {
- c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
- }
-}
-
-void CameraService::Client::copyFrameAndPostCopiedFrame(
- int32_t msgType, const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size,
- camera_frame_metadata_t *metadata) {
- LOG2("copyFrameAndPostCopiedFrame");
- // It is necessary to copy out of pmem before sending this to
- // the callback. For efficiency, reuse the same MemoryHeapBase
- // provided it's big enough. Don't allocate the memory or
- // perform the copy if there's no callback.
- // hold the preview lock while we grab a reference to the preview buffer
- sp<MemoryHeapBase> previewBuffer;
-
- if (mPreviewBuffer == 0) {
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- } else if (size > mPreviewBuffer->virtualSize()) {
- mPreviewBuffer.clear();
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- }
- if (mPreviewBuffer == 0) {
- ALOGE("failed to allocate space for preview buffer");
- mLock.unlock();
- return;
- }
- previewBuffer = mPreviewBuffer;
-
- memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size);
-
- sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
- if (frame == 0) {
- ALOGE("failed to allocate space for frame callback");
- mLock.unlock();
- return;
- }
-
- mLock.unlock();
- client->dataCallback(msgType, frame, metadata);
-}
-
-int CameraService::Client::getOrientation(int degrees, bool mirror) {
- if (!mirror) {
- if (degrees == 0) return 0;
- else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
- else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
- else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
- } else { // Do mirror (horizontal flip)
- if (degrees == 0) { // FLIP_H and ROT_0
- return HAL_TRANSFORM_FLIP_H;
- } else if (degrees == 90) { // FLIP_H and ROT_90
- return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
- } else if (degrees == 180) { // FLIP_H and ROT_180
- return HAL_TRANSFORM_FLIP_V;
- } else if (degrees == 270) { // FLIP_H and ROT_270
- return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
- }
- }
- ALOGE("Invalid setDisplayOrientation degrees=%d", degrees);
- return -1;
+// NOTE: function is idempotent
+void CameraService::Client::disconnect() {
+ mCameraService->removeClient(mCameraClient);
+ mCameraService->setCameraFree(mCameraId);
}
// ----------------------------------------------------------------------------
@@ -1293,41 +461,81 @@ static bool tryLock(Mutex& mutex)
}
status_t CameraService::dump(int fd, const Vector<String16>& args) {
- static const char* kDeadlockedString = "CameraService may be deadlocked\n";
-
- const size_t SIZE = 256;
- char buffer[SIZE];
String8 result;
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
+ result.appendFormat("Permission Denial: "
"can't dump CameraService from pid=%d, uid=%d\n",
getCallingPid(),
getCallingUid());
- result.append(buffer);
write(fd, result.string(), result.size());
} else {
bool locked = tryLock(mServiceLock);
// failed to lock - CameraService is probably deadlocked
if (!locked) {
- String8 result(kDeadlockedString);
+ result.append("CameraService may be deadlocked\n");
write(fd, result.string(), result.size());
}
bool hasClient = false;
+ if (!mModule) {
+ result = String8::format("No camera module available!\n");
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+ }
+
+ result = String8::format("Camera module HAL API version: 0x%x\n",
+ mModule->common.hal_api_version);
+ result.appendFormat("Camera module API version: 0x%x\n",
+ mModule->common.module_api_version);
+ result.appendFormat("Camera module name: %s\n",
+ mModule->common.name);
+ result.appendFormat("Camera module author: %s\n",
+ mModule->common.author);
+ result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras);
+ write(fd, result.string(), result.size());
for (int i = 0; i < mNumberOfCameras; i++) {
+ result = String8::format("Camera %d static information:\n", i);
+ camera_info info;
+
+ status_t rc = mModule->get_camera_info(i, &info);
+ if (rc != OK) {
+ result.appendFormat(" Error reading static information!\n");
+ write(fd, result.string(), result.size());
+ } else {
+ result.appendFormat(" Facing: %s\n",
+ info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT");
+ result.appendFormat(" Orientation: %d\n", info.orientation);
+ int deviceVersion;
+ if (mModule->common.module_api_version <
+ CAMERA_MODULE_API_VERSION_2_0) {
+ deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
+ } else {
+ deviceVersion = info.device_version;
+ }
+ result.appendFormat(" Device version: 0x%x\n", deviceVersion);
+ if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) {
+ result.appendFormat(" Device static metadata:\n");
+ write(fd, result.string(), result.size());
+ dump_indented_camera_metadata(info.static_camera_characteristics,
+ fd, 2, 4);
+ } else {
+ write(fd, result.string(), result.size());
+ }
+ }
+
sp<Client> client = mClient[i].promote();
- if (client == 0) continue;
+ if (client == 0) {
+ result = String8::format(" Device is closed, no client instance\n");
+ write(fd, result.string(), result.size());
+ continue;
+ }
hasClient = true;
- sprintf(buffer, "Client[%d] (%p) PID: %d\n",
- i,
- client->getCameraClient()->asBinder().get(),
- client->mClientPid);
- result.append(buffer);
+ result = String8::format(" Device is open. Client instance dump:\n");
write(fd, result.string(), result.size());
- client->mHardware->dump(fd, args);
+ client->dump(fd, args);
}
if (!hasClient) {
- result.append("No camera client yet.\n");
+ result = String8::format("\nNo active camera clients yet.\n");
write(fd, result.string(), result.size());
}
@@ -1336,16 +544,42 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
// change logging level
int n = args.size();
for (int i = 0; i + 1 < n; i++) {
- if (args[i] == String16("-v")) {
+ String16 verboseOption("-v");
+ if (args[i] == verboseOption) {
String8 levelStr(args[i+1]);
int level = atoi(levelStr.string());
- sprintf(buffer, "Set Log Level to %d", level);
- result.append(buffer);
+ result = String8::format("\nSetting log level to %d.\n", level);
setLogLevel(level);
+ write(fd, result.string(), result.size());
}
}
+
}
return NO_ERROR;
}
+/*virtual*/void CameraService::binderDied(
+ const wp<IBinder> &who) {
+
+ /**
+ * While tempting to promote the wp<IBinder> into a sp,
+ * it's actually not supported by the binder driver
+ */
+
+ ALOGV("java clients' binder died");
+
+ sp<Client> cameraClient = getClientByRemote(who);
+
+ if (cameraClient == 0) {
+ ALOGV("java clients' binder death already cleaned up (normal case)");
+ return;
+ }
+
+ ALOGW("Disconnecting camera client %p since the binder for it "
+ "died (this pid %d)", cameraClient.get(), getCallingPid());
+
+ cameraClient->disconnect();
+
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 95ac1970..4dab340d 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -27,17 +27,19 @@
namespace android {
+extern volatile int32_t gLogLevel;
+
class MemoryHeapBase;
class MediaPlayer;
-class CameraHardwareInterface;
class CameraService :
public BinderService<CameraService>,
- public BnCameraService
+ public BnCameraService,
+ public IBinder::DeathRecipient
{
- class Client;
friend class BinderService<CameraService>;
public:
+ class Client;
static char const* getServiceName() { return "media.camera"; }
CameraService();
@@ -53,6 +55,8 @@ public:
virtual Client* getClientByIdUnsafe(int cameraId);
virtual Mutex* getClientLockById(int cameraId);
+ virtual sp<Client> getClientByRemote(const wp<IBinder>& cameraClient);
+
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -68,114 +72,59 @@ public:
void playSound(sound_kind kind);
void releaseSound();
-private:
- Mutex mServiceLock;
- wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
- Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
- int mNumberOfCameras;
-
- // atomics to record whether the hardware is allocated to some client.
- volatile int32_t mBusy[MAX_CAMERAS];
- void setCameraBusy(int cameraId);
- void setCameraFree(int cameraId);
-
- // sounds
- MediaPlayer* newMediaPlayer(const char *file);
-
- Mutex mSoundLock;
- sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
- int mSoundRef; // reference count (release all MediaPlayer when 0)
-
class Client : public BnCamera
{
public:
// ICamera interface (see ICamera for details)
- virtual void disconnect();
- virtual status_t connect(const sp<ICameraClient>& client);
- virtual status_t lock();
- virtual status_t unlock();
- virtual status_t setPreviewDisplay(const sp<Surface>& surface);
- virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture);
- virtual void setPreviewCallbackFlag(int flag);
- virtual status_t startPreview();
- virtual void stopPreview();
- virtual bool previewEnabled();
- virtual status_t storeMetaDataInBuffers(bool enabled);
- virtual status_t startRecording();
- virtual void stopRecording();
- virtual bool recordingEnabled();
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
- virtual status_t autoFocus();
- virtual status_t cancelAutoFocus();
- virtual status_t takePicture(int msgType);
- virtual status_t setParameters(const String8& params);
- virtual String8 getParameters() const;
- virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
- private:
- friend class CameraService;
- Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient,
- const sp<CameraHardwareInterface>& hardware,
- int cameraId,
- int cameraFacing,
- int clientPid);
- ~Client();
+ virtual void disconnect();
+ virtual status_t connect(const sp<ICameraClient>& client) = 0;
+ virtual status_t lock() = 0;
+ virtual status_t unlock() = 0;
+ virtual status_t setPreviewDisplay(const sp<Surface>& surface) = 0;
+ virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) = 0;
+ virtual void setPreviewCallbackFlag(int flag) = 0;
+ virtual status_t startPreview() = 0;
+ virtual void stopPreview() = 0;
+ virtual bool previewEnabled() = 0;
+ virtual status_t storeMetaDataInBuffers(bool enabled) = 0;
+ virtual status_t startRecording() = 0;
+ virtual void stopRecording() = 0;
+ virtual bool recordingEnabled() = 0;
+ virtual void releaseRecordingFrame(const sp<IMemory>& mem) = 0;
+ virtual status_t autoFocus() = 0;
+ virtual status_t cancelAutoFocus() = 0;
+ virtual status_t takePicture(int msgType) = 0;
+ virtual status_t setParameters(const String8& params) = 0;
+ virtual String8 getParameters() const = 0;
+ virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;
+
+ // Interface used by CameraService
+ Client(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ int servicePid);
+ ~Client();
// return our camera client
- const sp<ICameraClient>& getCameraClient() { return mCameraClient; }
-
- // check whether the calling process matches mClientPid.
- status_t checkPid() const;
- status_t checkPidAndHardware() const; // also check mHardware != 0
-
- // these are internal functions used to set up preview buffers
- status_t registerPreviewBuffers();
-
- // camera operation mode
- enum camera_mode {
- CAMERA_PREVIEW_MODE = 0, // frame automatically released
- CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
- };
- // these are internal functions used for preview/recording
- status_t startCameraMode(camera_mode mode);
- status_t startPreviewMode();
- status_t startRecordingMode();
-
- // internal function used by sendCommand to enable/disable shutter sound.
- status_t enableShutterSound(bool enable);
-
- // these are static callback functions
- static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
- static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
- camera_frame_metadata_t *metadata, void* user);
- static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ const sp<ICameraClient>& getCameraClient() {
+ return mCameraClient;
+ }
+
+ virtual status_t initialize(camera_module_t *module) = 0;
+
+ virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+
+ protected:
static Mutex* getClientLockFromCookie(void* user);
// convert client from cookie. Client lock should be acquired before getting Client.
static Client* getClientFromCookie(void* user);
- // handlers for messages
- void handleShutter(void);
- void handlePreviewData(int32_t msgType, const sp<IMemory>& mem,
- camera_frame_metadata_t *metadata);
- void handlePostview(const sp<IMemory>& mem);
- void handleRawPicture(const sp<IMemory>& mem);
- void handleCompressedPicture(const sp<IMemory>& mem);
- void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
- void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr,
- camera_frame_metadata_t *metadata);
- void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
-
- void copyFrameAndPostCopiedFrame(
- int32_t msgType,
- const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap,
- size_t offset, size_t size,
- camera_frame_metadata_t *metadata);
-
- int getOrientation(int orientation, bool mirror);
-
- status_t setPreviewWindow(
- const sp<IBinder>& binder,
- const sp<ANativeWindow>& window);
+
+ // the instance is in the middle of destruction. When this is set,
+ // the instance should not be accessed from callback.
+ // CameraService's mClientLock should be acquired to access this.
+ bool mDestructionStarted;
// these are initialized in the constructor.
sp<CameraService> mCameraService; // immutable after constructor
@@ -183,51 +132,35 @@ private:
int mCameraId; // immutable after constructor
int mCameraFacing; // immutable after constructor
pid_t mClientPid;
- sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
- int mPreviewCallbackFlag;
- int mOrientation; // Current display orientation
- bool mPlayShutterSound;
-
- // Ensures atomicity among the public methods
- mutable Mutex mLock;
- // A lock to synchronize access through the ICamera binder
- // interface. The entire binder call should be done with mICameraLock
- // locked, to serialize ICamera access, even when mLock is disabled for
- // calls to the HAL.
- mutable Mutex mICameraLock;
- // This is a binder of Surface or SurfaceTexture.
- sp<IBinder> mSurface;
- sp<ANativeWindow> mPreviewWindow;
-
- // If the user want us to return a copy of the preview frame (instead
- // of the original one), we allocate mPreviewBuffer and reuse it if possible.
- sp<MemoryHeapBase> mPreviewBuffer;
-
- // the instance is in the middle of destruction. When this is set,
- // the instance should not be accessed from callback.
- // CameraService's mClientLock should be acquired to access this.
- bool mDestructionStarted;
+ pid_t mServicePid; // immutable after constructor
- // We need to avoid the deadlock when the incoming command thread and
- // the CameraHardwareInterface callback thread both want to grab mLock.
- // An extra flag is used to tell the callback thread that it should stop
- // trying to deliver the callback messages if the client is not
- // interested in it anymore. For example, if the client is calling
- // stopPreview(), the preview frame messages do not need to be delivered
- // anymore.
-
- // This function takes the same parameter as the enableMsgType() and
- // disableMsgType() functions in CameraHardwareInterface.
- void enableMsgType(int32_t msgType);
- void disableMsgType(int32_t msgType);
- volatile int32_t mMsgEnabled;
-
- // This function keeps trying to grab mLock, or give up if the message
- // is found to be disabled. It returns true if mLock is grabbed.
- bool lockIfMessageWanted(int32_t msgType);
};
+private:
+ Mutex mServiceLock;
+ wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
+ Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
+ int mNumberOfCameras;
+
+ // needs to be called with mServiceLock held
+ sp<Client> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
+
+ // atomics to record whether the hardware is allocated to some client.
+ volatile int32_t mBusy[MAX_CAMERAS];
+ void setCameraBusy(int cameraId);
+ void setCameraFree(int cameraId);
+
+ // sounds
+ MediaPlayer* newMediaPlayer(const char *file);
+
+ Mutex mSoundLock;
+ sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS];
+ int mSoundRef; // reference count (release all MediaPlayer when 0)
+
camera_module_t *mModule;
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder> &who);
};
} // namespace android
diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h
deleted file mode 100644
index 6dd99c1e..00000000
--- a/services/camera/libcameraservice/CannedJpeg.h
+++ /dev/null
@@ -1,750 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-const int kCannedJpegWidth = 320;
-const int kCannedJpegHeight = 240;
-const int kCannedJpegSize = 8733;
-
-const char kCannedJpeg[] = {
- 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
- 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66,
- 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0x1a, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x3e, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x46, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x31, 0x01, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33,
- 0x2e, 0x33, 0x36, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02,
- 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05,
- 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, 0x08, 0x0c,
- 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d,
- 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15,
- 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15,
- 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05,
- 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0,
- 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02,
- 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01,
- 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
- 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
- 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
- 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
- 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
- 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
- 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
- 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
- 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
- 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
- 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
- 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
- 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
- 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00,
- 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
- 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1,
- 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
- 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
- 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
- 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
- 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
- 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
- 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
- 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
- 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
- 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00,
- 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xd2, 0xa3, 0x95, 0xbb,
- 0x54, 0x84, 0xe0, 0x66, 0xa0, 0x27, 0x27, 0x35, 0xed, 0x9e, 0x50, 0x95,
- 0x2c, 0x4b, 0xc6, 0x6a, 0x35, 0x1b, 0x8e, 0x2a, 0x70, 0x30, 0x28, 0x00,
- 0xa8, 0xe5, 0x6e, 0x71, 0x52, 0x31, 0xda, 0x33, 0x50, 0x13, 0x93, 0x40,
- 0x09, 0x52, 0xc6, 0xb8, 0x19, 0xf5, 0xa6, 0x2a, 0xee, 0x6c, 0x54, 0xd4,
- 0x00, 0x54, 0x52, 0x36, 0x5b, 0x1e, 0x95, 0x23, 0xb6, 0xd5, 0xcd, 0x41,
- 0x40, 0x05, 0x4c, 0x8b, 0xb5, 0x7d, 0xea, 0x34, 0x5d, 0xcd, 0xed, 0x53,
- 0x50, 0x01, 0x50, 0xbb, 0x6e, 0x6f, 0x6a, 0x91, 0xdb, 0x6a, 0xfb, 0xd4,
- 0x34, 0x00, 0x54, 0xe8, 0xbb, 0x57, 0x15, 0x1c, 0x6b, 0x96, 0xcf, 0xa5,
- 0x4b, 0x40, 0x05, 0x42, 0xcd, 0xb9, 0xb3, 0x4f, 0x91, 0xb0, 0x31, 0xeb,
- 0x51, 0x50, 0x02, 0x81, 0x93, 0x53, 0xa8, 0xda, 0x31, 0x51, 0xc4, 0xbc,
- 0xe6, 0xa4, 0xa0, 0x00, 0x9c, 0x0a, 0x81, 0x8e, 0xe3, 0x9a, 0x92, 0x56,
- 0xe3, 0x15, 0x15, 0x00, 0x28, 0x19, 0x38, 0xa9, 0xc0, 0xc0, 0xc5, 0x47,
- 0x12, 0xf7, 0xa9, 0x28, 0x00, 0x27, 0x00, 0x9a, 0x80, 0x9c, 0x9c, 0xd3,
- 0xe5, 0x6e, 0xd5, 0x1d, 0x00, 0x2a, 0x8d, 0xc7, 0x15, 0x3d, 0x32, 0x35,
- 0xc0, 0xcf, 0xad, 0x3e, 0x80, 0x11, 0x8e, 0xd1, 0x9a, 0x82, 0x9f, 0x23,
- 0x64, 0xe3, 0xd2, 0x99, 0x40, 0x0e, 0x45, 0xdc, 0xde, 0xd5, 0x35, 0x36,
- 0x35, 0xc2, 0xfb, 0x9a, 0x75, 0x00, 0x35, 0xdb, 0x6a, 0xfb, 0xd4, 0x34,
- 0xe9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, 0x00, 0x3e, 0x35, 0xcb, 0x7b, 0x0a,
- 0x96, 0x91, 0x17, 0x6a, 0xd2, 0xd0, 0x03, 0x64, 0x6c, 0x2f, 0xb9, 0xa8,
- 0x69, 0xce, 0xdb, 0x9a, 0x9b, 0xd6, 0x80, 0x1f, 0x12, 0xe4, 0xe7, 0xd2,
- 0xa5, 0xa4, 0x51, 0xb4, 0x62, 0x97, 0xa5, 0x00, 0x67, 0xc9, 0xad, 0xd8,
- 0x91, 0x81, 0x72, 0x9f, 0x9d, 0x47, 0xfd, 0xb3, 0x65, 0xff, 0x00, 0x3f,
- 0x29, 0x5f, 0xa0, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad,
- 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43,
- 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd7, 0x3f, 0xb7, 0x87, 0x73, 0x6f,
- 0x63, 0x33, 0xe0, 0x28, 0xf5, 0x9b, 0x11, 0xc9, 0xb9, 0x4c, 0xfd, 0x69,
- 0xff, 0x00, 0xdb, 0x96, 0x1f, 0xf3, 0xf5, 0x1f, 0xe7, 0x5f, 0x7d, 0x7f,
- 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80,
- 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7,
- 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, 0x02, 0x93,
- 0x5b, 0xb1, 0x3c, 0x0b, 0x94, 0xc7, 0xd6, 0x99, 0xfd, 0xb3, 0x65, 0xff,
- 0x00, 0x3f, 0x29, 0xf9, 0xd7, 0xe8, 0x07, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f,
- 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09,
- 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0xbd, 0xbc, 0x03, 0xd8,
- 0xcc, 0xf8, 0x0e, 0x3d, 0x6a, 0xc1, 0x47, 0x37, 0x29, 0x9f, 0xad, 0x3b,
- 0xfb, 0x72, 0xc3, 0xfe, 0x7e, 0xa3, 0xfc, 0xeb, 0xef, 0xaf, 0xf8, 0x74,
- 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1,
- 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48,
- 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x19, 0x35, 0xbb, 0x26,
- 0x3c, 0x5c, 0xa6, 0x3e, 0xb4, 0xdf, 0xed, 0x9b, 0x2f, 0xf9, 0xf9, 0x4a,
- 0xfd, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
- 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x97, 0xb7, 0x80, 0x7b, 0x19, 0x9f, 0x01,
- 0xa6, 0xb5, 0x60, 0xab, 0xff, 0x00, 0x1f, 0x51, 0xe7, 0xeb, 0x4e, 0xfe,
- 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0xfb, 0xeb, 0xfe,
- 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f,
- 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a,
- 0x3d, 0xbc, 0x03, 0xd8, 0xcc, 0xf8, 0x05, 0xf5, 0xab, 0x26, 0x6f, 0xf8,
- 0xf9, 0x4c, 0x7d, 0x69, 0xbf, 0xdb, 0x36, 0x5f, 0xf3, 0xf2, 0x9f, 0x9d,
- 0x7e, 0x80, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5,
- 0xff, 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd,
- 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x80, 0x7b, 0x19, 0x9f,
- 0x02, 0x26, 0xb5, 0x60, 0xab, 0x8f, 0xb5, 0x47, 0xf9, 0xd2, 0xff, 0x00,
- 0x6e, 0x58, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x7d, 0xf5, 0xff, 0x00, 0x0e,
- 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc,
- 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e,
- 0xde, 0x01, 0xec, 0x66, 0x7c, 0x00, 0xda, 0xd5, 0x93, 0x1c, 0xfd, 0xa5,
- 0x3f, 0x3a, 0x4f, 0xed, 0x8b, 0x2f, 0xf9, 0xf9, 0x4f, 0xce, 0xbf, 0x40,
- 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00,
- 0xc0, 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd,
- 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa7, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
- 0x81, 0x57, 0x5a, 0xb0, 0x51, 0x8f, 0xb5, 0x47, 0xf9, 0xd1, 0xfd, 0xb9,
- 0x61, 0xff, 0x00, 0x3f, 0x49, 0xf9, 0xd7, 0xdf, 0x5f, 0xf0, 0xe9, 0x6f,
- 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5,
- 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd2,
- 0xf6, 0xf0, 0x0f, 0x63, 0x33, 0xe0, 0x06, 0xd6, 0xac, 0x98, 0xe7, 0xed,
- 0x29, 0xf9, 0xd2, 0x0d, 0x62, 0xcb, 0xfe, 0x7e, 0x53, 0xf3, 0xaf, 0xd0,
- 0x0f, 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12,
- 0x1a, 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff,
- 0x00, 0xc0, 0x48, 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x51,
- 0xad, 0xd8, 0x01, 0x8f, 0xb5, 0x47, 0xf9, 0xd0, 0x75, 0xcb, 0x0c, 0x7f,
- 0xc7, 0xca, 0x7e, 0x75, 0xf7, 0xd7, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, 0xf4,
- 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f,
- 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7b, 0x78, 0x77, 0x0f, 0x63,
- 0x33, 0xf3, 0xfc, 0xeb, 0x36, 0x44, 0xff, 0x00, 0xc7, 0xca, 0x7e, 0x74,
- 0xa3, 0x58, 0xb1, 0x24, 0x66, 0xe5, 0x31, 0xf5, 0xaf, 0xbf, 0xff, 0x00,
- 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0,
- 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xdb, 0xc3, 0xb8, 0x7b, 0x19, 0x9f, 0x02,
- 0xff, 0x00, 0x6d, 0xd8, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x07, 0x5c, 0xb1,
- 0x03, 0x8b, 0x94, 0xcf, 0xd6, 0xbe, 0xfa, 0xff, 0x00, 0x87, 0x4b, 0x78,
- 0x4b, 0xfe, 0x87, 0xdd, 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d,
- 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f,
- 0x0e, 0xe1, 0xec, 0x66, 0x7e, 0x7f, 0xff, 0x00, 0x6c, 0xd9, 0x7f, 0xcf,
- 0xca, 0x7e, 0x74, 0xab, 0xac, 0x58, 0xe7, 0x9b, 0x94, 0xc7, 0xd6, 0xbe,
- 0xff, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
- 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, 0x0e, 0xe1, 0xec, 0x66, 0x7c,
- 0x0b, 0xfd, 0xb9, 0x61, 0xff, 0x00, 0x3f, 0x51, 0xfe, 0x74, 0x8d, 0xae,
- 0x58, 0xed, 0x38, 0xb9, 0x4c, 0xfd, 0x6b, 0xef, 0xbf, 0xf8, 0x74, 0xb7,
- 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, 0xd2,
- 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, 0x68,
- 0xf6, 0xf0, 0xee, 0x1e, 0xc6, 0x67, 0xe7, 0xff, 0x00, 0xf6, 0xc5, 0x97,
- 0xfc, 0xfc, 0xa7, 0xe7, 0x4e, 0x4d, 0x62, 0xc7, 0x77, 0x37, 0x29, 0xf9,
- 0xd7, 0xdf, 0xdf, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f,
- 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee,
- 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
- 0x81, 0x7f, 0xb7, 0x2c, 0x3f, 0xe7, 0xea, 0x3f, 0xce, 0x91, 0xf5, 0xcb,
- 0x1c, 0x71, 0x72, 0x9f, 0x9d, 0x7d, 0xf7, 0xff, 0x00, 0x0e, 0x96, 0xf0,
- 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, 0x3a, 0x5b,
- 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, 0xde, 0x1d,
- 0xc3, 0xd8, 0xcc, 0xfc, 0xff, 0x00, 0xfe, 0xd9, 0xb2, 0xff, 0x00, 0x9f,
- 0x94, 0xfc, 0xe9, 0xd1, 0xeb, 0x36, 0x20, 0xe4, 0xdc, 0xa7, 0xe7, 0x5f,
- 0x7f, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff,
- 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f,
- 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e,
- 0x05, 0xfe, 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0x6c,
- 0x9a, 0xdd, 0x89, 0x18, 0x17, 0x29, 0xf9, 0xd7, 0xdf, 0x9f, 0xf0, 0xe9,
- 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3,
- 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90,
- 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, 0xbc, 0xa8, 0xa2, 0x8a, 0xf3,
- 0x0e, 0xf0, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0xa0, 0xbb, 0xbd, 0xb7, 0xb0,
- 0x88, 0x49, 0x73, 0x3c, 0x56, 0xf1, 0x96, 0x0a, 0x1e, 0x57, 0x0a, 0x09,
- 0x3d, 0x06, 0x4f, 0x7a, 0x9e, 0x95, 0xd3, 0x76, 0xea, 0x01, 0x45, 0x14,
- 0x53, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x82, 0xda, 0xf6, 0xde,
- 0xf0, 0xca, 0x2d, 0xe7, 0x8a, 0x73, 0x13, 0x98, 0xe4, 0xf2, 0xdc, 0x36,
- 0xc6, 0x1d, 0x54, 0xe3, 0xa1, 0xf6, 0xa4, 0xda, 0x4e, 0xcc, 0x09, 0xe8,
- 0xa2, 0x8a, 0x60, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
- 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
- 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x02, 0xb8,
- 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
- 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x18, 0x51, 0x45, 0x14, 0x0a, 0xe1, 0x45,
- 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb9, 0xca, 0xfc, 0x4a, 0xf0,
- 0x52, 0x78, 0xef, 0xc2, 0xb7, 0x1a, 0x76, 0xef, 0x2e, 0xe5, 0x4f, 0x9d,
- 0x6c, 0xe4, 0xe0, 0x09, 0x00, 0x38, 0xcf, 0xb1, 0xc9, 0x1f, 0x8e, 0x7b,
- 0x57, 0x3d, 0xf0, 0x5b, 0xc7, 0x53, 0x6b, 0xba, 0x6c, 0xda, 0x16, 0xaa,
- 0x5a, 0x3d, 0x73, 0x4a, 0xfd, 0xd4, 0x8b, 0x2f, 0xdf, 0x91, 0x01, 0xc0,
- 0x27, 0xdc, 0x1e, 0x0f, 0xe0, 0x7b, 0xd7, 0xa3, 0x5c, 0xdc, 0xc5, 0x67,
- 0x04, 0x93, 0xcf, 0x2a, 0x43, 0x0c, 0x60, 0xb3, 0xc9, 0x23, 0x05, 0x55,
- 0x1e, 0xa4, 0x9e, 0x95, 0xf3, 0x47, 0xc4, 0x8f, 0x1f, 0xe9, 0x36, 0xdf,
- 0x10, 0xed, 0x3c, 0x41, 0xe1, 0x39, 0x99, 0xaf, 0xa1, 0xe2, 0xea, 0x42,
- 0x98, 0x82, 0x72, 0x38, 0xe3, 0x90, 0x4e, 0x46, 0x41, 0xe9, 0x9c, 0x0c,
- 0x7a, 0xd7, 0xc4, 0x67, 0x98, 0x9a, 0x59, 0x3e, 0x26, 0x9e, 0x64, 0xa6,
- 0x93, 0x7e, 0xec, 0xe3, 0x7d, 0x65, 0x1e, 0xe9, 0x77, 0x8b, 0xd7, 0xd3,
- 0x4b, 0x99, 0x4d, 0xa8, 0xbe, 0x63, 0xe9, 0xca, 0x2b, 0xe4, 0x3d, 0x73,
- 0xe3, 0x3f, 0x8b, 0xb5, 0xc6, 0x6d, 0xfa, 0xb4, 0x96, 0x71, 0x9e, 0x91,
- 0x59, 0x0f, 0x28, 0x0f, 0xc4, 0x7c, 0xdf, 0x99, 0xae, 0x56, 0xe7, 0x5a,
- 0xd4, 0x6f, 0x18, 0xb5, 0xc5, 0xfd, 0xd4, 0xec, 0x7b, 0xc9, 0x33, 0x31,
- 0xfd, 0x4d, 0x78, 0x75, 0xf8, 0xfb, 0x0b, 0x07, 0x6a, 0x14, 0x65, 0x25,
- 0xe6, 0xd2, 0xff, 0x00, 0x32, 0x1d, 0x75, 0xd1, 0x1f, 0x73, 0x51, 0x5f,
- 0x0b, 0xdb, 0xea, 0xf7, 0xf6, 0xad, 0xba, 0x0b, 0xdb, 0x88, 0x58, 0x77,
- 0x8e, 0x56, 0x53, 0xfa, 0x1a, 0xe9, 0xf4, 0x5f, 0x8b, 0xfe, 0x2e, 0xd0,
- 0xd9, 0x7c, 0xad, 0x66, 0x7b, 0x84, 0x1f, 0xf2, 0xce, 0xec, 0xf9, 0xc0,
- 0xff, 0x00, 0xdf, 0x59, 0x23, 0xf0, 0x34, 0xa8, 0x71, 0xf6, 0x1a, 0x4e,
- 0xd5, 0xa8, 0x4a, 0x2b, 0xc9, 0xa7, 0xfe, 0x40, 0xab, 0xae, 0xa8, 0xfa,
- 0x13, 0xe2, 0xff, 0x00, 0x8f, 0xcf, 0x82, 0xfc, 0x3e, 0x21, 0xb3, 0x6d,
- 0xda, 0xcd, 0xfe, 0x62, 0xb5, 0x45, 0xe5, 0x97, 0xb1, 0x7c, 0x7b, 0x67,
- 0x8f, 0x72, 0x3d, 0xea, 0x5f, 0x84, 0x7e, 0x05, 0x6f, 0x04, 0x78, 0x60,
- 0x2d, 0xd1, 0x2d, 0xa9, 0xde, 0xb0, 0x9e, 0xe8, 0x93, 0x9d, 0xad, 0x8e,
- 0x17, 0xf0, 0x1d, 0x4f, 0xa9, 0x35, 0xe2, 0x5e, 0x13, 0xf8, 0x89, 0x61,
- 0xac, 0xfc, 0x49, 0x8f, 0xc4, 0x3e, 0x30, 0x76, 0xcc, 0x68, 0x16, 0xd8,
- 0x43, 0x19, 0x68, 0x61, 0x61, 0xd0, 0x91, 0x92, 0x40, 0x1c, 0x9e, 0x33,
- 0xc9, 0xcd, 0x7d, 0x3b, 0x63, 0x7f, 0x6d, 0xaa, 0x5a, 0x45, 0x75, 0x69,
- 0x3c, 0x77, 0x36, 0xd2, 0x8d, 0xc9, 0x2c, 0x4c, 0x19, 0x58, 0x7b, 0x11,
- 0x5e, 0xde, 0x4d, 0x8b, 0xa3, 0x9d, 0xe3, 0x2a, 0x66, 0x1c, 0xe9, 0xf2,
- 0x5e, 0x30, 0x8f, 0x58, 0xae, 0xb2, 0x6b, 0xbc, 0xbf, 0x05, 0xa1, 0x50,
- 0x6a, 0x6f, 0x98, 0xb1, 0x45, 0x14, 0x57, 0xdc, 0x9b, 0x5c, 0x28, 0xa2,
- 0x8a, 0x02, 0xe1, 0x45, 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb8,
- 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
- 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x0b, 0x8d, 0xcd, 0x19, 0xa6, 0xe4, 0x51,
- 0x91, 0x55, 0x62, 0x47, 0x66, 0x8c, 0xd3, 0x72, 0x28, 0xc8, 0xa2, 0xc0,
- 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x01, 0xd9, 0xa3, 0x34,
- 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, 0xcd, 0x19, 0xa6, 0xe4, 0x52, 0xe4,
- 0x51, 0x60, 0xb8, 0xb9, 0xa3, 0x34, 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e,
- 0xdd, 0x46, 0x69, 0xb9, 0x14, 0x64, 0x51, 0x60, 0x1d, 0x9a, 0xa7, 0xac,
- 0x6b, 0x16, 0x9a, 0x0e, 0x9b, 0x71, 0xa8, 0x5f, 0x4c, 0x20, 0xb5, 0x81,
- 0x37, 0xbb, 0x9e, 0xc3, 0xd0, 0x7a, 0x93, 0xd0, 0x0a, 0xb5, 0x91, 0x5f,
- 0x39, 0xfe, 0xd1, 0x1e, 0x37, 0x7d, 0x4b, 0x5a, 0x4f, 0x0f, 0x5b, 0x48,
- 0x45, 0xa5, 0x96, 0x1e, 0x70, 0xa7, 0xef, 0xca, 0x46, 0x40, 0x3f, 0xee,
- 0x83, 0xf9, 0x93, 0xe9, 0x5e, 0x06, 0x79, 0x9a, 0xc3, 0x27, 0xc1, 0x4b,
- 0x12, 0xd5, 0xe5, 0xb4, 0x57, 0x76, 0xff, 0x00, 0xab, 0xbf, 0x24, 0x44,
- 0xe5, 0xca, 0xae, 0x72, 0xbf, 0x12, 0xbe, 0x2a, 0xea, 0x3e, 0x3e, 0xbd,
- 0x78, 0xd5, 0x9e, 0xd3, 0x48, 0x46, 0xfd, 0xd5, 0xa2, 0x9f, 0xbd, 0xe8,
- 0xcf, 0xea, 0x7f, 0x41, 0xdb, 0xd4, 0xc3, 0xe0, 0x5f, 0x85, 0x1a, 0xd7,
- 0x8f, 0xed, 0xe6, 0xb9, 0xb1, 0xf2, 0x2d, 0xed, 0x22, 0x6d, 0x86, 0x7b,
- 0x96, 0x21, 0x59, 0xb1, 0x9c, 0x0c, 0x02, 0x4f, 0x51, 0xf9, 0xd7, 0x19,
- 0x5e, 0xcd, 0xf0, 0x73, 0xe3, 0x16, 0x97, 0xe1, 0x0d, 0x06, 0x4d, 0x23,
- 0x57, 0x49, 0x63, 0x44, 0x95, 0xa5, 0x86, 0x78, 0x53, 0x78, 0x21, 0xba,
- 0xab, 0x0e, 0xb9, 0xcf, 0x7f, 0x7f, 0x6a, 0xfc, 0x1b, 0x2e, 0xa9, 0x87,
- 0xcd, 0xb3, 0x2f, 0x69, 0x9c, 0xd5, 0x6a, 0x2e, 0xfa, 0xde, 0xda, 0xf4,
- 0x57, 0xe8, 0xbf, 0xe1, 0x8e, 0x48, 0xda, 0x52, 0xf7, 0x8f, 0x30, 0xf1,
- 0x57, 0x85, 0x75, 0x0f, 0x06, 0xeb, 0x12, 0x69, 0xba, 0x94, 0x42, 0x3b,
- 0x84, 0x01, 0x83, 0x21, 0xca, 0xba, 0x9e, 0x8c, 0xa7, 0xb8, 0xac, 0x8a,
- 0xed, 0x3e, 0x2c, 0xf8, 0xee, 0x1f, 0x1f, 0xf8, 0x9c, 0x5e, 0xda, 0xc2,
- 0xf0, 0xda, 0x41, 0x08, 0x82, 0x2f, 0x33, 0x01, 0xd8, 0x02, 0x49, 0x63,
- 0xe9, 0xc9, 0x3c, 0x57, 0x17, 0x5e, 0x26, 0x3e, 0x9e, 0x1e, 0x96, 0x2a,
- 0xa4, 0x30, 0xb2, 0xe6, 0xa6, 0x9b, 0xb3, 0xee, 0x88, 0x76, 0xbe, 0x81,
- 0x5a, 0x1a, 0x06, 0x83, 0x7b, 0xe2, 0x7d, 0x5e, 0xdf, 0x4d, 0xd3, 0xe2,
- 0xf3, 0xae, 0xa7, 0x38, 0x55, 0xce, 0x00, 0x00, 0x64, 0x92, 0x7b, 0x00,
- 0x39, 0xac, 0xfa, 0xea, 0x3e, 0x1b, 0x78, 0xc1, 0x7c, 0x0d, 0xe2, 0xcb,
- 0x5d, 0x52, 0x58, 0x4c, 0xf6, 0xe1, 0x5a, 0x39, 0x51, 0x3e, 0xf6, 0xd6,
- 0x18, 0x24, 0x7b, 0x8e, 0x0d, 0x67, 0x83, 0x85, 0x1a, 0x98, 0x8a, 0x70,
- 0xc4, 0x4b, 0x96, 0x0d, 0xae, 0x67, 0xd9, 0x5f, 0x50, 0x56, 0xbe, 0xa6,
- 0x97, 0x8d, 0x7e, 0x0e, 0xeb, 0xde, 0x06, 0xd3, 0x17, 0x50, 0xbb, 0x36,
- 0xf7, 0x56, 0x99, 0x0b, 0x24, 0x96, 0xae, 0x4f, 0x96, 0x4f, 0x4d, 0xc0,
- 0x81, 0xc1, 0x3c, 0x66, 0xa9, 0xfc, 0x3e, 0xf8, 0x93, 0xaa, 0x78, 0x03,
- 0x50, 0x0f, 0x6c, 0xe6, 0x7b, 0x07, 0x6f, 0xdf, 0xd9, 0x3b, 0x7c, 0x8e,
- 0x3d, 0x47, 0xa3, 0x7b, 0xfe, 0x79, 0xaf, 0x45, 0xf8, 0xad, 0xf1, 0xb3,
- 0x47, 0xf1, 0x27, 0x85, 0x26, 0xd2, 0x34, 0x84, 0x9a, 0x67, 0xbb, 0x2b,
- 0xe6, 0xcb, 0x34, 0x7b, 0x04, 0x6a, 0x18, 0x36, 0x07, 0xa9, 0xc8, 0x1e,
- 0xd5, 0xe1, 0x95, 0xf4, 0x39, 0xab, 0xc2, 0x65, 0x79, 0x84, 0x67, 0x93,
- 0x55, 0x6d, 0x24, 0x9d, 0xd3, 0xbd, 0x9f, 0x55, 0x7e, 0xaa, 0xd6, 0xbe,
- 0xfb, 0xd8, 0xb9, 0x5a, 0x32, 0xf7, 0x59, 0xf6, 0xef, 0x86, 0xbc, 0x49,
- 0x63, 0xe2, 0xbd, 0x1a, 0xdf, 0x53, 0xd3, 0xe5, 0xf3, 0x2d, 0xe6, 0x1d,
- 0xfe, 0xf2, 0x1e, 0xea, 0xc3, 0xb1, 0x15, 0xa9, 0x9a, 0xf9, 0x7b, 0xe0,
- 0x27, 0x8d, 0xe4, 0xf0, 0xef, 0x8a, 0x53, 0x4a, 0x9e, 0x43, 0xfd, 0x9f,
- 0xa9, 0x30, 0x8f, 0x69, 0x3c, 0x24, 0xdf, 0xc0, 0xc3, 0xeb, 0xf7, 0x7f,
- 0x11, 0xe9, 0x5f, 0x4f, 0xe4, 0x57, 0xee, 0x3c, 0x3f, 0x9b, 0xc7, 0x39,
- 0xc1, 0x2a, 0xed, 0x5a, 0x6b, 0x49, 0x2f, 0x3f, 0xf2, 0x7b, 0xfe, 0x1d,
- 0x0e, 0xb8, 0x4f, 0x99, 0x5c, 0x76, 0x4d, 0x19, 0xa6, 0xe4, 0x51, 0x91,
- 0x5f, 0x4b, 0x62, 0xc7, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
- 0x2e, 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x0b, 0x8e, 0xcd,
- 0x19, 0xa6, 0xe4, 0x51, 0x91, 0x45, 0x80, 0x76, 0x68, 0xcd, 0x37, 0x34,
- 0x64, 0x51, 0x60, 0xb8, 0xec, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
- 0x07, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, 0x06, 0x6e, 0xa3,
- 0x75, 0x37, 0x34, 0x66, 0xae, 0xc4, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73,
- 0x46, 0x68, 0xb0, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, 0x46, 0x68, 0xb0,
- 0x5c, 0x76, 0xea, 0x37, 0x53, 0x72, 0x28, 0xcd, 0x16, 0x0b, 0x8e, 0xdd,
- 0x46, 0xea, 0x6e, 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0x6e,
- 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0xc4, 0xf1, 0x57, 0x8c,
- 0x34, 0xaf, 0x06, 0x69, 0xff, 0x00, 0x6b, 0xd5, 0x2e, 0x44, 0x28, 0xc7,
- 0x08, 0x8a, 0x37, 0x3c, 0x87, 0xd1, 0x47, 0x7f, 0xe5, 0x5c, 0x0d, 0x9f,
- 0xed, 0x1f, 0xe1, 0xcb, 0x8b, 0xc1, 0x14, 0xd6, 0x97, 0xf6, 0xb0, 0x93,
- 0x81, 0x3b, 0xa2, 0xb0, 0x1e, 0xe4, 0x06, 0x27, 0xf2, 0xcd, 0x78, 0xf8,
- 0xac, 0xdf, 0x2f, 0xc0, 0xd4, 0x54, 0x71, 0x35, 0xa3, 0x19, 0x3e, 0x8d,
- 0xfe, 0x7d, 0xbe, 0x64, 0xb9, 0x25, 0xb9, 0xeb, 0x05, 0xf6, 0x82, 0x4f,
- 0x41, 0x5f, 0x10, 0xeb, 0x7a, 0x93, 0xeb, 0x3a, 0xcd, 0xf5, 0xfc, 0x84,
- 0x97, 0xb9, 0x9d, 0xe6, 0x39, 0xff, 0x00, 0x69, 0x89, 0xfe, 0xb5, 0xf6,
- 0xad, 0x8e, 0xa1, 0x6b, 0xab, 0x58, 0xc5, 0x75, 0x69, 0x34, 0x77, 0x36,
- 0xb3, 0x2e, 0xe4, 0x91, 0x0e, 0x55, 0x85, 0x78, 0x5f, 0xfc, 0x2d, 0x5f,
- 0x87, 0x3f, 0xf4, 0x25, 0x27, 0xfe, 0x00, 0xdb, 0xff, 0x00, 0x8d, 0x7c,
- 0x67, 0x18, 0xe1, 0xa8, 0x63, 0x61, 0x87, 0x55, 0x31, 0x31, 0xa7, 0x1f,
- 0x79, 0xab, 0xdd, 0xf3, 0x7c, 0x3a, 0xab, 0x76, 0xfd, 0x4c, 0xea, 0x59,
- 0xdb, 0x53, 0xc4, 0x68, 0xaf, 0x6e, 0xff, 0x00, 0x85, 0xab, 0xf0, 0xe7,
- 0xfe, 0x84, 0xa4, 0xff, 0x00, 0xc0, 0x1b, 0x7f, 0xf1, 0xa3, 0xfe, 0x16,
- 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
- 0xc6, 0xbf, 0x33, 0xfe, 0xc5, 0xc0, 0xff, 0x00, 0xd0, 0x7c, 0x3e, 0xe9,
- 0x7f, 0x91, 0x8f, 0x2a, 0xee, 0x78, 0x8d, 0x15, 0xed, 0xdf, 0xf0, 0xb5,
- 0x7e, 0x1c, 0xff, 0x00, 0xd0, 0x94, 0x9f, 0xf8, 0x03, 0x6f, 0xfe, 0x34,
- 0x7f, 0xc2, 0xd5, 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d,
- 0xbf, 0xf8, 0xd1, 0xfd, 0x8b, 0x81, 0xff, 0x00, 0xa0, 0xf8, 0x7d, 0xd2,
- 0xff, 0x00, 0x20, 0xe5, 0x5d, 0xcf, 0x11, 0xa2, 0xbd, 0xbb, 0xfe, 0x16,
- 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
- 0xc6, 0x8f, 0xf8, 0x5a, 0xbf, 0x0e, 0x7f, 0xe8, 0x4a, 0x4f, 0xfc, 0x01,
- 0xb7, 0xff, 0x00, 0x1a, 0x3f, 0xb1, 0x70, 0x3f, 0xf4, 0x1f, 0x0f, 0xba,
- 0x5f, 0xe4, 0x1c, 0xab, 0xb9, 0xe2, 0x34, 0x57, 0xb7, 0x7f, 0xc2, 0xd5,
- 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd1,
- 0xff, 0x00, 0x0b, 0x57, 0xe1, 0xcf, 0xfd, 0x09, 0x49, 0xff, 0x00, 0x80,
- 0x36, 0xff, 0x00, 0xe3, 0x47, 0xf6, 0x2e, 0x07, 0xfe, 0x83, 0xe1, 0xf7,
- 0x4b, 0xfc, 0x83, 0x95, 0x77, 0x3c, 0x52, 0x09, 0xde, 0xda, 0x78, 0xe6,
- 0x89, 0x8a, 0x49, 0x1b, 0x07, 0x56, 0x1d, 0x41, 0x07, 0x20, 0xd7, 0xdb,
- 0xfa, 0x5d, 0xf0, 0xd4, 0x74, 0xdb, 0x4b, 0xb0, 0x30, 0x27, 0x85, 0x25,
- 0x03, 0xfd, 0xe5, 0x07, 0xfa, 0xd7, 0x85, 0xff, 0x00, 0xc2, 0xd5, 0xf8,
- 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd7, 0xb6,
- 0x69, 0x1a, 0x95, 0xa5, 0xc7, 0x87, 0xec, 0xaf, 0xe1, 0x55, 0xb3, 0xb1,
- 0x7b, 0x54, 0x99, 0x11, 0xf0, 0x82, 0x28, 0xca, 0x02, 0x01, 0xec, 0x30,
- 0x3f, 0x0e, 0x2b, 0xf4, 0x7e, 0x0e, 0xc2, 0xd0, 0xc1, 0xce, 0xbc, 0x69,
- 0x62, 0x63, 0x51, 0x34, 0x9b, 0x4a, 0xfa, 0x5a, 0xfa, 0xeb, 0xea, 0x6d,
- 0x4e, 0xca, 0xfa, 0x9a, 0x5b, 0xa8, 0xdd, 0x5e, 0x57, 0xab, 0xfe, 0xd1,
- 0x3e, 0x1b, 0xd3, 0xaf, 0x1a, 0x0b, 0x68, 0x6e, 0xf5, 0x15, 0x53, 0x83,
- 0x34, 0x28, 0xaa, 0x87, 0xe9, 0xb8, 0x82, 0x7f, 0x2a, 0xeb, 0xbc, 0x1b,
- 0xf1, 0x07, 0x45, 0xf1, 0xcd, 0xbb, 0xbe, 0x99, 0x70, 0x7c, 0xe8, 0xc6,
- 0x64, 0xb6, 0x98, 0x6d, 0x91, 0x07, 0xa9, 0x1d, 0xc7, 0xb8, 0xc8, 0xaf,
- 0xb7, 0xc3, 0xe7, 0x19, 0x76, 0x2a, 0xb7, 0xd5, 0xe8, 0x56, 0x8c, 0xa7,
- 0xd9, 0x3f, 0xcb, 0xbf, 0xc8, 0xd1, 0x49, 0x3d, 0x2e, 0x74, 0xdb, 0xa8,
- 0xcd, 0x37, 0x34, 0x64, 0x57, 0xb2, 0x55, 0xc7, 0x6e, 0xa3, 0x75, 0x37,
- 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b,
- 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e,
- 0xa3, 0x75, 0x37, 0x34, 0x64, 0x51, 0x60, 0xb8, 0xed, 0xd4, 0x6e, 0xa6,
- 0xe4, 0x51, 0x9a, 0x2c, 0x17, 0x1b, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75,
- 0x55, 0x88, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, 0xd4, 0x58, 0x2e,
- 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, 0xc9, 0x32, 0x28,
- 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, 0x1e, 0xea,
- 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8c, 0xd3, 0xb0,
- 0x5c, 0xf9, 0x7b, 0xe3, 0xb6, 0xaf, 0x3e, 0xa5, 0xf1, 0x0e, 0xf2, 0xde,
- 0x47, 0x26, 0x1b, 0x24, 0x48, 0x62, 0x4e, 0xc0, 0x15, 0x0c, 0x4f, 0xe2,
- 0x58, 0xfe, 0x95, 0xe7, 0x95, 0xda, 0x7c, 0x64, 0xff, 0x00, 0x92, 0x97,
- 0xad, 0xff, 0x00, 0xbf, 0x1f, 0xfe, 0x8a, 0x4a, 0xe2, 0xeb, 0xf9, 0x47,
- 0x3a, 0x9c, 0xaa, 0x66, 0x78, 0x99, 0x49, 0xdd, 0xf3, 0xcb, 0xf0, 0x6d,
- 0x23, 0x92, 0x5b, 0x9e, 0xf7, 0xfb, 0x34, 0xeb, 0x13, 0xcd, 0x67, 0xac,
- 0xe9, 0xb2, 0x39, 0x6b, 0x78, 0x1a, 0x39, 0xa2, 0x04, 0xfd, 0xd2, 0xdb,
- 0x83, 0x0f, 0xc7, 0x68, 0xfd, 0x6b, 0xc1, 0x2b, 0xda, 0xff, 0x00, 0x66,
- 0x73, 0x8b, 0xed, 0x7f, 0xfe, 0xb9, 0xc3, 0xfc, 0xde, 0xbc, 0x52, 0xbd,
- 0x8c, 0xd2, 0x72, 0x9e, 0x4b, 0x97, 0x39, 0x3b, 0xdb, 0xda, 0xaf, 0x92,
- 0x92, 0xb1, 0x4f, 0xe1, 0x41, 0x45, 0x14, 0x57, 0xc6, 0x90, 0x14, 0x51,
- 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0xef,
- 0x7f, 0x13, 0xf5, 0x89, 0xf4, 0xef, 0x82, 0xbe, 0x19, 0xb6, 0x81, 0xca,
- 0x0b, 0xc8, 0x2d, 0x62, 0x94, 0x83, 0xd5, 0x04, 0x3b, 0x88, 0xfc, 0x48,
- 0x15, 0xe0, 0x95, 0xed, 0x7f, 0x17, 0x0f, 0xfc, 0x5a, 0x5f, 0x05, 0xff,
- 0x00, 0xd7, 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x96, 0x47, 0x39, 0x43, 0x03,
- 0x98, 0x4a, 0x2e, 0xcf, 0x91, 0x7e, 0x32, 0xb3, 0xfc, 0x0a, 0x8e, 0xcc,
- 0xf1, 0x4a, 0xe9, 0xbe, 0x1a, 0x6a, 0xf3, 0xe8, 0xbe, 0x3a, 0xd1, 0x67,
- 0x81, 0xca, 0x99, 0x2e, 0x52, 0x07, 0x00, 0xfd, 0xe4, 0x76, 0x0a, 0xc0,
- 0xfe, 0x07, 0xf4, 0xae, 0x66, 0xb6, 0x3c, 0x1b, 0xff, 0x00, 0x23, 0x7e,
- 0x87, 0xff, 0x00, 0x5f, 0xd0, 0x7f, 0xe8, 0xc5, 0xaf, 0x9b, 0xc0, 0xce,
- 0x54, 0xf1, 0x54, 0xa7, 0x07, 0x66, 0xa4, 0xbf, 0x31, 0x2d, 0xcf, 0xb4,
- 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xaf, 0xeb, 0x9b, 0x1d, 0x57, 0x24,
- 0xcd, 0x19, 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35,
- 0x1e, 0xea, 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d,
- 0xd4, 0x58, 0x2e, 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15,
- 0xc9, 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xa2, 0xc3, 0xb8, 0xdd, 0xd4,
- 0x9b, 0xa9, 0xbb, 0xa8, 0xdd, 0x5a, 0x19, 0xdc, 0x7e, 0xea, 0x4d, 0xd4,
- 0xdd, 0xd4, 0x6e, 0xa0, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e,
- 0xa0, 0x77, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, 0xa4, 0x2b, 0x8f,
- 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0xc2, 0xe3, 0xb7, 0x52, 0xee,
- 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb9, 0xf2, 0x9f, 0xc6, 0x3f, 0xf9, 0x29,
- 0x5a, 0xdf, 0xfb, 0xf1, 0xff, 0x00, 0xe8, 0xb4, 0xae, 0x32, 0xbb, 0x2f,
- 0x8c, 0x5c, 0xfc, 0x49, 0xd6, 0xff, 0x00, 0xdf, 0x8f, 0xff, 0x00, 0x45,
- 0xa5, 0x71, 0xb5, 0xfc, 0x99, 0x9c, 0x7f, 0xc8, 0xcb, 0x13, 0xfe, 0x39,
- 0xff, 0x00, 0xe9, 0x4c, 0xc1, 0xee, 0x7b, 0x57, 0xec, 0xd2, 0x71, 0x7d,
- 0xaf, 0x7f, 0xd7, 0x38, 0x7f, 0x9b, 0xd7, 0x8a, 0xd7, 0xb4, 0x7e, 0xcd,
- 0x67, 0x17, 0xda, 0xf7, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, 0xbd, 0x7b,
- 0x19, 0x97, 0xfc, 0x89, 0x32, 0xef, 0xfb, 0x8b, 0xff, 0x00, 0xa5, 0x21,
- 0xbd, 0x90, 0x51, 0x45, 0x15, 0xf2, 0x02, 0x0a, 0x28, 0xa2, 0x80, 0x0a,
- 0x28, 0xa2, 0x80, 0x0a, 0x28, 0xa2, 0x80, 0x0a, 0xf6, 0xaf, 0x8b, 0x47,
- 0x3f, 0x09, 0x7c, 0x17, 0xff, 0x00, 0x5c, 0xe0, 0xff, 0x00, 0xd1, 0x15,
- 0xe2, 0xb5, 0xed, 0x1f, 0x16, 0x4f, 0xfc, 0x5a, 0x6f, 0x06, 0x7f, 0xd7,
- 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x7e, 0x4d, 0xff, 0x00, 0x22, 0xfc, 0xc3,
- 0xfc, 0x11, 0xff, 0x00, 0xd2, 0x90, 0xd6, 0xcc, 0xf1, 0x7a, 0xd8, 0xf0,
- 0x67, 0xfc, 0x8e, 0x1a, 0x17, 0xfd, 0x7f, 0xc1, 0xff, 0x00, 0xa3, 0x16,
- 0xb1, 0xeb, 0x63, 0xc1, 0xdf, 0xf2, 0x37, 0x68, 0x7f, 0xf5, 0xfd, 0x07,
- 0xfe, 0x8c, 0x5a, 0xf9, 0xbc, 0x27, 0xfb, 0xcd, 0x3f, 0xf1, 0x2f, 0xcc,
- 0x48, 0xfb, 0x2b, 0x75, 0x1b, 0xa9, 0x9b, 0xa8, 0xdd, 0x5f, 0xd8, 0x16,
- 0x37, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb8, 0xed,
- 0xd4, 0xbb, 0xa9, 0x9b, 0xa8, 0xdd, 0x4c, 0x2e, 0x3f, 0x75, 0x26, 0xea,
- 0x6e, 0xea, 0x37, 0x52, 0x0b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d,
- 0xd4, 0xec, 0x3b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0x0a,
- 0xe4, 0x74, 0x53, 0x33, 0x4b, 0x93, 0x57, 0x63, 0x3b, 0x8e, 0xa2, 0x9b,
- 0x9a, 0x4c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa3, 0x34, 0x58, 0x2e,
- 0x3f, 0x34, 0x53, 0x33, 0x4b, 0x93, 0x45, 0x82, 0xe3, 0xa8, 0xcd, 0x33,
- 0x34, 0x66, 0x8b, 0x05, 0xc7, 0xd1, 0x4d, 0xc9, 0xa3, 0x26, 0x8b, 0x05,
- 0xcf, 0x96, 0x3e, 0x30, 0x7f, 0xc9, 0x48, 0xd6, 0xbf, 0xdf, 0x8f, 0xff,
- 0x00, 0x45, 0xa5, 0x71, 0xb5, 0xdd, 0xfc, 0x6c, 0xd3, 0xa6, 0xb1, 0xf8,
- 0x85, 0x7f, 0x2c, 0x8a, 0x44, 0x77, 0x4b, 0x1c, 0xd1, 0xb7, 0x66, 0x1b,
- 0x02, 0x9f, 0xd5, 0x4d, 0x70, 0x95, 0xfc, 0x97, 0x9d, 0x42, 0x50, 0xcc,
- 0xf1, 0x31, 0x92, 0xb7, 0xbf, 0x2f, 0xfd, 0x29, 0x90, 0x7b, 0x3f, 0xec,
- 0xdb, 0xff, 0x00, 0x1f, 0xba, 0xef, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78,
- 0xc5, 0x7b, 0x87, 0xec, 0xe3, 0xa7, 0x4d, 0x1c, 0x3a, 0xd5, 0xf3, 0x29,
- 0x58, 0x24, 0x31, 0xc2, 0x8d, 0xfd, 0xe2, 0x37, 0x16, 0xfc, 0xb2, 0x3f,
- 0x3a, 0xf0, 0xfa, 0xf6, 0x73, 0x58, 0x4a, 0x19, 0x26, 0x5b, 0xcc, 0xad,
- 0x7f, 0x6a, 0xff, 0x00, 0xf2, 0x64, 0x01, 0x45, 0x14, 0x57, 0xc6, 0x00,
- 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00,
- 0x57, 0xb3, 0xfc, 0x57, 0xff, 0x00, 0x92, 0x51, 0xe0, 0xdf, 0xfa, 0xe7,
- 0x07, 0xfe, 0x88, 0xaf, 0x18, 0xaf, 0x70, 0xf8, 0x9b, 0xa7, 0x4d, 0x79,
- 0xf0, 0x77, 0xc3, 0x37, 0x11, 0x29, 0x74, 0xb5, 0x8a, 0xd9, 0xe4, 0xc7,
- 0x65, 0x30, 0xed, 0xcf, 0xe6, 0x40, 0xfc, 0x6b, 0xec, 0xf2, 0x38, 0x4a,
- 0x78, 0x0c, 0xc1, 0x45, 0x5f, 0xdc, 0x5f, 0x84, 0xae, 0xc0, 0xf0, 0xfa,
- 0xd8, 0xf0, 0x6f, 0xfc, 0x8d, 0xfa, 0x1f, 0xfd, 0x7f, 0x41, 0xff, 0x00,
- 0xa3, 0x16, 0xb1, 0xeb, 0xa2, 0xf8, 0x79, 0xa7, 0x4d, 0xaa, 0x78, 0xdf,
- 0x45, 0x86, 0x15, 0x2c, 0xcb, 0x75, 0x1c, 0xad, 0x8e, 0xca, 0x8c, 0x18,
- 0x9f, 0xc8, 0x57, 0xcd, 0x60, 0x61, 0x29, 0xe2, 0xe9, 0x46, 0x2a, 0xed,
- 0xca, 0x3f, 0x9a, 0x03, 0xeb, 0x9c, 0xd1, 0x4c, 0xcd, 0x2e, 0x4d, 0x7f,
- 0x60, 0x58, 0xbb, 0x8e, 0xa2, 0x99, 0x9a, 0x33, 0x45, 0x82, 0xe3, 0xe8,
- 0xa6, 0x66, 0x8c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa5, 0xc9, 0xa2,
- 0xc1, 0x71, 0xd9, 0xa2, 0x9b, 0x93, 0x49, 0x9a, 0x2c, 0x17, 0x1f, 0x45,
- 0x33, 0x34, 0x66, 0x8b, 0x05, 0xc6, 0x6e, 0xa3, 0x75, 0x30, 0x90, 0x3a,
- 0x9c, 0x51, 0x9a, 0xd2, 0xc6, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xda, 0x4c,
- 0xd1, 0x60, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x02, 0x0f, 0x43, 0x9a, 0x37,
- 0x01, 0xdf, 0x14, 0x58, 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x94, 0x58,
- 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x80, 0x83, 0xd0, 0xe6, 0x8b, 0x05,
- 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x3a, 0x9c, 0x51, 0x9a, 0x2c, 0x17,
- 0x31, 0x3c, 0x5d, 0xe0, 0xcd, 0x2f, 0xc6, 0xb6, 0x2b, 0x6f, 0xa8, 0xc4,
- 0x4b, 0x26, 0x4c, 0x53, 0xc6, 0x71, 0x24, 0x64, 0xf5, 0xc1, 0xfe, 0x87,
- 0x8a, 0xe0, 0x6d, 0x3f, 0x67, 0x7d, 0x32, 0x2b, 0xb0, 0xf7, 0x1a, 0xad,
- 0xcc, 0xf6, 0xe0, 0xe7, 0xca, 0x58, 0xd5, 0x09, 0x1e, 0x85, 0xb2, 0x7f,
- 0x95, 0x7a, 0xd5, 0x25, 0x78, 0x38, 0xcc, 0x87, 0x2c, 0xcc, 0x2b, 0x2a,
- 0xf8, 0x9a, 0x2a, 0x52, 0xef, 0xaa, 0xfb, 0xec, 0xd5, 0xfe, 0x77, 0x0b,
- 0x95, 0xf4, 0xad, 0x32, 0xd3, 0x44, 0xb0, 0x86, 0xca, 0xc6, 0x05, 0xb7,
- 0xb6, 0x88, 0x61, 0x23, 0x4e, 0x83, 0xfc, 0x4f, 0xbd, 0x7c, 0x6b, 0x5f,
- 0x69, 0x57, 0xc5, 0xb5, 0xf9, 0x8f, 0x88, 0xb0, 0x8d, 0x38, 0xe0, 0xe1,
- 0x05, 0x64, 0xb9, 0xec, 0x97, 0xfd, 0xb8, 0x34, 0x14, 0x51, 0x45, 0x7e,
- 0x32, 0x30, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xaf, 0xad, 0xfc, 0x2b, 0x04, 0x57, 0x9e, 0x06, 0xd1, 0xad,
- 0xe7, 0x8d, 0x66, 0x86, 0x4d, 0x3a, 0x04, 0x78, 0xdc, 0x65, 0x58, 0x18,
- 0xd7, 0x20, 0x8a, 0xf9, 0x22, 0xbe, 0xba, 0xf0, 0x67, 0xfc, 0x89, 0xfa,
- 0x17, 0xfd, 0x78, 0x41, 0xff, 0x00, 0xa2, 0xd6, 0xbf, 0x5c, 0xf0, 0xee,
- 0x2a, 0x58, 0x9c, 0x42, 0x7b, 0x72, 0xaf, 0xcc, 0x47, 0x05, 0xab, 0x7e,
- 0xcf, 0x7a, 0x4d, 0xdd, 0xdb, 0x4b, 0x65, 0xa8, 0x4f, 0x63, 0x13, 0x1c,
- 0xf9, 0x25, 0x04, 0x80, 0x7b, 0x02, 0x48, 0x3f, 0x9e, 0x6b, 0xaf, 0xf0,
- 0x57, 0xc3, 0xcd, 0x27, 0xc0, 0xd1, 0xb9, 0xb3, 0x47, 0x9a, 0xee, 0x41,
- 0xb6, 0x4b, 0xa9, 0xb0, 0x5c, 0x8f, 0x41, 0xe8, 0x3d, 0x87, 0xe3, 0x5d,
- 0x36, 0x69, 0x6b, 0xf5, 0x7c, 0x37, 0x0f, 0xe5, 0x78, 0x3a, 0xff, 0x00,
- 0x59, 0xa1, 0x41, 0x46, 0x7d, 0xf5, 0xd3, 0xd1, 0x6c, 0xbe, 0x49, 0x0a,
- 0xe3, 0xb7, 0x51, 0xba, 0x99, 0x9a, 0x03, 0x03, 0xde, 0xbd, 0xfb, 0x05,
- 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x06, 0x4f, 0x14, 0x51, 0x60, 0xb8,
- 0xfd, 0xd4, 0x6e, 0xa6, 0xd1, 0x45, 0x82, 0xe3, 0xb7, 0x51, 0xba, 0x98,
- 0x08, 0x3d, 0x0e, 0x68, 0x24, 0x0e, 0xf4, 0x58, 0x2e, 0x3f, 0x75, 0x1b,
- 0xa9, 0x94, 0xb4, 0x58, 0x2e, 0x3b, 0x75, 0x1b, 0xa9, 0x99, 0xa0, 0x10,
- 0x7a, 0x1a, 0x2c, 0x17, 0x31, 0x62, 0xbd, 0x91, 0x46, 0xf7, 0x90, 0x3e,
- 0xd3, 0xf7, 0x1b, 0xa9, 0xad, 0x58, 0xa5, 0x13, 0x46, 0xae, 0xbd, 0x08,
- 0xac, 0x3f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, 0x91, 0x56, 0x52, 0xf1,
- 0xe3, 0x48, 0x55, 0x42, 0x80, 0x7a, 0x80, 0x3d, 0xeb, 0xb2, 0x70, 0xbe,
- 0xc7, 0x9d, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x36, 0x59, 0x44, 0x31,
- 0xb3, 0xb7, 0x41, 0x49, 0xba, 0xb3, 0x1e, 0xf2, 0x49, 0x12, 0x65, 0x60,
- 0x08, 0x1d, 0x01, 0x1e, 0xf5, 0x84, 0x61, 0xcc, 0x75, 0x4e, 0xa7, 0x22,
- 0x1b, 0x2d, 0xec, 0x8c, 0x0b, 0xa4, 0x81, 0x37, 0x1f, 0xb8, 0xbd, 0x7e,
- 0xb4, 0xb1, 0x5e, 0xc8, 0xa0, 0x3b, 0xb8, 0x7d, 0xa7, 0xee, 0x37, 0x5f,
- 0xad, 0x57, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x14, 0x79, 0xc7, 0xfb,
- 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xeb, 0xe5, 0x56, 0xb5, 0x8e, 0x0e, 0x77,
- 0x7b, 0xdc, 0xdd, 0x8e, 0x41, 0x2a, 0x2b, 0xaf, 0x42, 0x33, 0x4e, 0xac,
- 0xa8, 0xef, 0x1d, 0x3c, 0x85, 0x00, 0x05, 0x3d, 0x40, 0x1e, 0xe6, 0xb4,
- 0x77, 0x57, 0x24, 0xa3, 0xca, 0x77, 0xc2, 0x7c, 0xe8, 0x74, 0x92, 0x08,
- 0xd1, 0x99, 0x8f, 0x00, 0x66, 0xb2, 0xa5, 0xbd, 0x91, 0x81, 0x74, 0x70,
- 0x99, 0x38, 0xd8, 0x3a, 0xfd, 0x69, 0xd2, 0x5e, 0x3b, 0xf9, 0xea, 0x40,
- 0x2a, 0x3a, 0x02, 0x3d, 0xc5, 0x55, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9,
- 0x15, 0xbc, 0x21, 0x6d, 0xce, 0x6a, 0x95, 0x6f, 0xa2, 0x64, 0xf1, 0x5e,
- 0x48, 0xa3, 0x7b, 0x48, 0x1f, 0x69, 0xfb, 0x8d, 0xd4, 0xd6, 0xac, 0x52,
- 0x89, 0xa3, 0x57, 0x5e, 0x86, 0xb0, 0xfc, 0xe3, 0xfd, 0xc4, 0xff, 0x00,
- 0xbe, 0x45, 0x59, 0x4b, 0xc7, 0x8d, 0x61, 0x0a, 0x14, 0x03, 0xd4, 0x01,
- 0xef, 0x44, 0xe1, 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x15,
- 0x1e, 0xea, 0x5d, 0xd5, 0xcd, 0x63, 0xba, 0xe3, 0xeb, 0xc0, 0xbc, 0x71,
- 0xf0, 0x57, 0x55, 0x83, 0x57, 0x9e, 0xe7, 0x44, 0x85, 0x6f, 0x6c, 0xa6,
- 0x72, 0xe2, 0x25, 0x70, 0xaf, 0x16, 0x4e, 0x76, 0xe0, 0x91, 0x91, 0xe9,
- 0x8a, 0xf7, 0xad, 0xd4, 0x66, 0xbe, 0x7f, 0x39, 0xc8, 0xf0, 0x99, 0xe5,
- 0x18, 0xd2, 0xc4, 0xdd, 0x72, 0xbb, 0xa6, 0xb7, 0x5d, 0xfb, 0xef, 0xe8,
- 0x17, 0xb1, 0xf2, 0xc5, 0xff, 0x00, 0xc3, 0x5f, 0x12, 0xe9, 0x96, 0x53,
- 0x5d, 0xdd, 0x69, 0x52, 0x43, 0x6f, 0x0a, 0x97, 0x92, 0x42, 0xe8, 0x42,
- 0x81, 0xd4, 0xf0, 0x6b, 0x99, 0xaf, 0xaa, 0xfe, 0x21, 0x9c, 0xf8, 0x1f,
- 0x5c, 0xff, 0x00, 0xaf, 0x47, 0xfe, 0x55, 0xf2, 0xa5, 0x7e, 0x01, 0xc5,
- 0x59, 0x1e, 0x1f, 0x22, 0xc4, 0x53, 0xa3, 0x87, 0x93, 0x92, 0x94, 0x6f,
- 0xef, 0x5b, 0xbd, 0xba, 0x24, 0x5a, 0x77, 0x0a, 0xe9, 0xec, 0xbe, 0x19,
- 0xf8, 0x9b, 0x51, 0xb3, 0x86, 0xea, 0xdb, 0x49, 0x92, 0x5b, 0x79, 0x90,
- 0x49, 0x1b, 0x87, 0x40, 0x19, 0x48, 0xc8, 0x3d, 0x6b, 0x98, 0xaf, 0xac,
- 0x3c, 0x08, 0x71, 0xe0, 0xad, 0x0b, 0xfe, 0xbc, 0xa1, 0xff, 0x00, 0xd0,
- 0x05, 0x57, 0x0a, 0xe4, 0x58, 0x7c, 0xf6, 0xbd, 0x5a, 0x58, 0x89, 0x4a,
- 0x2a, 0x2a, 0xfe, 0xed, 0xbb, 0xdb, 0xaa, 0x60, 0xdd, 0x8f, 0x9e, 0xdb,
- 0xe1, 0x4f, 0x8a, 0xd1, 0x4b, 0x1d, 0x1a, 0x50, 0x00, 0xc9, 0x3e, 0x62,
- 0x7f, 0xf1, 0x55, 0xc9, 0xd7, 0xd9, 0x17, 0x4d, 0xfe, 0x8d, 0x37, 0xfb,
- 0x87, 0xf9, 0x57, 0xc6, 0xf5, 0xd1, 0xc5, 0x9c, 0x3d, 0x86, 0xc8, 0x5d,
- 0x05, 0x87, 0x9c, 0xa5, 0xcf, 0xcd, 0x7e, 0x6b, 0x74, 0xb6, 0xd6, 0x4b,
- 0xb8, 0x27, 0x70, 0xae, 0x87, 0x47, 0xf0, 0x07, 0x88, 0x35, 0xfb, 0x04,
- 0xbd, 0xb0, 0xd3, 0x64, 0xb9, 0xb5, 0x72, 0x42, 0xc8, 0xae, 0xa0, 0x12,
- 0x0e, 0x0f, 0x53, 0xeb, 0x5c, 0xf5, 0x7d, 0x27, 0xf0, 0x54, 0xe3, 0xe1,
- 0xed, 0x8f, 0xfd, 0x74, 0x97, 0xff, 0x00, 0x43, 0x35, 0xe7, 0x70, 0xbe,
- 0x4f, 0x43, 0x3c, 0xc6, 0xcb, 0x0d, 0x5e, 0x4d, 0x25, 0x16, 0xf4, 0xb5,
- 0xee, 0x9a, 0x5d, 0x53, 0xee, 0x0d, 0xd8, 0xf3, 0x1f, 0x0d, 0x7c, 0x12,
- 0xd7, 0xb5, 0x3d, 0x42, 0x31, 0xa9, 0x40, 0x34, 0xdb, 0x20, 0xc0, 0xc8,
- 0xee, 0xea, 0xce, 0x47, 0x70, 0xa0, 0x13, 0xcf, 0xb9, 0xe2, 0xbe, 0x87,
- 0xb7, 0x82, 0x3b, 0x4b, 0x78, 0xa0, 0x89, 0x42, 0x45, 0x12, 0x84, 0x45,
- 0x1d, 0x80, 0x18, 0x02, 0x97, 0x34, 0x9b, 0xab, 0xfa, 0x07, 0x25, 0xe1,
- 0xfc, 0x1e, 0x45, 0x09, 0x47, 0x0d, 0x76, 0xe5, 0xbb, 0x7a, 0xbd, 0x36,
- 0x5a, 0x24, 0xad, 0xf2, 0x22, 0xf7, 0x24, 0xcd, 0x19, 0xa8, 0xf7, 0x51,
- 0xba, 0xbe, 0x96, 0xc1, 0x71, 0xd2, 0x48, 0x22, 0x46, 0x76, 0xe0, 0x0e,
- 0x6b, 0x2a, 0x5b, 0xd9, 0x18, 0x17, 0x47, 0x11, 0xe4, 0xe3, 0x60, 0xeb,
- 0xf5, 0xa7, 0x49, 0x78, 0xef, 0xe7, 0xa9, 0x00, 0xa8, 0xe8, 0x08, 0xf7,
- 0x02, 0xaa, 0xf9, 0xc7, 0xfb, 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xe9, 0x84,
- 0x2d, 0xb9, 0xc5, 0x56, 0xaf, 0x36, 0x89, 0x93, 0xc5, 0x7b, 0x22, 0x8d,
- 0xef, 0x20, 0x7d, 0xa7, 0xee, 0x37, 0x53, 0x5a, 0xd1, 0x4a, 0x26, 0x8d,
- 0x5d, 0x7a, 0x1a, 0xc2, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x15, 0x65,
- 0x2f, 0x1e, 0x34, 0x84, 0x28, 0x50, 0x09, 0xe4, 0x01, 0xef, 0x44, 0xe1,
- 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xa6, 0xcb, 0x28, 0x86, 0x36,
- 0x76, 0xe8, 0x29, 0x37, 0x56, 0x63, 0xde, 0x3c, 0x89, 0x30, 0x60, 0xa4,
- 0x0e, 0x80, 0x8f, 0x7a, 0xc2, 0x30, 0xe6, 0x3a, 0xa7, 0x53, 0x91, 0x0d,
- 0x96, 0xf6, 0x46, 0x05, 0xd2, 0x40, 0x9b, 0x8f, 0xdc, 0x5e, 0xa3, 0xde,
- 0x96, 0x2b, 0xd9, 0x14, 0x07, 0x77, 0x0f, 0xb4, 0xfd, 0xc6, 0xeb, 0xf5,
- 0xaa, 0xfe, 0x71, 0xfe, 0xe2, 0x7f, 0xdf, 0x22, 0x8f, 0x38, 0xff, 0x00,
- 0x71, 0x3f, 0xef, 0x91, 0x5d, 0x7c, 0xaa, 0xd6, 0xb1, 0xc1, 0xce, 0xef,
- 0x7b, 0x9b, 0xb1, 0xc8, 0x25, 0x45, 0x75, 0xe8, 0x46, 0x69, 0xd5, 0x95,
- 0x1d, 0xe3, 0xa0, 0x81, 0x46, 0x02, 0x9e, 0xa0, 0x0f, 0x72, 0x2b, 0x4b,
- 0x35, 0xc9, 0x28, 0xf2, 0x9d, 0xf0, 0xa9, 0xce, 0x85, 0x92, 0x41, 0x1a,
- 0x33, 0xb7, 0x40, 0x32, 0x6b, 0x2a, 0x5b, 0xd9, 0x1c, 0x17, 0x47, 0x09,
- 0x93, 0x8d, 0x83, 0xaf, 0xd6, 0x9d, 0x25, 0xe3, 0xbf, 0x9e, 0xa4, 0x02,
- 0xa3, 0xa0, 0x23, 0xdc, 0x55, 0x5f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef,
- 0x91, 0x5b, 0xc2, 0x16, 0xdc, 0xe6, 0xa9, 0x57, 0x9b, 0x44, 0xc8, 0xea,
- 0x70, 0xa4, 0x88, 0x30, 0x09, 0xff, 0x00, 0xf5, 0xd3, 0x4d, 0xbb, 0x09,
- 0x84, 0x59, 0x1b, 0xbd, 0x7b, 0x56, 0x95, 0xb4, 0x66, 0x18, 0x42, 0x13,
- 0x92, 0x3d, 0x2a, 0xe5, 0x2b, 0x23, 0x2a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
- 0xc8, 0x2a, 0x40, 0x9f, 0x20, 0x8f, 0xff, 0x00, 0x5d, 0x6a, 0xe6, 0xa3,
- 0xb9, 0x8c, 0xcf, 0x09, 0x40, 0x40, 0x27, 0xd6, 0xb1, 0x83, 0xe5, 0x3a,
- 0x6a, 0x47, 0x99, 0x5c, 0xc7, 0xa2, 0xa5, 0x5b, 0x76, 0x69, 0x8c, 0x59,
- 0x1b, 0xbf, 0x4a, 0x3e, 0xce, 0xde, 0x7f, 0x95, 0x91, 0xbb, 0xd7, 0xb5,
- 0x74, 0xdd, 0x1c, 0x3c, 0xac, 0x7a, 0xa9, 0x2d, 0x6f, 0x80, 0x7f, 0xcb,
- 0x1a, 0xd6, 0xa8, 0x6d, 0xe3, 0x30, 0xc2, 0xa8, 0x48, 0x24, 0x77, 0x15,
- 0x26, 0x6b, 0x9a, 0x6f, 0x99, 0x9d, 0xf4, 0xe3, 0xca, 0x8c, 0xb6, 0x52,
- 0x1a, 0xe3, 0x20, 0xff, 0x00, 0x96, 0x15, 0x5e, 0xb6, 0x2e, 0x23, 0x33,
- 0x44, 0xc8, 0x08, 0x04, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
- 0x7a, 0xf6, 0xad, 0xa1, 0x24, 0xd1, 0xcb, 0x52, 0x0e, 0x2d, 0x58, 0x8a,
- 0xa7, 0x0a, 0x48, 0x83, 0x00, 0x9f, 0xff, 0x00, 0x5d, 0x34, 0xdb, 0xb2,
- 0xcc, 0x22, 0xc8, 0xdd, 0xeb, 0xda, 0xb4, 0xad, 0xa3, 0x30, 0xc2, 0xaa,
- 0x4e, 0x48, 0xf4, 0xa2, 0x52, 0xb2, 0x0a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
- 0x29, 0xa4, 0xd1, 0x9a, 0xe5, 0xb1, 0xde, 0x3a, 0x8a, 0x6e, 0x73, 0x41,
- 0x34, 0x58, 0x0e, 0x7f, 0xe2, 0x1f, 0xfc, 0x88, 0xfa, 0xe7, 0xfd, 0x7a,
- 0xbf, 0xf2, 0xaf, 0x95, 0xab, 0xeb, 0x2f, 0x18, 0x58, 0x4b, 0xaa, 0xf8,
- 0x57, 0x56, 0xb4, 0x80, 0x6e, 0x9a, 0x6b, 0x69, 0x15, 0x17, 0xd5, 0xb6,
- 0x9c, 0x0f, 0xce, 0xbe, 0x4e, 0x65, 0x2a, 0x48, 0x20, 0x82, 0x38, 0x20,
- 0xf6, 0xaf, 0xc1, 0x7c, 0x46, 0x84, 0x96, 0x32, 0x84, 0xed, 0xa3, 0x8b,
- 0x5f, 0x73, 0xff, 0x00, 0x82, 0x8d, 0x20, 0x25, 0x7d, 0x5d, 0xe0, 0x5f,
- 0xf9, 0x12, 0xf4, 0x2f, 0xfa, 0xf2, 0x87, 0xff, 0x00, 0x40, 0x15, 0xf2,
- 0x9a, 0x23, 0x48, 0xea, 0x88, 0xa5, 0x9d, 0x8e, 0x02, 0x81, 0x92, 0x4d,
- 0x7d, 0x69, 0xe1, 0x8b, 0x19, 0x34, 0xbf, 0x0d, 0xe9, 0x76, 0x73, 0x71,
- 0x2c, 0x16, 0xd1, 0xc6, 0xe3, 0xd0, 0x85, 0x00, 0xd5, 0x78, 0x73, 0x09,
- 0x3c, 0x56, 0x22, 0x76, 0xd1, 0x45, 0x2f, 0xc7, 0xfe, 0x00, 0x4c, 0xd0,
- 0xba, 0xff, 0x00, 0x8f, 0x69, 0xbf, 0xdc, 0x3f, 0xca, 0xbe, 0x39, 0xaf,
- 0xb1, 0xe5, 0x5f, 0x32, 0x27, 0x4c, 0xe3, 0x72, 0x91, 0x9a, 0xf9, 0x03,
- 0x50, 0xb1, 0x9b, 0x4c, 0xbe, 0xb8, 0xb4, 0x9d, 0x0a, 0x4d, 0x04, 0x86,
- 0x37, 0x53, 0xd8, 0x83, 0x8a, 0xed, 0xf1, 0x22, 0x12, 0xff, 0x00, 0x65,
- 0x9d, 0xb4, 0xf7, 0xd7, 0xfe, 0x92, 0x10, 0x2b, 0xd7, 0xd2, 0x5f, 0x05,
- 0xbf, 0xe4, 0x9f, 0x58, 0xff, 0x00, 0xd7, 0x49, 0x7f, 0xf4, 0x33, 0x5f,
- 0x36, 0xd7, 0xd3, 0x9f, 0x0a, 0x74, 0xe9, 0xb4, 0xbf, 0x01, 0xe9, 0x91,
- 0x4e, 0xa5, 0x24, 0x75, 0x69, 0x76, 0x9e, 0xa0, 0x33, 0x12, 0x3f, 0x42,
- 0x2b, 0xc3, 0xf0, 0xf6, 0x12, 0x96, 0x69, 0x52, 0x49, 0x68, 0xa0, 0xff,
- 0x00, 0x19, 0x44, 0x72, 0xd8, 0xeb, 0xe8, 0xa6, 0xe6, 0x8c, 0xd7, 0xf4,
- 0x3d, 0x8c, 0x87, 0x51, 0x4d, 0xcd, 0x19, 0xa2, 0xc0, 0x65, 0xb2, 0x90,
- 0xd7, 0x19, 0x04, 0x7f, 0xfb, 0x42, 0xab, 0xd6, 0xc5, 0xc4, 0x66, 0x68,
- 0x59, 0x07, 0x04, 0xfa, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
- 0x7a, 0xf6, 0xae, 0xa8, 0x4a, 0xe8, 0xe0, 0xa9, 0x07, 0x16, 0xac, 0x45,
- 0x53, 0x85, 0x24, 0x41, 0x80, 0x4f, 0xff, 0x00, 0xae, 0x9a, 0x6d, 0xd9,
- 0x66, 0x11, 0x64, 0x6e, 0xf5, 0xed, 0x5a, 0x76, 0xd1, 0x98, 0x21, 0x0a,
- 0x48, 0x27, 0xda, 0x89, 0x4a, 0xc8, 0x29, 0xc1, 0xc9, 0xb4, 0xc9, 0xab,
- 0x20, 0xa9, 0x02, 0x7c, 0x82, 0x3f, 0xfd, 0x75, 0xab, 0x9a, 0x8a, 0xe6,
- 0x33, 0x34, 0x45, 0x41, 0xc1, 0xf7, 0xac, 0x60, 0xf9, 0x4e, 0x9a, 0x91,
- 0xe6, 0x46, 0x45, 0x15, 0x28, 0xb7, 0x66, 0x98, 0xc5, 0x91, 0xb8, 0x77,
- 0xed, 0x47, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, 0x7a, 0xf6, 0xae, 0x9b,
- 0xa3, 0x87, 0x95, 0x8f, 0x55, 0x25, 0xad, 0xf0, 0x0f, 0xf9, 0x63, 0x5a,
- 0xd5, 0x0d, 0xba, 0x18, 0x61, 0x54, 0x24, 0x12, 0x3d, 0x2a, 0x4c, 0xd7,
- 0x34, 0xdf, 0x33, 0x3b, 0xe9, 0xc7, 0x95, 0x19, 0x6c, 0xa4, 0x35, 0xc6,
- 0x41, 0xff, 0x00, 0x2c, 0x2a, 0xbd, 0x6c, 0x5c, 0x46, 0x66, 0x85, 0x90,
- 0x1c, 0x13, 0xeb, 0x59, 0x9f, 0x67, 0x6f, 0x3f, 0xca, 0xc8, 0xdd, 0xfa,
- 0x56, 0xd0, 0x92, 0x68, 0xe5, 0xa9, 0x06, 0x9a, 0xb1, 0xa0, 0xd6, 0xc1,
- 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, 0x6a, 0x28, 0xae, 0x76, 0xdb, 0x3a,
- 0xd2, 0xb6, 0xc1, 0x45, 0x14, 0x52, 0x19, 0x0a, 0xdb, 0x05, 0xb8, 0x32,
- 0xe4, 0xe4, 0xf6, 0xa3, 0xec, 0xc3, 0xed, 0x1e, 0x6e, 0x4e, 0x7d, 0x2a,
- 0x6a, 0x2a, 0xb9, 0x99, 0x3c, 0xa8, 0x28, 0xa2, 0x8a, 0x92, 0x82, 0xa1,
- 0xfb, 0x30, 0xfb, 0x47, 0x9b, 0x93, 0x9f, 0x4a, 0x9a, 0x8a, 0x69, 0xd8,
- 0x4d, 0x5f, 0x72, 0x16, 0xb6, 0x0d, 0x70, 0x25, 0xc9, 0xc8, 0xed, 0x53,
- 0x51, 0x45, 0x0d, 0xb6, 0x09, 0x5b, 0x60, 0xa2, 0x8a, 0x29, 0x0c, 0x28,
- 0xa2, 0x8a, 0x00, 0x2b, 0x83, 0xf1, 0x57, 0xc1, 0xdd, 0x1b, 0xc4, 0xb7,
- 0xaf, 0x79, 0x1b, 0xcb, 0xa7, 0x5d, 0x48, 0x73, 0x21, 0x80, 0x02, 0x8e,
- 0x7d, 0x4a, 0x9e, 0xff, 0x00, 0x42, 0x2b, 0xbc, 0xa2, 0xbc, 0xfc, 0x76,
- 0x5f, 0x85, 0xcc, 0xa9, 0xfb, 0x1c, 0x5d, 0x35, 0x38, 0xf9, 0xfe, 0x8f,
- 0x75, 0xf2, 0x1a, 0x6d, 0x1c, 0x3f, 0x84, 0xbe, 0x11, 0xe8, 0xde, 0x16,
- 0xbb, 0x4b, 0xc2, 0xd2, 0x5f, 0xde, 0x27, 0x29, 0x24, 0xf8, 0xda, 0x87,
- 0xd5, 0x54, 0x77, 0xf7, 0x39, 0xae, 0xe2, 0x8a, 0x29, 0xe0, 0xb0, 0x18,
- 0x5c, 0xba, 0x97, 0xb1, 0xc2, 0x53, 0x50, 0x8f, 0x97, 0xeb, 0xd5, 0xfc,
- 0xc2, 0xed, 0x85, 0x71, 0xfe, 0x31, 0xf8, 0x5f, 0xa4, 0x78, 0xc6, 0x6f,
- 0xb4, 0xcd, 0xe6, 0x5a, 0x5e, 0xe3, 0x06, 0xe2, 0x0c, 0x65, 0xc7, 0x6d,
- 0xc0, 0xf5, 0xfe, 0x7e, 0xf5, 0xd8, 0x51, 0x55, 0x8c, 0xc1, 0x61, 0xf1,
- 0xf4, 0x9d, 0x0c, 0x54, 0x14, 0xe2, 0xfa, 0x3f, 0xeb, 0x46, 0x17, 0x68,
- 0xf3, 0xbf, 0x0f, 0x7c, 0x11, 0xd1, 0x74, 0x6b, 0xc4, 0xb9, 0xb9, 0x96,
- 0x5d, 0x49, 0xd0, 0xe5, 0x63, 0x94, 0x05, 0x8f, 0x3e, 0xa5, 0x47, 0x5f,
- 0xc4, 0xe3, 0xda, 0xbd, 0x13, 0xa5, 0x14, 0x56, 0x38, 0x1c, 0xb7, 0x07,
- 0x96, 0x41, 0xd3, 0xc1, 0xd3, 0x50, 0x4f, 0x7b, 0x75, 0xf5, 0x7b, 0xb0,
- 0x6d, 0xb0, 0xa2, 0x8a, 0x2b, 0xd3, 0x10, 0x51, 0x45, 0x14, 0x00, 0x54,
- 0x3f, 0x66, 0x1f, 0x68, 0xf3, 0x72, 0x73, 0xe9, 0x53, 0x51, 0x4d, 0x3b,
- 0x09, 0xab, 0xee, 0x42, 0xd6, 0xc1, 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa,
- 0x6a, 0x28, 0xa1, 0xb6, 0xc1, 0x2b, 0x6c, 0x14, 0x51, 0x45, 0x21, 0x90,
- 0xad, 0xb0, 0x5b, 0x83, 0x2e, 0x4e, 0x4f, 0x6a, 0x3e, 0xcc, 0x3e, 0xd1,
- 0xe6, 0xe4, 0xe7, 0xd2, 0xa6, 0xa2, 0xab, 0x99, 0x93, 0xca, 0x82, 0x8a,
- 0x28, 0xa9, 0x28, 0x2a, 0x1f, 0xb3, 0x0f, 0xb4, 0x79, 0xb9, 0x39, 0xf4,
- 0xa9, 0xa8, 0xa6, 0x9d, 0x84, 0xd5, 0xf7, 0x3e, 0x20, 0xff, 0x00, 0x87,
- 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3,
- 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a,
- 0xbe, 0x17, 0xfe, 0xc6, 0xb2, 0xff, 0x00, 0x9f, 0x64, 0xa7, 0x47, 0xa2,
- 0x59, 0x31, 0xe6, 0xd9, 0x31, 0x45, 0x8e, 0x9e, 0x44, 0x7d, 0xcd, 0xff,
- 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
- 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
- 0x15, 0x7c, 0x3b, 0xfd, 0x87, 0x61, 0xff, 0x00, 0x3e, 0xb1, 0xfe, 0x54,
- 0xd9, 0x34, 0x6b, 0x05, 0x1c, 0x5b, 0x47, 0x9f, 0xa5, 0x16, 0x0e, 0x44,
- 0x7d, 0xc9, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7,
- 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb,
- 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x2f, 0xfd, 0x8d, 0x65, 0xff, 0x00, 0x3e,
- 0xc9, 0xf9, 0x53, 0xe3, 0xd1, 0x2c, 0x4f, 0x26, 0xd9, 0x31, 0xf4, 0xa2,
- 0xc1, 0xc8, 0x8f, 0xb9, 0x7f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
- 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, 0xa6, 0xf8,
- 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, 0x87, 0x7f,
- 0xb0, 0xec, 0x3f, 0xe7, 0xd6, 0x3f, 0xca, 0x99, 0x26, 0x8d, 0x62, 0x38,
- 0x16, 0xc9, 0x9a, 0x2c, 0x1c, 0x88, 0xfb, 0x97, 0xfe, 0x1e, 0x9b, 0xe1,
- 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0x8f, 0xf8, 0x7a, 0x6f,
- 0x85, 0xbf, 0xe8, 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0xf8, 0x5f, 0xfb,
- 0x1a, 0xcb, 0xfe, 0x7d, 0x93, 0xf2, 0xa9, 0x23, 0xd1, 0x2c, 0x48, 0xc9,
- 0xb6, 0x4f, 0xca, 0x8b, 0x07, 0x22, 0x3e, 0xe4, 0xff, 0x00, 0x87, 0xa6,
- 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe,
- 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe,
- 0x1d, 0xfe, 0xc3, 0xb0, 0xff, 0x00, 0x9f, 0x58, 0xff, 0x00, 0x2a, 0x64,
- 0x9a, 0x35, 0x88, 0x38, 0x16, 0xc9, 0xf9, 0x51, 0x60, 0xe4, 0x47, 0xdc,
- 0xbf, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
- 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff,
- 0x00, 0x81, 0x51, 0x57, 0xc2, 0xff, 0x00, 0xd8, 0xd6, 0x5f, 0xf3, 0xec,
- 0x9f, 0x95, 0x48, 0x9a, 0x1d, 0x89, 0x19, 0x36, 0xc9, 0xf9, 0x51, 0x60,
- 0xe4, 0x47, 0xdc, 0x9f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac,
- 0x7f, 0xe0, 0x54, 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42,
- 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, 0x57, 0xc3, 0xbf, 0xd8, 0x76, 0x1f,
- 0xf3, 0xeb, 0x1f, 0xe5, 0x51, 0xbe, 0x8d, 0x63, 0x9c, 0x0b, 0x64, 0xfc,
- 0xa8, 0xb0, 0x72, 0x23, 0xee, 0x6f, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8,
- 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
- 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x7f, 0xec,
- 0x7b, 0x2f, 0xf9, 0xf6, 0x4f, 0xca, 0x9b, 0x2e, 0x9d, 0xa7, 0xdb, 0xed,
- 0x53, 0x67, 0xe6, 0xc8, 0xc0, 0x90, 0xb1, 0xae, 0x4e, 0x3d, 0x68, 0xb0,
- 0x72, 0x23, 0xee, 0xaf, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, 0x47, 0xd6,
- 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
- 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x1f, 0x23, 0x4b, 0xd8,
- 0x1b, 0xec, 0x44, 0x8d, 0xbb, 0x9b, 0x09, 0xf7, 0x06, 0x48, 0xc9, 0xe7,
- 0xd8, 0xfe, 0x54, 0x3d, 0x9e, 0x9e, 0x2e, 0x04, 0x66, 0xcb, 0x60, 0x27,
- 0x68, 0x72, 0xbf, 0x29, 0x3f, 0x5c, 0xd1, 0x60, 0xe4, 0x47, 0xdd, 0xdf,
- 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x54,
- 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00,
- 0x81, 0x51, 0x57, 0xc1, 0x82, 0xdf, 0x4e, 0x1b, 0xb7, 0xd9, 0x98, 0x88,
- 0x19, 0xda, 0xeb, 0xc9, 0xe7, 0x1c, 0x73, 0xea, 0x45, 0x4d, 0x1d, 0x9e,
- 0x9a, 0x46, 0x0d, 0x91, 0x12, 0x02, 0x41, 0x8c, 0xaf, 0xcd, 0x9c, 0x67,
- 0xd7, 0xd2, 0x8b, 0x07, 0x22, 0x3e, 0xed, 0xff, 0x00, 0x87, 0xa6, 0xf8,
- 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e,
- 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x16,
- 0xb7, 0xd3, 0xf4, 0xeb, 0x86, 0x65, 0xfb, 0x1f, 0x96, 0xea, 0x01, 0x2b,
- 0x22, 0xe0, 0xe0, 0xf7, 0xa7, 0x36, 0x8f, 0x62, 0x49, 0xc5, 0xb2, 0x62,
- 0x8b, 0x07, 0x22, 0x3e, 0xe7, 0xff, 0x00, 0x87, 0xa6, 0xf8, 0x5b, 0xfe,
- 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, 0x9b, 0xe1,
- 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x17, 0xfe, 0xc6,
- 0xb2, 0x3f, 0xf2, 0xec, 0x95, 0x30, 0xd0, 0xec, 0x40, 0xff, 0x00, 0x8f,
- 0x64, 0xa2, 0xc1, 0xc8, 0x8f, 0xb8, 0xbf, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
- 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87,
- 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf,
- 0x87, 0x7f, 0xb1, 0x2c, 0x07, 0xfc, 0xba, 0xc7, 0xf9, 0x54, 0x27, 0x47,
- 0xb2, 0x27, 0xfe, 0x3d, 0x92, 0x8b, 0x07, 0x22, 0x3e, 0xe8, 0xff, 0x00,
- 0x87, 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2,
- 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a,
- 0x8a, 0xbe, 0x17, 0x1a, 0x2d, 0x91, 0x38, 0xfb, 0x32, 0x54, 0xdf, 0xd8,
- 0x76, 0x1f, 0xf3, 0xea, 0x9f, 0x95, 0x16, 0x0e, 0x44, 0x7d, 0xc5, 0xff,
- 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
- 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
- 0x15, 0x7c, 0x3a, 0x74, 0x4b, 0x00, 0x33, 0xf6, 0x58, 0xff, 0x00, 0x2a,
- 0x84, 0xe8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x47,
- 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15,
- 0x1f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
- 0x55, 0xf0, 0xc2, 0xe8, 0xb6, 0x4c, 0x40, 0xfb, 0x32, 0x54, 0xbf, 0xd8,
- 0x76, 0x1f, 0xf3, 0xea, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x17, 0xfc, 0x3d,
- 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x1f, 0xf0,
- 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x55, 0xf0,
- 0xe3, 0x68, 0xb6, 0x0a, 0x09, 0xfb, 0x2c, 0x7f, 0x95, 0x45, 0xfd, 0x8d,
- 0x65, 0xff, 0x00, 0x3e, 0xc9, 0x45, 0x83, 0x91, 0x1f, 0x74, 0x7f, 0xc3,
- 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51,
- 0x51, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe,
- 0x05, 0x45, 0x5f, 0x0c, 0x26, 0x89, 0x64, 0xcd, 0xff, 0x00, 0x1e, 0xc9,
- 0x52, 0xff, 0x00, 0x61, 0xd8, 0x7f, 0xcf, 0xac, 0x7f, 0x95, 0x16, 0x0e,
- 0x44, 0x7d, 0xc5, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa,
- 0xc7, 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23,
- 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x38, 0xfa, 0x2d, 0x82, 0xaf, 0xfc,
- 0x7a, 0xc7, 0xf9, 0x54, 0x5f, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58,
- 0x39, 0x11, 0x72, 0xa7, 0x45, 0xda, 0xb8, 0xa8, 0xe3, 0x5c, 0xb6, 0x7d,
- 0x2a, 0x5a, 0xa2, 0xc2, 0xa1, 0x66, 0xdc, 0xd9, 0xa7, 0xc8, 0xd8, 0x18,
- 0xf5, 0xa8, 0xa8, 0x01, 0x40, 0xc9, 0xa9, 0xd4, 0x6d, 0x18, 0xa8, 0xe2,
- 0x5e, 0x73, 0x52, 0x50, 0x00, 0x4e, 0x05, 0x40, 0xc7, 0x71, 0xcd, 0x49,
- 0x2b, 0x71, 0x8a, 0x8a, 0x80, 0x14, 0x0c, 0x9c, 0x54, 0xe0, 0x60, 0x62,
- 0xa3, 0x89, 0x7b, 0xd4, 0x94, 0x00, 0x13, 0x80, 0x4d, 0x40, 0x4e, 0x4e,
- 0x69, 0xf2, 0xb7, 0x6a, 0x8e, 0x80, 0x15, 0x46, 0xe3, 0x8a, 0x9e, 0x99,
- 0x1a, 0xe0, 0x67, 0xd6, 0x9f, 0x40, 0x08, 0xc7, 0x68, 0xcd, 0x41, 0x4f,
- 0x91, 0xb2, 0x71, 0xe9, 0x4c, 0xa0, 0x07, 0x22, 0xee, 0x6f, 0x6a, 0x27,
- 0xb5, 0x59, 0xa4, 0x49, 0x37, 0xbc, 0x6e, 0xbc, 0x06, 0x43, 0x8c, 0x8f,
- 0x43, 0xed, 0x52, 0x46, 0xb8, 0x5f, 0x73, 0x4e, 0xa0, 0x0a, 0xa6, 0xca,
- 0x38, 0xa2, 0x95, 0x41, 0x6f, 0xde, 0x2e, 0xc3, 0xcf, 0x6c, 0x93, 0xff,
- 0x00, 0xb3, 0x1a, 0x87, 0xec, 0x60, 0xca, 0xae, 0xd2, 0x48, 0xe1, 0x4e,
- 0x42, 0x12, 0x36, 0x83, 0xf9, 0x55, 0xa9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d,
- 0x00, 0x57, 0x8b, 0x49, 0xb7, 0x0c, 0xfb, 0x53, 0xcb, 0x0c, 0x00, 0x21,
- 0x38, 0xe8, 0x72, 0x0f, 0xd6, 0xa6, 0x5d, 0x39, 0x17, 0x90, 0xf2, 0x79,
- 0x99, 0x2c, 0x64, 0x27, 0x2c, 0x4e, 0xdc, 0x7e, 0x82, 0xac, 0xa2, 0xed,
- 0x5a, 0x5a, 0x00, 0xad, 0x1d, 0xaa, 0xdb, 0x16, 0x6f, 0x31, 0xe5, 0x91,
- 0xc0, 0x05, 0xe4, 0x39, 0x38, 0x1d, 0xbf, 0x5a, 0x5a, 0x73, 0xb6, 0xe6,
- 0xa6, 0xf5, 0xa0, 0x07, 0xc4, 0xb9, 0x39, 0xf4, 0xa9, 0x69, 0x14, 0x6d,
- 0x18, 0xa5, 0xe9, 0x40, 0x0c, 0x95, 0xb0, 0x31, 0x51, 0x52, 0xb1, 0xdc,
- 0x49, 0xa0, 0x0c, 0x9c, 0x50, 0x03, 0xe2, 0x5e, 0xf5, 0x25, 0x00, 0x60,
- 0x62, 0x82, 0x70, 0x33, 0x40, 0x11, 0xca, 0xdd, 0xaa, 0x3a, 0x52, 0x72,
- 0x73, 0x42, 0x8d, 0xc4, 0x0a, 0x00, 0x92, 0x25, 0xc0, 0xcd, 0x3e, 0x8e,
- 0x94, 0x8c, 0x76, 0x82, 0x68, 0x02, 0x39, 0x5b, 0x27, 0x1e, 0x94, 0xca,
- 0x3a, 0xd3, 0x91, 0x77, 0x35, 0x00, 0x49, 0x1a, 0xe1, 0x7d, 0xcd, 0x3a,
- 0x8a, 0x47, 0x6d, 0xab, 0x40, 0x11, 0xc8, 0xd9, 0x6f, 0x61, 0x4c, 0xa2,
- 0x9d, 0x1a, 0xee, 0x6f, 0x61, 0x40, 0x1f, 0xff, 0xd9
-};
diff --git a/services/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp
deleted file mode 100644
index f3a6a67e..00000000
--- a/services/camera/libcameraservice/FakeCamera.cpp
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
-**
-** Copyright 2008, 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.
-*/
-
-#define LOG_TAG "FakeCamera"
-#include <utils/Log.h>
-
-#include <string.h>
-#include <stdlib.h>
-#include <utils/String8.h>
-
-#include "FakeCamera.h"
-
-
-namespace android {
-
-// TODO: All this rgb to yuv should probably be in a util class.
-
-// TODO: I think something is wrong in this class because the shadow is kBlue
-// and the square color should alternate between kRed and kGreen. However on the
-// emulator screen these are all shades of gray. Y seems ok but the U and V are
-// probably not.
-
-static int tables_initialized = 0;
-uint8_t *gYTable, *gCbTable, *gCrTable;
-
-static int
-clamp(int x)
-{
- if (x > 255) return 255;
- if (x < 0) return 0;
- return x;
-}
-
-/* the equation used by the video code to translate YUV to RGB looks like this
- *
- * Y = (Y0 - 16)*k0
- * Cb = Cb0 - 128
- * Cr = Cr0 - 128
- *
- * G = ( Y - k1*Cr - k2*Cb )
- * R = ( Y + k3*Cr )
- * B = ( Y + k4*Cb )
- *
- */
-
-static const double k0 = 1.164;
-static const double k1 = 0.813;
-static const double k2 = 0.391;
-static const double k3 = 1.596;
-static const double k4 = 2.018;
-
-/* let's try to extract the value of Y
- *
- * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 )
- *
- * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4)
- * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16
- *
- * let define:
- * kYr = k1/k3
- * kYb = k2/k4
- * kYy = k0 * ( 1 + kYr + kYb )
- *
- * we have:
- * Y = ( G + kYr*R + kYb*B )
- * Y0 = clamp[ Y/kYy + 16 ]
- */
-
-static const double kYr = k1/k3;
-static const double kYb = k2/k4;
-static const double kYy = k0*( 1. + kYr + kYb );
-
-static void
-initYtab( void )
-{
- const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 );
- int i;
-
- gYTable = (uint8_t *)malloc(imax);
-
- for(i=0; i<imax; i++) {
- int x = (int)(i/kYy + 16.5);
- if (x < 16) x = 16;
- else if (x > 235) x = 235;
- gYTable[i] = (uint8_t) x;
- }
-}
-
-/*
- * the source is RGB565, so adjust for 8-bit range of input values:
- *
- * G = (pixels >> 3) & 0xFC;
- * R = (pixels >> 8) & 0xF8;
- * B = (pixels & 0x1f) << 3;
- *
- * R2 = (pixels >> 11) R = R2*8
- * B2 = (pixels & 0x1f) B = B2*8
- *
- * kYr*R = kYr2*R2 => kYr2 = kYr*8
- * kYb*B = kYb2*B2 => kYb2 = kYb*8
- *
- * we want to use integer multiplications:
- *
- * SHIFT1 = 9
- *
- * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1)
- *
- * ALPHA = kYr*(1 << (SHIFT1+3))
- * BETA = kYb*(1 << (SHIFT1+3))
- */
-
-static const int SHIFT1 = 9;
-static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 );
-static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 );
-
-/*
- * now let's try to get the values of Cb and Cr
- *
- * R-B = (k3*Cr - k4*Cb)
- *
- * k3*Cr = k4*Cb + (R-B)
- * k4*Cb = k3*Cr - (R-B)
- *
- * R-G = (k1+k3)*Cr + k2*Cb
- * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0)
- * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B)
- *
- * kRr*Cr = (R-G) + kYb*(R-B)
- *
- * Cr = ((R-G) + kYb*(R-B))/kRr
- * Cr0 = clamp(Cr + 128)
- */
-
-static const double kRr = (k1 + k3 + k2*k3/k4);
-
-static void
-initCrtab( void )
-{
- uint8_t *pTable;
- int i;
-
- gCrTable = (uint8_t *)malloc(768*2);
-
- pTable = gCrTable + 384;
- for(i=-384; i<384; i++)
- pTable[i] = (uint8_t) clamp( i/kRr + 128.5 );
-}
-
-/*
- * B-G = (k2 + k4)*Cb + k1*Cr
- * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B))
- * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B)
- *
- * kBb*Cb = (B-G) - kYr*(R-B)
- *
- * Cb = ((B-G) - kYr*(R-B))/kBb
- * Cb0 = clamp(Cb + 128)
- *
- */
-
-static const double kBb = (k2 + k4 + k1*k4/k3);
-
-static void
-initCbtab( void )
-{
- uint8_t *pTable;
- int i;
-
- gCbTable = (uint8_t *)malloc(768*2);
-
- pTable = gCbTable + 384;
- for(i=-384; i<384; i++)
- pTable[i] = (uint8_t) clamp( i/kBb + 128.5 );
-}
-
-/*
- * SHIFT2 = 16
- *
- * DELTA = kYb*(1 << SHIFT2)
- * GAMMA = kYr*(1 << SHIFT2)
- */
-
-static const int SHIFT2 = 16;
-static const int DELTA = kYb*(1 << SHIFT2);
-static const int GAMMA = kYr*(1 << SHIFT2);
-
-int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420,
- uint32_t *param, uint8_t *table[])
-{
- uint16_t *inputRGB = (uint16_t*)rgb16;
- uint8_t *outYUV = yuv420;
- int32_t width_dst = param[0];
- int32_t height_dst = param[1];
- int32_t pitch_dst = param[2];
- int32_t mheight_dst = param[3];
- int32_t pitch_src = param[4];
- uint8_t *y_tab = table[0];
- uint8_t *cb_tab = table[1];
- uint8_t *cr_tab = table[2];
-
- int32_t size16 = pitch_dst*mheight_dst;
- int32_t i,j,count;
- int32_t ilimit,jlimit;
- uint8_t *tempY,*tempU,*tempV;
- uint16_t pixels;
- int tmp;
-uint32_t temp;
-
- tempY = outYUV;
- tempU = outYUV + (height_dst * pitch_dst);
- tempV = tempU + 1;
-
- jlimit = height_dst;
- ilimit = width_dst;
-
- for(j=0; j<jlimit; j+=1)
- {
- for (i=0; i<ilimit; i+=2)
- {
- int32_t G_ds = 0, B_ds = 0, R_ds = 0;
- uint8_t y0, y1, u, v;
-
- pixels = inputRGB[i];
- temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
- y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
-
- G_ds += (pixels>>1) & 0x03E0;
- B_ds += (pixels<<5) & 0x03E0;
- R_ds += (pixels>>6) & 0x03E0;
-
- pixels = inputRGB[i+1];
- temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
- y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
-
- G_ds += (pixels>>1) & 0x03E0;
- B_ds += (pixels<<5) & 0x03E0;
- R_ds += (pixels>>6) & 0x03E0;
-
- R_ds >>= 1;
- B_ds >>= 1;
- G_ds >>= 1;
-
- tmp = R_ds - B_ds;
-
- u = cb_tab[(((B_ds-G_ds)<<SHIFT2) - GAMMA*tmp)>>(SHIFT2+2)];
- v = cr_tab[(((R_ds-G_ds)<<SHIFT2) + DELTA*tmp)>>(SHIFT2+2)];
-
- tempY[0] = y0;
- tempY[1] = y1;
- tempY += 2;
-
- if ((j&1) == 0) {
- tempU[0] = u;
- tempV[0] = v;
- tempU += 2;
- tempV += 2;
- }
- }
-
- inputRGB += pitch_src;
- }
-
- return 1;
-}
-
-#define min(a,b) ((a)<(b)?(a):(b))
-#define max(a,b) ((a)>(b)?(a):(b))
-
-static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height)
-{
- if (!tables_initialized) {
- initYtab();
- initCrtab();
- initCbtab();
- tables_initialized = 1;
- }
-
- uint32_t param[6];
- param[0] = (uint32_t) width;
- param[1] = (uint32_t) height;
- param[2] = (uint32_t) width;
- param[3] = (uint32_t) height;
- param[4] = (uint32_t) width;
- param[5] = (uint32_t) 0;
-
- uint8_t *table[3];
- table[0] = gYTable;
- table[1] = gCbTable + 384;
- table[2] = gCrTable + 384;
-
- ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table);
-}
-
-const int FakeCamera::kRed;
-const int FakeCamera::kGreen;
-const int FakeCamera::kBlue;
-
-FakeCamera::FakeCamera(int width, int height)
- : mTmpRgb16Buffer(0)
-{
- setSize(width, height);
-}
-
-FakeCamera::~FakeCamera()
-{
- delete[] mTmpRgb16Buffer;
-}
-
-void FakeCamera::setSize(int width, int height)
-{
- mWidth = width;
- mHeight = height;
- mCounter = 0;
- mCheckX = 0;
- mCheckY = 0;
-
- // This will cause it to be reallocated on the next call
- // to getNextFrameAsYuv420().
- delete[] mTmpRgb16Buffer;
- mTmpRgb16Buffer = 0;
-}
-
-void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer)
-{
- int size = mWidth / 10;
-
- drawCheckerboard(buffer, size);
-
- int x = ((mCounter*3)&255);
- if(x>128) x = 255 - x;
- int y = ((mCounter*5)&255);
- if(y>128) y = 255 - y;
-
- drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue);
-
- mCounter++;
-}
-
-void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer)
-{
- if (mTmpRgb16Buffer == 0)
- mTmpRgb16Buffer = new uint16_t[mWidth * mHeight];
-
- getNextFrameAsRgb565(mTmpRgb16Buffer);
- convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
-}
-
-void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow)
-{
- int square_xstop, square_ystop, shadow_xstop, shadow_ystop;
-
- square_xstop = min(mWidth, x+size);
- square_ystop = min(mHeight, y+size);
- shadow_xstop = min(mWidth, x+size+(size/4));
- shadow_ystop = min(mHeight, y+size+(size/4));
-
- // Do the shadow.
- uint16_t *sh = &dst[(y+(size/4))*mWidth];
- for (int j = y + (size/4); j < shadow_ystop; j++) {
- for (int i = x + (size/4); i < shadow_xstop; i++) {
- sh[i] &= shadow;
- }
- sh += mWidth;
- }
-
- // Draw the square.
- uint16_t *sq = &dst[y*mWidth];
- for (int j = y; j < square_ystop; j++) {
- for (int i = x; i < square_xstop; i++) {
- sq[i] = color;
- }
- sq += mWidth;
- }
-}
-
-void FakeCamera::drawCheckerboard(uint16_t *dst, int size)
-{
- bool black = true;
-
- if((mCheckX/size)&1)
- black = false;
- if((mCheckY/size)&1)
- black = !black;
-
- int county = mCheckY%size;
- int checkxremainder = mCheckX%size;
-
- for(int y=0;y<mHeight;y++) {
- int countx = checkxremainder;
- bool current = black;
- for(int x=0;x<mWidth;x++) {
- dst[y*mWidth+x] = current?0:0xffff;
- if(countx++ >= size) {
- countx=0;
- current = !current;
- }
- }
- if(county++ >= size) {
- county=0;
- black = !black;
- }
- }
- mCheckX += 3;
- mCheckY++;
-}
-
-
-void FakeCamera::dump(int fd) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
-}
-
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h
deleted file mode 100644
index 724de207..00000000
--- a/services/camera/libcameraservice/FakeCamera.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-**
-** Copyright 2008, 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 ANDROID_HARDWARE_FAKECAMERA_H
-#define ANDROID_HARDWARE_FAKECAMERA_H
-
-#include <sys/types.h>
-#include <stdint.h>
-
-namespace android {
-
-/*
- * FakeCamera is used in the CameraHardwareStub to provide a fake video feed
- * when the system does not have a camera in hardware.
- * The fake video is a moving black and white checkerboard background with a
- * bouncing gray square in the foreground.
- * This class is not thread-safe.
- *
- * TODO: Since the major methods provides a raw/uncompressed video feed, rename
- * this class to RawVideoSource.
- */
-
-class FakeCamera {
-public:
- FakeCamera(int width, int height);
- ~FakeCamera();
-
- void setSize(int width, int height);
- void getNextFrameAsYuv420(uint8_t *buffer);
- // Write to the fd a string representing the current state.
- void dump(int fd) const;
-
-private:
- // TODO: remove the uint16_t buffer param everywhere since it is a field of
- // this class.
- void getNextFrameAsRgb565(uint16_t *buffer);
-
- void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow);
- void drawCheckerboard(uint16_t *buffer, int size);
-
- static const int kRed = 0xf800;
- static const int kGreen = 0x07c0;
- static const int kBlue = 0x003e;
-
- int mWidth, mHeight;
- int mCounter;
- int mCheckX, mCheckY;
- uint16_t *mTmpRgb16Buffer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_HARDWARE_FAKECAMERA_H
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.cpp b/services/camera/libcameraservice/camera2/BurstCapture.cpp
new file mode 100644
index 00000000..f56c50cf
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/BurstCapture.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Camera2-BurstCapture"
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "BurstCapture.h"
+
+#include "../Camera2Client.h"
+#include "JpegCompressor.h"
+
+namespace android {
+namespace camera2 {
+
+BurstCapture::BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer):
+ mCaptureStreamId(NO_STREAM),
+ mClient(client),
+ mSequencer(sequencer)
+{
+}
+
+BurstCapture::~BurstCapture() {
+}
+
+status_t BurstCapture::start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId) {
+ ALOGE("Not completely implemented");
+ return INVALID_OPERATION;
+}
+
+void BurstCapture::onFrameAvailable() {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock l(mInputMutex);
+ if(!mInputChanged) {
+ mInputChanged = true;
+ mInputSignal.signal();
+ }
+}
+
+bool BurstCapture::threadLoop() {
+ status_t res;
+ {
+ Mutex::Autolock l(mInputMutex);
+ while(!mInputChanged) {
+ res = mInputSignal.waitRelative(mInputMutex, kWaitDuration);
+ if(res == TIMED_OUT) return true;
+ }
+ mInputChanged = false;
+ }
+
+ do {
+ sp<Camera2Client> client = mClient.promote();
+ if(client == 0) return false;
+ ALOGV("%s: Calling processFrameAvailable()", __FUNCTION__);
+ res = processFrameAvailable(client);
+ } while(res == OK);
+
+ return true;
+}
+
+CpuConsumer::LockedBuffer* BurstCapture::jpegEncode(
+ CpuConsumer::LockedBuffer *imgBuffer,
+ int quality)
+{
+ ALOGV("%s", __FUNCTION__);
+
+ CpuConsumer::LockedBuffer *imgEncoded = new CpuConsumer::LockedBuffer;
+ uint8_t *data = new uint8_t[ANDROID_JPEG_MAX_SIZE];
+ imgEncoded->data = data;
+ imgEncoded->width = imgBuffer->width;
+ imgEncoded->height = imgBuffer->height;
+ imgEncoded->stride = imgBuffer->stride;
+
+ Vector<CpuConsumer::LockedBuffer*> buffers;
+ buffers.push_back(imgBuffer);
+ buffers.push_back(imgEncoded);
+
+ sp<JpegCompressor> jpeg = new JpegCompressor();
+ status_t res = jpeg->start(buffers, 1);
+
+ bool success = jpeg->waitForDone(10 * 1e9);
+ if(success) {
+ return buffers[1];
+ }
+ else {
+ ALOGE("%s: JPEG encode timed out", __FUNCTION__);
+ return NULL; // TODO: maybe change function return value to status_t
+ }
+}
+
+status_t BurstCapture::processFrameAvailable(sp<Camera2Client> &client) {
+ ALOGE("Not implemented");
+ return INVALID_OPERATION;
+}
+
+} // namespace camera2
+} // namespace android
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/camera2/BurstCapture.h
new file mode 100644
index 00000000..dfc45eb3
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/BurstCapture.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
+#define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
+
+#include "camera2/CameraMetadata.h"
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <gui/CpuConsumer.h>
+#include "Camera2Device.h"
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+class CaptureSequencer;
+
+class BurstCapture : public virtual Thread,
+ public virtual CpuConsumer::FrameAvailableListener
+{
+public:
+ BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
+ virtual ~BurstCapture();
+
+ virtual void onFrameAvailable();
+ virtual status_t start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId);
+
+protected:
+ Mutex mInputMutex;
+ bool mInputChanged;
+ Condition mInputSignal;
+ int mCaptureStreamId;
+ wp<Camera2Client> mClient;
+ wp<CaptureSequencer> mSequencer;
+
+ // Should only be accessed by processing thread
+ enum {
+ NO_STREAM = -1
+ };
+
+ CpuConsumer::LockedBuffer* jpegEncode(
+ CpuConsumer::LockedBuffer *imgBuffer,
+ int quality);
+
+ virtual status_t processFrameAvailable(sp<Camera2Client> &client);
+
+private:
+ virtual bool threadLoop();
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+};
+
+} // namespace camera2
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
new file mode 100644
index 00000000..3e9c2555
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-CallbackProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "CallbackProcessor.h"
+#include <gui/SurfaceTextureClient.h>
+#include "../Camera2Device.h"
+#include "../Camera2Client.h"
+
+
+namespace android {
+namespace camera2 {
+
+CallbackProcessor::CallbackProcessor(wp<Camera2Client> client):
+ Thread(false),
+ mClient(client),
+ mCallbackAvailable(false),
+ mCallbackStreamId(NO_STREAM) {
+}
+
+CallbackProcessor::~CallbackProcessor() {
+ ALOGV("%s: Exit", __FUNCTION__);
+ deleteStream();
+}
+
+void CallbackProcessor::onFrameAvailable() {
+ Mutex::Autolock l(mInputMutex);
+ if (!mCallbackAvailable) {
+ mCallbackAvailable = true;
+ mCallbackAvailableSignal.signal();
+ }
+}
+
+status_t CallbackProcessor::updateStream(const Parameters &params) {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ if (mCallbackConsumer == 0) {
+ // Create CPU buffer queue endpoint
+ mCallbackConsumer = new CpuConsumer(kCallbackHeapCount);
+ mCallbackConsumer->setFrameAvailableListener(this);
+ mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer"));
+ mCallbackWindow = new SurfaceTextureClient(
+ mCallbackConsumer->getProducerInterface());
+ }
+
+ if (mCallbackStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight, currentFormat;
+ res = device->getStreamInfo(mCallbackStreamId,
+ &currentWidth, &currentHeight, &currentFormat);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying callback output stream info: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)params.previewWidth ||
+ currentHeight != (uint32_t)params.previewHeight ||
+ currentFormat != (uint32_t)params.previewFormat) {
+ // Since size should only change while preview is not running,
+ // assuming that all existing use of old callback stream is
+ // completed.
+ ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+ __FUNCTION__, client->getCameraId(), mCallbackStreamId);
+ res = device->deleteStream(mCallbackStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for callbacks: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ mCallbackStreamId = NO_STREAM;
+ }
+ }
+
+ if (mCallbackStreamId == NO_STREAM) {
+ ALOGV("Creating callback stream: %d %d format 0x%x",
+ params.previewWidth, params.previewHeight,
+ params.previewFormat);
+ res = device->createStream(mCallbackWindow,
+ params.previewWidth, params.previewHeight,
+ params.previewFormat, 0, &mCallbackStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ return OK;
+}
+
+status_t CallbackProcessor::deleteStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ if (mCallbackStreamId != NO_STREAM) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ device->deleteStream(mCallbackStreamId);
+
+ mCallbackHeap.clear();
+ mCallbackWindow.clear();
+ mCallbackConsumer.clear();
+
+ mCallbackStreamId = NO_STREAM;
+ }
+ return OK;
+}
+
+int CallbackProcessor::getStreamId() const {
+ Mutex::Autolock l(mInputMutex);
+ return mCallbackStreamId;
+}
+
+void CallbackProcessor::dump(int fd, const Vector<String16>& args) const {
+}
+
+bool CallbackProcessor::threadLoop() {
+ status_t res;
+
+ {
+ Mutex::Autolock l(mInputMutex);
+ while (!mCallbackAvailable) {
+ res = mCallbackAvailableSignal.waitRelative(mInputMutex,
+ kWaitDuration);
+ if (res == TIMED_OUT) return true;
+ }
+ mCallbackAvailable = false;
+ }
+
+ do {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ res = processNewCallback(client);
+ } while (res == OK);
+
+ return true;
+}
+
+status_t CallbackProcessor::processNewCallback(sp<Camera2Client> &client) {
+ ATRACE_CALL();
+ status_t res;
+
+ int callbackHeapId;
+ sp<Camera2Heap> callbackHeap;
+ size_t heapIdx;
+
+ CpuConsumer::LockedBuffer imgBuffer;
+ ALOGV("%s: Getting buffer", __FUNCTION__);
+ res = mCallbackConsumer->lockNextBuffer(&imgBuffer);
+ if (res != OK) {
+ if (res != BAD_VALUE) {
+ ALOGE("%s: Camera %d: Error receiving next callback buffer: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ }
+ return res;
+ }
+ ALOGV("%s: Camera %d: Preview callback available", __FUNCTION__,
+ client->getCameraId());
+
+ {
+ SharedParameters::Lock l(client->getParameters());
+
+ if ( l.mParameters.state != Parameters::PREVIEW
+ && l.mParameters.state != Parameters::RECORD
+ && l.mParameters.state != Parameters::VIDEO_SNAPSHOT) {
+ ALOGV("%s: Camera %d: No longer streaming",
+ __FUNCTION__, client->getCameraId());
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
+ if (! (l.mParameters.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ) {
+ ALOGV("%s: No longer enabled, dropping", __FUNCTION__);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+ if ((l.mParameters.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) &&
+ !l.mParameters.previewCallbackOneShot) {
+ ALOGV("%s: One shot mode, already sent, dropping", __FUNCTION__);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
+ if (imgBuffer.format != l.mParameters.previewFormat) {
+ ALOGE("%s: Camera %d: Unexpected format for callback: "
+ "%x, expected %x", __FUNCTION__, client->getCameraId(),
+ imgBuffer.format, l.mParameters.previewFormat);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return INVALID_OPERATION;
+ }
+
+ // In one-shot mode, stop sending callbacks after the first one
+ if (l.mParameters.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) {
+ ALOGV("%s: clearing oneshot", __FUNCTION__);
+ l.mParameters.previewCallbackOneShot = false;
+ }
+ }
+
+ size_t bufferSize = Camera2Client::calculateBufferSize(
+ imgBuffer.width, imgBuffer.height,
+ imgBuffer.format, imgBuffer.stride);
+ size_t currentBufferSize = (mCallbackHeap == 0) ?
+ 0 : (mCallbackHeap->mHeap->getSize() / kCallbackHeapCount);
+ if (bufferSize != currentBufferSize) {
+ mCallbackHeap.clear();
+ mCallbackHeap = new Camera2Heap(bufferSize, kCallbackHeapCount,
+ "Camera2Client::CallbackHeap");
+ if (mCallbackHeap->mHeap->getSize() == 0) {
+ ALOGE("%s: Camera %d: Unable to allocate memory for callbacks",
+ __FUNCTION__, client->getCameraId());
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return INVALID_OPERATION;
+ }
+
+ mCallbackHeapHead = 0;
+ mCallbackHeapFree = kCallbackHeapCount;
+ }
+
+ if (mCallbackHeapFree == 0) {
+ ALOGE("%s: Camera %d: No free callback buffers, dropping frame",
+ __FUNCTION__, client->getCameraId());
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
+ heapIdx = mCallbackHeapHead;
+
+ mCallbackHeapHead = (mCallbackHeapHead + 1) & kCallbackHeapCount;
+ mCallbackHeapFree--;
+
+ // TODO: Get rid of this memcpy by passing the gralloc queue all the way
+ // to app
+
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap =
+ mCallbackHeap->mBuffers[heapIdx]->getMemory(&offset,
+ &size);
+ uint8_t *data = (uint8_t*)heap->getBase() + offset;
+ memcpy(data, imgBuffer.data, bufferSize);
+
+ ALOGV("%s: Freeing buffer", __FUNCTION__);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+
+ // Call outside parameter lock to allow re-entrancy from notification
+ {
+ Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+ if (l.mCameraClient != 0) {
+ ALOGV("%s: Camera %d: Invoking client data callback",
+ __FUNCTION__, client->getCameraId());
+ l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME,
+ mCallbackHeap->mBuffers[heapIdx], NULL);
+ }
+ }
+
+ // Only increment free if we're still using the same heap
+ mCallbackHeapFree++;
+
+ ALOGV("%s: exit", __FUNCTION__);
+
+ return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/camera2/CallbackProcessor.h
new file mode 100644
index 00000000..c2a13724
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+#include <gui/CpuConsumer.h>
+#include "Parameters.h"
+#include "CameraMetadata.h"
+#include "Camera2Heap.h"
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+/***
+ * Still image capture output image processing
+ */
+class CallbackProcessor:
+ public Thread, public CpuConsumer::FrameAvailableListener {
+ public:
+ CallbackProcessor(wp<Camera2Client> client);
+ ~CallbackProcessor();
+
+ void onFrameAvailable();
+
+ status_t updateStream(const Parameters &params);
+ status_t deleteStream();
+ int getStreamId() const;
+
+ void dump(int fd, const Vector<String16>& args) const;
+ private:
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ wp<Camera2Client> mClient;
+
+ mutable Mutex mInputMutex;
+ bool mCallbackAvailable;
+ Condition mCallbackAvailableSignal;
+
+ enum {
+ NO_STREAM = -1
+ };
+
+ int mCallbackStreamId;
+ static const size_t kCallbackHeapCount = 6;
+ sp<CpuConsumer> mCallbackConsumer;
+ sp<ANativeWindow> mCallbackWindow;
+ sp<Camera2Heap> mCallbackHeap;
+ int mCallbackHeapId;
+ size_t mCallbackHeapHead, mCallbackHeapFree;
+
+ virtual bool threadLoop();
+
+ status_t processNewCallback(sp<Camera2Client> &client);
+
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/Camera2Heap.h b/services/camera/libcameraservice/camera2/Camera2Heap.h
new file mode 100644
index 00000000..9c72d762
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/Camera2Heap.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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 ANDROiD_SERVERS_CAMERA_CAMERA2HEAP_H
+#define ANDROiD_SERVERS_CAMERA_CAMERA2HEAP_H
+
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+
+namespace android {
+namespace camera2 {
+
+// Utility class for managing a set of IMemory blocks
+class Camera2Heap : public RefBase {
+ public:
+ Camera2Heap(size_t buf_size, uint_t num_buffers = 1,
+ const char *name = NULL) :
+ mBufSize(buf_size),
+ mNumBufs(num_buffers) {
+ mHeap = new MemoryHeapBase(buf_size * num_buffers, 0, name);
+ mBuffers = new sp<MemoryBase>[mNumBufs];
+ for (uint_t i = 0; i < mNumBufs; i++)
+ mBuffers[i] = new MemoryBase(mHeap,
+ i * mBufSize,
+ mBufSize);
+ }
+
+ virtual ~Camera2Heap()
+ {
+ delete [] mBuffers;
+ }
+
+ size_t mBufSize;
+ uint_t mNumBufs;
+ sp<MemoryHeapBase> mHeap;
+ sp<MemoryBase> *mBuffers;
+};
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.cpp b/services/camera/libcameraservice/camera2/CameraMetadata.cpp
new file mode 100644
index 00000000..835587db
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CameraMetadata.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-Metadata"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+
+#include "CameraMetadata.h"
+
+namespace android {
+
+namespace camera2 {
+CameraMetadata::CameraMetadata() :
+ mBuffer(NULL) {
+}
+
+CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity)
+{
+ mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
+}
+
+CameraMetadata::CameraMetadata(const CameraMetadata &other) {
+ mBuffer = clone_camera_metadata(other.mBuffer);
+}
+
+CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) {
+ return operator=(other.mBuffer);
+}
+
+CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) {
+ if (CC_LIKELY(buffer != mBuffer)) {
+ camera_metadata_t *newBuffer = clone_camera_metadata(buffer);
+ clear();
+ mBuffer = newBuffer;
+ }
+ return *this;
+}
+
+CameraMetadata::~CameraMetadata() {
+ clear();
+}
+
+camera_metadata_t* CameraMetadata::release() {
+ camera_metadata_t *released = mBuffer;
+ mBuffer = NULL;
+ return released;
+}
+
+void CameraMetadata::clear() {
+ if (mBuffer) {
+ free_camera_metadata(mBuffer);
+ mBuffer = NULL;
+ }
+}
+
+void CameraMetadata::acquire(camera_metadata_t *buffer) {
+ clear();
+ mBuffer = buffer;
+}
+
+void CameraMetadata::acquire(CameraMetadata &other) {
+ acquire(other.release());
+}
+
+status_t CameraMetadata::append(const CameraMetadata &other) {
+ return append_camera_metadata(mBuffer, other.mBuffer);
+}
+
+size_t CameraMetadata::entryCount() const {
+ return (mBuffer == NULL) ? 0 :
+ get_camera_metadata_entry_count(mBuffer);
+}
+
+bool CameraMetadata::isEmpty() const {
+ return entryCount() == 0;
+}
+
+status_t CameraMetadata::sort() {
+ return sort_camera_metadata(mBuffer);
+}
+
+status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) {
+ int tagType = get_camera_metadata_tag_type(tag);
+ if ( CC_UNLIKELY(tagType == -1)) {
+ ALOGE("Update metadata entry: Unknown tag %d", tag);
+ return INVALID_OPERATION;
+ }
+ if ( CC_UNLIKELY(tagType != expectedType) ) {
+ ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; "
+ "got type %s data instead ",
+ get_camera_metadata_tag_name(tag), tag,
+ camera_metadata_type_names[tagType],
+ camera_metadata_type_names[expectedType]);
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const int32_t *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_INT32)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const uint8_t *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const float *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_FLOAT)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const int64_t *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_INT64)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const double *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const camera_metadata_rational_t *data, size_t data_count) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)data, data_count);
+}
+
+status_t CameraMetadata::update(uint32_t tag,
+ const String8 &string) {
+ status_t res;
+ if ( (res = checkType(tag, TYPE_BYTE)) != OK) {
+ return res;
+ }
+ return update(tag, (const void*)string.string(), string.size());
+}
+
+status_t CameraMetadata::update(uint32_t tag, const void *data,
+ size_t data_count) {
+ status_t res;
+ int type = get_camera_metadata_tag_type(tag);
+ if (type == -1) {
+ ALOGE("%s: Tag %d not found", __FUNCTION__, tag);
+ return BAD_VALUE;
+ }
+ size_t data_size = calculate_camera_metadata_entry_data_size(type,
+ data_count);
+
+ res = resizeIfNeeded(1, data_size);
+
+ if (res == OK) {
+ camera_metadata_entry_t entry;
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (res == NAME_NOT_FOUND) {
+ res = add_camera_metadata_entry(mBuffer,
+ tag, data, data_count);
+ } else if (res == OK) {
+ res = update_camera_metadata_entry(mBuffer,
+ entry.index, data, data_count, NULL);
+ }
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)",
+ __FUNCTION__, get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ }
+ return res;
+}
+
+camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
+ status_t res;
+ camera_metadata_entry entry;
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (CC_UNLIKELY( res != OK )) {
+ entry.count = 0;
+ entry.data.u8 = NULL;
+ }
+ return entry;
+}
+
+camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const {
+ status_t res;
+ camera_metadata_ro_entry entry;
+ res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
+ if (CC_UNLIKELY( res != OK )) {
+ entry.count = 0;
+ entry.data.u8 = NULL;
+ }
+ return entry;
+}
+
+status_t CameraMetadata::erase(uint32_t tag) {
+ camera_metadata_entry_t entry;
+ status_t res;
+ res = find_camera_metadata_entry(mBuffer, tag, &entry);
+ if (res == NAME_NOT_FOUND) {
+ return OK;
+ } else if (res != OK) {
+ ALOGE("%s: Error looking for entry %s.%s (%x): %s %d",
+ __FUNCTION__,
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ return res;
+ }
+ res = delete_camera_metadata_entry(mBuffer, entry.index);
+ if (res != OK) {
+ ALOGE("%s: Error deleting entry %s.%s (%x): %s %d",
+ __FUNCTION__,
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag), tag, strerror(-res), res);
+ }
+ return res;
+}
+
+void CameraMetadata::dump(int fd, int verbosity, int indentation) const {
+ dump_indented_camera_metadata(mBuffer, fd, verbosity, indentation);
+}
+
+status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) {
+ if (mBuffer == NULL) {
+ mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2);
+ if (mBuffer == NULL) {
+ ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ } else {
+ size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer);
+ size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer);
+ size_t newEntryCount = currentEntryCount +
+ extraEntries;
+ newEntryCount = (newEntryCount > currentEntryCap) ?
+ newEntryCount * 2 : currentEntryCap;
+
+ size_t currentDataCount = get_camera_metadata_data_count(mBuffer);
+ size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer);
+ size_t newDataCount = currentDataCount +
+ extraData;
+ newDataCount = (newDataCount > currentDataCap) ?
+ newDataCount * 2 : currentDataCap;
+
+ if (newEntryCount > currentEntryCap ||
+ newDataCount > currentDataCap) {
+ camera_metadata_t *oldBuffer = mBuffer;
+ mBuffer = allocate_camera_metadata(newEntryCount,
+ newDataCount);
+ if (mBuffer == NULL) {
+ ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ append_camera_metadata(mBuffer, oldBuffer);
+ free_camera_metadata(oldBuffer);
+ }
+ }
+ return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.h b/services/camera/libcameraservice/camera2/CameraMetadata.h
new file mode 100644
index 00000000..aee6cd78
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CameraMetadata.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
+#define ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
+
+#include "system/camera_metadata.h"
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace camera2 {
+
+/**
+ * A convenience wrapper around the C-based camera_metadata_t library.
+ */
+class CameraMetadata {
+ public:
+ /** Creates an empty object; best used when expecting to acquire contents
+ * from elsewhere */
+ CameraMetadata();
+ /** Creates an object with space for entryCapacity entries, with
+ * dataCapacity extra storage */
+ CameraMetadata(size_t entryCapacity, size_t dataCapacity = 10);
+
+ ~CameraMetadata();
+
+ /** Takes ownership of passed-in buffer */
+ CameraMetadata(camera_metadata_t *buffer);
+ /** Clones the metadata */
+ CameraMetadata(const CameraMetadata &other);
+
+ /**
+ * Assignment clones metadata buffer.
+ */
+ CameraMetadata &operator=(const CameraMetadata &other);
+ CameraMetadata &operator=(const camera_metadata_t *buffer);
+
+ /**
+ * Release a raw metadata buffer to the caller. After this call,
+ * CameraMetadata no longer references the buffer, and the caller takes
+ * responsibility for freeing the raw metadata buffer (using
+ * free_camera_metadata()), or for handing it to another CameraMetadata
+ * instance.
+ */
+ camera_metadata_t* release();
+
+ /**
+ * Clear the metadata buffer and free all storage used by it
+ */
+ void clear();
+
+ /**
+ * Acquire a raw metadata buffer from the caller. After this call,
+ * the caller no longer owns the raw buffer, and must not free or manipulate it.
+ * If CameraMetadata already contains metadata, it is freed.
+ */
+ void acquire(camera_metadata_t* buffer);
+
+ /**
+ * Acquires raw buffer from other CameraMetadata object. After the call, the argument
+ * object no longer has any metadata.
+ */
+ void acquire(CameraMetadata &other);
+
+ /**
+ * Append metadata from another CameraMetadata object.
+ */
+ status_t append(const CameraMetadata &other);
+
+ /**
+ * Number of metadata entries.
+ */
+ size_t entryCount() const;
+
+ /**
+ * Is the buffer empty (no entires)
+ */
+ bool isEmpty() const;
+
+ /**
+ * Sort metadata buffer for faster find
+ */
+ status_t sort();
+
+ /**
+ * Update metadata entry. Will create entry if it doesn't exist already, and
+ * will reallocate the buffer if insufficient space exists. Overloaded for
+ * the various types of valid data.
+ */
+ status_t update(uint32_t tag,
+ const uint8_t *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const int32_t *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const float *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const int64_t *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const double *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const camera_metadata_rational_t *data, size_t data_count);
+ status_t update(uint32_t tag,
+ const String8 &string);
+
+ template<typename T>
+ status_t update(uint32_t tag, Vector<T> data) {
+ return update(tag, data.array(), data.size());
+ }
+
+ /**
+ * Get metadata entry by tag id
+ */
+ camera_metadata_entry find(uint32_t tag);
+
+ /**
+ * Get metadata entry by tag id, with no editing
+ */
+ camera_metadata_ro_entry find(uint32_t tag) const;
+
+ /**
+ * Delete metadata entry by tag
+ */
+ status_t erase(uint32_t tag);
+
+ /**
+ * Dump contents into FD for debugging. The verbosity levels are
+ * 0: Tag entry information only, no data values
+ * 1: Level 0 plus at most 16 data values per entry
+ * 2: All information
+ *
+ * The indentation parameter sets the number of spaces to add to the start
+ * each line of output.
+ */
+ void dump(int fd, int verbosity = 1, int indentation = 0) const;
+
+ private:
+ camera_metadata_t *mBuffer;
+
+ /**
+ * Check if tag has a given type
+ */
+ status_t checkType(uint32_t tag, uint8_t expectedType);
+
+ /**
+ * Base update entry method
+ */
+ status_t update(uint32_t tag, const void *data, size_t data_count);
+
+ /**
+ * Resize metadata buffer if needed by reallocating it and copying it over.
+ */
+ status_t resizeIfNeeded(size_t extraEntries, size_t extraData);
+
+};
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
new file mode 100644
index 00000000..072453b0
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
@@ -0,0 +1,673 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-CaptureSequencer"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "CaptureSequencer.h"
+#include "BurstCapture.h"
+#include "../Camera2Device.h"
+#include "../Camera2Client.h"
+#include "Parameters.h"
+
+namespace android {
+namespace camera2 {
+
+/** Public members */
+
+CaptureSequencer::CaptureSequencer(wp<Camera2Client> client):
+ Thread(false),
+ mStartCapture(false),
+ mBusy(false),
+ mNewAEState(false),
+ mNewFrameReceived(false),
+ mNewCaptureReceived(false),
+ mShutterNotified(false),
+ mClient(client),
+ mCaptureState(IDLE),
+ mTriggerId(0),
+ mTimeoutCount(0),
+ mCaptureId(Camera2Client::kCaptureRequestIdStart),
+ mMsgType(0) {
+ ALOGV("%s", __FUNCTION__);
+}
+
+CaptureSequencer::~CaptureSequencer() {
+ ALOGV("%s: Exit", __FUNCTION__);
+}
+
+void CaptureSequencer::setZslProcessor(wp<ZslProcessor> processor) {
+ Mutex::Autolock l(mInputMutex);
+ mZslProcessor = processor;
+}
+
+status_t CaptureSequencer::startCapture(int msgType) {
+ ALOGV("%s", __FUNCTION__);
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ if (mBusy) {
+ ALOGE("%s: Already busy capturing!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if (!mStartCapture) {
+ mMsgType = msgType;
+ mStartCapture = true;
+ mStartCaptureSignal.signal();
+ }
+ return OK;
+}
+
+status_t CaptureSequencer::waitUntilIdle(nsecs_t timeout) {
+ ATRACE_CALL();
+ ALOGV("%s: Waiting for idle", __FUNCTION__);
+ Mutex::Autolock l(mStateMutex);
+ status_t res = -1;
+ while (mCaptureState != IDLE) {
+ nsecs_t startTime = systemTime();
+
+ res = mStateChanged.waitRelative(mStateMutex, timeout);
+ if (res != OK) return res;
+
+ timeout -= (systemTime() - startTime);
+ }
+ ALOGV("%s: Now idle", __FUNCTION__);
+ return OK;
+}
+
+void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ mAEState = newState;
+ mAETriggerId = triggerId;
+ if (!mNewAEState) {
+ mNewAEState = true;
+ mNewNotifySignal.signal();
+ }
+}
+
+void CaptureSequencer::onFrameAvailable(int32_t frameId,
+ const CameraMetadata &frame) {
+ ALOGV("%s: Listener found new frame", __FUNCTION__);
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ mNewFrameId = frameId;
+ mNewFrame = frame;
+ if (!mNewFrameReceived) {
+ mNewFrameReceived = true;
+ mNewFrameSignal.signal();
+ }
+}
+
+void CaptureSequencer::onCaptureAvailable(nsecs_t timestamp,
+ sp<MemoryBase> captureBuffer) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock l(mInputMutex);
+ mCaptureTimestamp = timestamp;
+ mCaptureBuffer = captureBuffer;
+ if (!mNewCaptureReceived) {
+ mNewCaptureReceived = true;
+ mNewCaptureSignal.signal();
+ }
+}
+
+
+void CaptureSequencer::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+ if (mCaptureRequest.entryCount() != 0) {
+ result = " Capture request:\n";
+ write(fd, result.string(), result.size());
+ mCaptureRequest.dump(fd, 2, 6);
+ } else {
+ result = " Capture request: undefined\n";
+ write(fd, result.string(), result.size());
+ }
+ result = String8::format(" Current capture state: %s\n",
+ kStateNames[mCaptureState]);
+ result.append(" Latest captured frame:\n");
+ write(fd, result.string(), result.size());
+ mNewFrame.dump(fd, 2, 6);
+}
+
+/** Private members */
+
+const char* CaptureSequencer::kStateNames[CaptureSequencer::NUM_CAPTURE_STATES+1] =
+{
+ "IDLE",
+ "START",
+ "ZSL_START",
+ "ZSL_WAITING",
+ "ZSL_REPROCESSING",
+ "STANDARD_START",
+ "STANDARD_PRECAPTURE_WAIT",
+ "STANDARD_CAPTURE",
+ "STANDARD_CAPTURE_WAIT",
+ "BURST_CAPTURE_START",
+ "BURST_CAPTURE_WAIT",
+ "DONE",
+ "ERROR",
+ "UNKNOWN"
+};
+
+const CaptureSequencer::StateManager
+ CaptureSequencer::kStateManagers[CaptureSequencer::NUM_CAPTURE_STATES-1] = {
+ &CaptureSequencer::manageIdle,
+ &CaptureSequencer::manageStart,
+ &CaptureSequencer::manageZslStart,
+ &CaptureSequencer::manageZslWaiting,
+ &CaptureSequencer::manageZslReprocessing,
+ &CaptureSequencer::manageStandardStart,
+ &CaptureSequencer::manageStandardPrecaptureWait,
+ &CaptureSequencer::manageStandardCapture,
+ &CaptureSequencer::manageStandardCaptureWait,
+ &CaptureSequencer::manageBurstCaptureStart,
+ &CaptureSequencer::manageBurstCaptureWait,
+ &CaptureSequencer::manageDone,
+};
+
+bool CaptureSequencer::threadLoop() {
+ status_t res;
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+
+ CaptureState currentState;
+ {
+ Mutex::Autolock l(mStateMutex);
+ currentState = mCaptureState;
+ }
+
+ currentState = (this->*kStateManagers[currentState])(client);
+
+ Mutex::Autolock l(mStateMutex);
+ if (currentState != mCaptureState) {
+ mCaptureState = currentState;
+ ATRACE_INT("cam2_capt_state", mCaptureState);
+ ALOGV("Camera %d: New capture state %s",
+ client->getCameraId(), kStateNames[mCaptureState]);
+ mStateChanged.signal();
+ }
+
+ if (mCaptureState == ERROR) {
+ ALOGE("Camera %d: Stopping capture sequencer due to error",
+ client->getCameraId());
+ return false;
+ }
+
+ return true;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageIdle(sp<Camera2Client> &client) {
+ status_t res;
+ Mutex::Autolock l(mInputMutex);
+ while (!mStartCapture) {
+ res = mStartCaptureSignal.waitRelative(mInputMutex,
+ kWaitDuration);
+ if (res == TIMED_OUT) break;
+ }
+ if (mStartCapture) {
+ mStartCapture = false;
+ mBusy = true;
+ return START;
+ }
+ return IDLE;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &client) {
+ status_t res = OK;
+ ATRACE_CALL();
+ mCaptureId++;
+ if (mCaptureId >= Camera2Client::kCaptureRequestIdEnd) {
+ mCaptureId = Camera2Client::kCaptureRequestIdStart;
+ }
+ {
+ Mutex::Autolock l(mInputMutex);
+ mBusy = false;
+ }
+
+ {
+ SharedParameters::Lock l(client->getParameters());
+ switch (l.mParameters.state) {
+ case Parameters::DISCONNECTED:
+ ALOGW("%s: Camera %d: Discarding image data during shutdown ",
+ __FUNCTION__, client->getCameraId());
+ res = INVALID_OPERATION;
+ break;
+ case Parameters::STILL_CAPTURE:
+ l.mParameters.state = Parameters::STOPPED;
+ break;
+ case Parameters::VIDEO_SNAPSHOT:
+ l.mParameters.state = Parameters::RECORD;
+ break;
+ default:
+ ALOGE("%s: Camera %d: Still image produced unexpectedly "
+ "in state %s!",
+ __FUNCTION__, client->getCameraId(),
+ Parameters::getStateName(l.mParameters.state));
+ res = INVALID_OPERATION;
+ }
+ }
+ sp<ZslProcessor> processor = mZslProcessor.promote();
+ if (processor != 0) {
+ processor->clearZslQueue();
+ }
+
+ if (mCaptureBuffer != 0 && res == OK) {
+ Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+ ALOGV("%s: Sending still image to client", __FUNCTION__);
+ if (l.mCameraClient != 0) {
+ l.mCameraClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE,
+ mCaptureBuffer, NULL);
+ } else {
+ ALOGV("%s: No client!", __FUNCTION__);
+ }
+ }
+ mCaptureBuffer.clear();
+
+ return IDLE;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageStart(
+ sp<Camera2Client> &client) {
+ ALOGV("%s", __FUNCTION__);
+ status_t res;
+ ATRACE_CALL();
+ SharedParameters::Lock l(client->getParameters());
+ CaptureState nextState = DONE;
+
+ res = updateCaptureRequest(l.mParameters, client);
+ if (res != OK ) {
+ ALOGE("%s: Camera %d: Can't update still image capture request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return DONE;
+ }
+
+ if(l.mParameters.lightFx != Parameters::LIGHTFX_NONE &&
+ l.mParameters.state == Parameters::STILL_CAPTURE) {
+ nextState = BURST_CAPTURE_START;
+ }
+ else if (l.mParameters.zslMode &&
+ l.mParameters.state == Parameters::STILL_CAPTURE &&
+ l.mParameters.flashMode != Parameters::FLASH_MODE_ON) {
+ nextState = ZSL_START;
+ } else {
+ nextState = STANDARD_START;
+ }
+ mShutterNotified = false;
+
+ return nextState;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageZslStart(
+ sp<Camera2Client> &client) {
+ ALOGV("%s", __FUNCTION__);
+ status_t res;
+ sp<ZslProcessor> processor = mZslProcessor.promote();
+ if (processor == 0) {
+ ALOGE("%s: No ZSL queue to use!", __FUNCTION__);
+ return DONE;
+ }
+
+ client->registerFrameListener(mCaptureId, mCaptureId + 1,
+ this);
+
+ // TODO: Actually select the right thing here.
+ res = processor->pushToReprocess(mCaptureId);
+ if (res != OK) {
+ if (res == NOT_ENOUGH_DATA) {
+ ALOGV("%s: Camera %d: ZSL queue doesn't have good frame, "
+ "falling back to normal capture", __FUNCTION__,
+ client->getCameraId());
+ } else {
+ ALOGE("%s: Camera %d: Error in ZSL queue: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ }
+ return STANDARD_START;
+ }
+
+ SharedParameters::Lock l(client->getParameters());
+ /* warning: this also locks a SharedCameraClient */
+ shutterNotifyLocked(l.mParameters, client, mMsgType);
+ mShutterNotified = true;
+ mTimeoutCount = kMaxTimeoutsForCaptureEnd;
+ return STANDARD_CAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageZslWaiting(
+ sp<Camera2Client> &client) {
+ ALOGV("%s", __FUNCTION__);
+ return DONE;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageZslReprocessing(
+ sp<Camera2Client> &client) {
+ ALOGV("%s", __FUNCTION__);
+ return START;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart(
+ sp<Camera2Client> &client) {
+ ATRACE_CALL();
+ client->registerFrameListener(mCaptureId, mCaptureId + 1,
+ this);
+ {
+ SharedParameters::Lock l(client->getParameters());
+ mTriggerId = l.mParameters.precaptureTriggerCounter++;
+ }
+ client->getCameraDevice()->triggerPrecaptureMetering(mTriggerId);
+
+ mAeInPrecapture = false;
+ mTimeoutCount = kMaxTimeoutsForPrecaptureStart;
+ return STANDARD_PRECAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageStandardPrecaptureWait(
+ sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ while (!mNewAEState) {
+ res = mNewNotifySignal.waitRelative(mInputMutex, kWaitDuration);
+ if (res == TIMED_OUT) {
+ mTimeoutCount--;
+ break;
+ }
+ }
+ if (mTimeoutCount <= 0) {
+ ALOGW("Timed out waiting for precapture %s",
+ mAeInPrecapture ? "end" : "start");
+ return STANDARD_CAPTURE;
+ }
+ if (mNewAEState) {
+ if (!mAeInPrecapture) {
+ // Waiting to see PRECAPTURE state
+ if (mAETriggerId == mTriggerId &&
+ mAEState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+ ALOGV("%s: Got precapture start", __FUNCTION__);
+ mAeInPrecapture = true;
+ mTimeoutCount = kMaxTimeoutsForPrecaptureEnd;
+ }
+ } else {
+ // Waiting to see PRECAPTURE state end
+ if (mAETriggerId == mTriggerId &&
+ mAEState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+ ALOGV("%s: Got precapture end", __FUNCTION__);
+ return STANDARD_CAPTURE;
+ }
+ }
+ mNewAEState = false;
+ }
+ return STANDARD_PRECAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageStandardCapture(
+ sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ SharedParameters::Lock l(client->getParameters());
+ Vector<uint8_t> outputStreams;
+
+ outputStreams.push(client->getPreviewStreamId());
+ outputStreams.push(client->getCaptureStreamId());
+
+ if (l.mParameters.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+ outputStreams.push(client->getCallbackStreamId());
+ }
+
+ if (l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
+ outputStreams.push(client->getRecordingStreamId());
+ }
+
+ res = mCaptureRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
+ outputStreams);
+ if (res == OK) {
+ res = mCaptureRequest.update(ANDROID_REQUEST_ID,
+ &mCaptureId, 1);
+ }
+ if (res == OK) {
+ res = mCaptureRequest.sort();
+ }
+
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set up still capture request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return DONE;
+ }
+
+ CameraMetadata captureCopy = mCaptureRequest;
+ if (captureCopy.entryCount() == 0) {
+ ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
+ __FUNCTION__, client->getCameraId());
+ return DONE;
+ }
+
+ if (l.mParameters.state == Parameters::STILL_CAPTURE) {
+ res = client->stopStream();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to stop preview for still capture: "
+ "%s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return DONE;
+ }
+ }
+ // TODO: Capture should be atomic with setStreamingRequest here
+ res = client->getCameraDevice()->capture(captureCopy);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to submit still image capture request: "
+ "%s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return DONE;
+ }
+
+ mTimeoutCount = kMaxTimeoutsForCaptureEnd;
+ return STANDARD_CAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait(
+ sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ Mutex::Autolock l(mInputMutex);
+ while (!mNewFrameReceived) {
+ res = mNewFrameSignal.waitRelative(mInputMutex, kWaitDuration);
+ if (res == TIMED_OUT) {
+ mTimeoutCount--;
+ break;
+ }
+ }
+ if (mNewFrameReceived && !mShutterNotified) {
+ SharedParameters::Lock l(client->getParameters());
+ /* warning: this also locks a SharedCameraClient */
+ shutterNotifyLocked(l.mParameters, client, mMsgType);
+ mShutterNotified = true;
+ }
+ while (mNewFrameReceived && !mNewCaptureReceived) {
+ res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
+ if (res == TIMED_OUT) {
+ mTimeoutCount--;
+ break;
+ }
+ }
+ if (mTimeoutCount <= 0) {
+ ALOGW("Timed out waiting for capture to complete");
+ return DONE;
+ }
+ if (mNewFrameReceived && mNewCaptureReceived) {
+ if (mNewFrameId != mCaptureId) {
+ ALOGW("Mismatched capture frame IDs: Expected %d, got %d",
+ mCaptureId, mNewFrameId);
+ }
+ camera_metadata_entry_t entry;
+ entry = mNewFrame.find(ANDROID_SENSOR_TIMESTAMP);
+ if (entry.count == 0) {
+ ALOGE("No timestamp field in capture frame!");
+ }
+ if (entry.data.i64[0] != mCaptureTimestamp) {
+ ALOGW("Mismatched capture timestamps: Metadata frame %lld,"
+ " captured buffer %lld", entry.data.i64[0], mCaptureTimestamp);
+ }
+ client->removeFrameListener(mCaptureId, mCaptureId + 1, this);
+
+ mNewFrameReceived = false;
+ mNewCaptureReceived = false;
+ return DONE;
+ }
+ return STANDARD_CAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureStart(
+ sp<Camera2Client> &client) {
+ ALOGV("%s", __FUNCTION__);
+ status_t res;
+ ATRACE_CALL();
+
+ // check which burst mode is set, create respective burst object
+ {
+ SharedParameters::Lock l(client->getParameters());
+
+ res = updateCaptureRequest(l.mParameters, client);
+ if(res != OK) {
+ return DONE;
+ }
+
+ //
+ // check for burst mode type in mParameters here
+ //
+ mBurstCapture = new BurstCapture(client, this);
+ }
+
+ res = mCaptureRequest.update(ANDROID_REQUEST_ID, &mCaptureId, 1);
+ if (res == OK) {
+ res = mCaptureRequest.sort();
+ }
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set up still capture request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return DONE;
+ }
+
+ CameraMetadata captureCopy = mCaptureRequest;
+ if (captureCopy.entryCount() == 0) {
+ ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
+ __FUNCTION__, client->getCameraId());
+ return DONE;
+ }
+
+ Vector<CameraMetadata> requests;
+ requests.push(mCaptureRequest);
+ res = mBurstCapture->start(requests, mCaptureId);
+ mTimeoutCount = kMaxTimeoutsForCaptureEnd * 10;
+ return BURST_CAPTURE_WAIT;
+}
+
+CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureWait(
+ sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+
+ while (!mNewCaptureReceived) {
+ res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration);
+ if (res == TIMED_OUT) {
+ mTimeoutCount--;
+ break;
+ }
+ }
+
+ if (mTimeoutCount <= 0) {
+ ALOGW("Timed out waiting for burst capture to complete");
+ return DONE;
+ }
+ if (mNewCaptureReceived) {
+ mNewCaptureReceived = false;
+ // TODO: update mCaptureId to last burst's capture ID + 1?
+ return DONE;
+ }
+
+ return BURST_CAPTURE_WAIT;
+}
+
+status_t CaptureSequencer::updateCaptureRequest(const Parameters &params,
+ sp<Camera2Client> &client) {
+ ATRACE_CALL();
+ status_t res;
+ if (mCaptureRequest.entryCount() == 0) {
+ res = client->getCameraDevice()->createDefaultRequest(
+ CAMERA2_TEMPLATE_STILL_CAPTURE,
+ &mCaptureRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create default still image request:"
+ " %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = params.updateRequest(&mCaptureRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update common entries of capture "
+ "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+
+ res = params.updateRequestJpeg(&mCaptureRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update JPEG entries of capture "
+ "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+/*static*/ void CaptureSequencer::shutterNotifyLocked(const Parameters &params,
+ sp<Camera2Client> client, int msgType) {
+ ATRACE_CALL();
+
+ if (params.state == Parameters::STILL_CAPTURE
+ && params.playShutterSound
+ && (msgType & CAMERA_MSG_SHUTTER)) {
+ client->getCameraService()->playSound(CameraService::SOUND_SHUTTER);
+ }
+
+ {
+ Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+
+ ALOGV("%s: Notifying of shutter close to client", __FUNCTION__);
+ if (l.mCameraClient != 0) {
+ // ShutterCallback
+ l.mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER,
+ /*ext1*/0, /*ext2*/0);
+
+ // RawCallback with null buffer
+ l.mCameraClient->notifyCallback(CAMERA_MSG_RAW_IMAGE_NOTIFY,
+ /*ext1*/0, /*ext2*/0);
+ } else {
+ ALOGV("%s: No client!", __FUNCTION__);
+ }
+ }
+}
+
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/camera2/CaptureSequencer.h
new file mode 100644
index 00000000..c42df053
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_CAPTURESEQUENCER_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_CAPTURESEQUENCER_H
+
+#include <binder/MemoryBase.h>
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+#include "CameraMetadata.h"
+#include "Parameters.h"
+#include "FrameProcessor.h"
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+class ZslProcessor;
+class BurstCapture;
+
+/**
+ * Manages the still image capture process for
+ * zero-shutter-lag, regular, and video snapshots.
+ */
+class CaptureSequencer:
+ virtual public Thread,
+ virtual public FrameProcessor::FilteredListener {
+ public:
+ CaptureSequencer(wp<Camera2Client> client);
+ ~CaptureSequencer();
+
+ // Get reference to the ZslProcessor, which holds the ZSL buffers and frames
+ void setZslProcessor(wp<ZslProcessor> processor);
+
+ // Begin still image capture
+ status_t startCapture(int msgType);
+
+ // Wait until current image capture completes; returns immediately if no
+ // capture is active. Returns TIMED_OUT if capture does not complete during
+ // the specified duration.
+ status_t waitUntilIdle(nsecs_t timeout);
+
+ // Notifications about AE state changes
+ void notifyAutoExposure(uint8_t newState, int triggerId);
+
+ // Notifications from the frame processor
+ virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+
+ // Notifications from the JPEG processor
+ void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer);
+
+ void dump(int fd, const Vector<String16>& args);
+
+ private:
+ /**
+ * Accessed by other threads
+ */
+ Mutex mInputMutex;
+
+ bool mStartCapture;
+ bool mBusy;
+ Condition mStartCaptureSignal;
+
+ bool mNewAEState;
+ uint8_t mAEState;
+ int mAETriggerId;
+ Condition mNewNotifySignal;
+
+ bool mNewFrameReceived;
+ int32_t mNewFrameId;
+ CameraMetadata mNewFrame;
+ Condition mNewFrameSignal;
+
+ bool mNewCaptureReceived;
+ nsecs_t mCaptureTimestamp;
+ sp<MemoryBase> mCaptureBuffer;
+ Condition mNewCaptureSignal;
+
+ bool mShutterNotified;
+
+ /**
+ * Internal to CaptureSequencer
+ */
+ static const nsecs_t kWaitDuration = 100000000; // 100 ms
+ static const int kMaxTimeoutsForPrecaptureStart = 2; // 200 ms
+ static const int kMaxTimeoutsForPrecaptureEnd = 20; // 2 sec
+ static const int kMaxTimeoutsForCaptureEnd = 40; // 4 sec
+
+ wp<Camera2Client> mClient;
+ wp<ZslProcessor> mZslProcessor;
+ sp<BurstCapture> mBurstCapture;
+
+ enum CaptureState {
+ IDLE,
+ START,
+ ZSL_START,
+ ZSL_WAITING,
+ ZSL_REPROCESSING,
+ STANDARD_START,
+ STANDARD_PRECAPTURE_WAIT,
+ STANDARD_CAPTURE,
+ STANDARD_CAPTURE_WAIT,
+ BURST_CAPTURE_START,
+ BURST_CAPTURE_WAIT,
+ DONE,
+ ERROR,
+ NUM_CAPTURE_STATES
+ } mCaptureState;
+ static const char* kStateNames[];
+ Mutex mStateMutex; // Guards mCaptureState
+ Condition mStateChanged;
+
+ typedef CaptureState (CaptureSequencer::*StateManager)(sp<Camera2Client> &client);
+ static const StateManager kStateManagers[];
+
+ CameraMetadata mCaptureRequest;
+
+ int mTriggerId;
+ int mTimeoutCount;
+ bool mAeInPrecapture;
+
+ int32_t mCaptureId;
+ int mMsgType;
+
+ // Main internal methods
+
+ virtual bool threadLoop();
+
+ CaptureState manageIdle(sp<Camera2Client> &client);
+ CaptureState manageStart(sp<Camera2Client> &client);
+
+ CaptureState manageZslStart(sp<Camera2Client> &client);
+ CaptureState manageZslWaiting(sp<Camera2Client> &client);
+ CaptureState manageZslReprocessing(sp<Camera2Client> &client);
+
+ CaptureState manageStandardStart(sp<Camera2Client> &client);
+ CaptureState manageStandardPrecaptureWait(sp<Camera2Client> &client);
+ CaptureState manageStandardCapture(sp<Camera2Client> &client);
+ CaptureState manageStandardCaptureWait(sp<Camera2Client> &client);
+
+ CaptureState manageBurstCaptureStart(sp<Camera2Client> &client);
+ CaptureState manageBurstCaptureWait(sp<Camera2Client> &client);
+
+ CaptureState manageDone(sp<Camera2Client> &client);
+
+ // Utility methods
+
+ status_t updateCaptureRequest(const Parameters &params,
+ sp<Camera2Client> &client);
+
+ // Emit Shutter/Raw callback to java, and maybe play a shutter sound
+ static void shutterNotifyLocked(const Parameters &params,
+ sp<Camera2Client> client, int msgType);
+};
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
new file mode 100644
index 00000000..76e84ccd
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-FrameProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "FrameProcessor.h"
+#include "../Camera2Device.h"
+#include "../Camera2Client.h"
+
+namespace android {
+namespace camera2 {
+
+FrameProcessor::FrameProcessor(wp<Camera2Client> client):
+ Thread(false), mClient(client), mLastFrameNumberOfFaces(0) {
+}
+
+FrameProcessor::~FrameProcessor() {
+ ALOGV("%s: Exit", __FUNCTION__);
+}
+
+status_t FrameProcessor::registerListener(int32_t minId,
+ int32_t maxId, wp<FilteredListener> listener) {
+ Mutex::Autolock l(mInputMutex);
+ ALOGV("%s: Registering listener for frame id range %d - %d",
+ __FUNCTION__, minId, maxId);
+ RangeListener rListener = { minId, maxId, listener };
+ mRangeListeners.push_back(rListener);
+ return OK;
+}
+
+status_t FrameProcessor::removeListener(int32_t minId,
+ int32_t maxId, wp<FilteredListener> listener) {
+ Mutex::Autolock l(mInputMutex);
+ List<RangeListener>::iterator item = mRangeListeners.begin();
+ while (item != mRangeListeners.end()) {
+ if (item->minId == minId &&
+ item->maxId == maxId &&
+ item->listener == listener) {
+ item = mRangeListeners.erase(item);
+ } else {
+ item++;
+ }
+ }
+ return OK;
+}
+
+void FrameProcessor::dump(int fd, const Vector<String16>& args) {
+ String8 result(" Latest received frame:\n");
+ write(fd, result.string(), result.size());
+ mLastFrame.dump(fd, 2, 6);
+}
+
+bool FrameProcessor::threadLoop() {
+ status_t res;
+
+ sp<Camera2Device> device;
+ {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ device = client->getCameraDevice();
+ if (device == 0) return false;
+ }
+
+ res = device->waitForNextFrame(kWaitDuration);
+ if (res == OK) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ processNewFrames(client);
+ } else if (res != TIMED_OUT) {
+ ALOGE("Camera2Client::FrameProcessor: Error waiting for new "
+ "frames: %s (%d)", strerror(-res), res);
+ }
+
+ return true;
+}
+
+void FrameProcessor::processNewFrames(sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ CameraMetadata frame;
+ while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) {
+ camera_metadata_entry_t entry;
+
+ entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Error reading frame number",
+ __FUNCTION__, client->getCameraId());
+ break;
+ }
+ ATRACE_INT("cam2_frame", entry.data.i32[0]);
+
+ res = processFaceDetect(frame, client);
+ if (res != OK) break;
+
+ res = processListeners(frame, client);
+ if (res != OK) break;
+
+ if (!frame.isEmpty()) {
+ mLastFrame.acquire(frame);
+ }
+ }
+ if (res != NOT_ENOUGH_DATA) {
+ ALOGE("%s: Camera %d: Error getting next frame: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return;
+ }
+
+ return;
+}
+
+status_t FrameProcessor::processListeners(const CameraMetadata &frame,
+ sp<Camera2Client> &client) {
+ status_t res;
+ ATRACE_CALL();
+ camera_metadata_ro_entry_t entry;
+
+ entry = frame.find(ANDROID_REQUEST_ID);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Error reading frame id",
+ __FUNCTION__, client->getCameraId());
+ return BAD_VALUE;
+ }
+ int32_t frameId = entry.data.i32[0];
+
+ List<sp<FilteredListener> > listeners;
+ {
+ Mutex::Autolock l(mInputMutex);
+
+ List<RangeListener>::iterator item = mRangeListeners.begin();
+ while (item != mRangeListeners.end()) {
+ if (frameId >= item->minId &&
+ frameId < item->maxId) {
+ sp<FilteredListener> listener = item->listener.promote();
+ if (listener == 0) {
+ item = mRangeListeners.erase(item);
+ continue;
+ } else {
+ listeners.push_back(listener);
+ }
+ }
+ item++;
+ }
+ }
+ ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size());
+ List<sp<FilteredListener> >::iterator item = listeners.begin();
+ for (; item != listeners.end(); item++) {
+ (*item)->onFrameAvailable(frameId, frame);
+ }
+ return OK;
+}
+
+status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame,
+ sp<Camera2Client> &client) {
+ status_t res = BAD_VALUE;
+ ATRACE_CALL();
+ camera_metadata_ro_entry_t entry;
+ bool enableFaceDetect;
+ int maxFaces;
+ {
+ SharedParameters::Lock l(client->getParameters());
+ enableFaceDetect = l.mParameters.enableFaceDetect;
+ }
+ entry = frame.find(ANDROID_STATISTICS_FACE_DETECT_MODE);
+
+ // TODO: This should be an error once implementations are compliant
+ if (entry.count == 0) {
+ return OK;
+ }
+
+ uint8_t faceDetectMode = entry.data.u8[0];
+
+ camera_frame_metadata metadata;
+ Vector<camera_face_t> faces;
+ metadata.number_of_faces = 0;
+
+ if (enableFaceDetect && faceDetectMode != ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) {
+ SharedParameters::Lock l(client->getParameters());
+ entry = frame.find(ANDROID_STATISTICS_FACE_RECTANGLES);
+ if (entry.count == 0) {
+ // No faces this frame
+ /* warning: locks SharedCameraClient */
+ callbackFaceDetection(client, metadata);
+ return OK;
+ }
+ metadata.number_of_faces = entry.count / 4;
+ if (metadata.number_of_faces >
+ l.mParameters.fastInfo.maxFaces) {
+ ALOGE("%s: Camera %d: More faces than expected! (Got %d, max %d)",
+ __FUNCTION__, client->getCameraId(),
+ metadata.number_of_faces, l.mParameters.fastInfo.maxFaces);
+ return res;
+ }
+ const int32_t *faceRects = entry.data.i32;
+
+ entry = frame.find(ANDROID_STATISTICS_FACE_SCORES);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Unable to read face scores",
+ __FUNCTION__, client->getCameraId());
+ return res;
+ }
+ const uint8_t *faceScores = entry.data.u8;
+
+ const int32_t *faceLandmarks = NULL;
+ const int32_t *faceIds = NULL;
+
+ if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) {
+ entry = frame.find(ANDROID_STATISTICS_FACE_LANDMARKS);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Unable to read face landmarks",
+ __FUNCTION__, client->getCameraId());
+ return res;
+ }
+ faceLandmarks = entry.data.i32;
+
+ entry = frame.find(ANDROID_STATISTICS_FACE_IDS);
+
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Unable to read face IDs",
+ __FUNCTION__, client->getCameraId());
+ return res;
+ }
+ faceIds = entry.data.i32;
+ }
+
+ faces.setCapacity(metadata.number_of_faces);
+
+ size_t maxFaces = metadata.number_of_faces;
+ for (size_t i = 0; i < maxFaces; i++) {
+ if (faceScores[i] == 0) {
+ metadata.number_of_faces--;
+ continue;
+ }
+
+ camera_face_t face;
+
+ face.rect[0] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 0]);
+ face.rect[1] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 1]);
+ face.rect[2] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 2]);
+ face.rect[3] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 3]);
+
+ face.score = faceScores[i];
+ if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) {
+ face.id = faceIds[i];
+ face.left_eye[0] =
+ l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 0]);
+ face.left_eye[1] =
+ l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 1]);
+ face.right_eye[0] =
+ l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 2]);
+ face.right_eye[1] =
+ l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 3]);
+ face.mouth[0] =
+ l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 4]);
+ face.mouth[1] =
+ l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 5]);
+ } else {
+ face.id = 0;
+ face.left_eye[0] = face.left_eye[1] = -2000;
+ face.right_eye[0] = face.right_eye[1] = -2000;
+ face.mouth[0] = face.mouth[1] = -2000;
+ }
+ faces.push_back(face);
+ }
+
+ metadata.faces = faces.editArray();
+ }
+
+ /* warning: locks SharedCameraClient */
+ callbackFaceDetection(client, metadata);
+
+ return OK;
+}
+
+void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client,
+ /*in*/camera_frame_metadata &metadata) {
+
+ /* Filter out repeated 0-face callbacks, but not when the last frame was >0 */
+ if (metadata.number_of_faces != 0 || mLastFrameNumberOfFaces != metadata.number_of_faces) {
+ Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+ if (l.mCameraClient != NULL) {
+ l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_METADATA,
+ NULL, &metadata);
+ }
+ }
+
+ mLastFrameNumberOfFaces = metadata.number_of_faces;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h
new file mode 100644
index 00000000..3bd4e257
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_FRAMEPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_FRAMEPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include "CameraMetadata.h"
+
+struct camera_frame_metadata;
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+/* Output frame metadata processing thread. This thread waits for new
+ * frames from the device, and analyzes them as necessary.
+ */
+class FrameProcessor: public Thread {
+ public:
+ FrameProcessor(wp<Camera2Client> client);
+ ~FrameProcessor();
+
+ struct FilteredListener: virtual public RefBase {
+ virtual void onFrameAvailable(int32_t frameId,
+ const CameraMetadata &frame) = 0;
+ };
+
+ // Register a listener for a range of IDs [minId, maxId). Multiple listeners
+ // can be listening to the same range
+ status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+ status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+
+ void dump(int fd, const Vector<String16>& args);
+ private:
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ wp<Camera2Client> mClient;
+
+ virtual bool threadLoop();
+
+ Mutex mInputMutex;
+
+ struct RangeListener {
+ int32_t minId;
+ int32_t maxId;
+ wp<FilteredListener> listener;
+ };
+ List<RangeListener> mRangeListeners;
+
+ void processNewFrames(sp<Camera2Client> &client);
+
+ status_t processFaceDetect(const CameraMetadata &frame,
+ sp<Camera2Client> &client);
+
+ status_t processListeners(const CameraMetadata &frame,
+ sp<Camera2Client> &client);
+
+ CameraMetadata mLastFrame;
+ int mLastFrameNumberOfFaces;
+
+ // Emit FaceDetection event to java if faces changed
+ void callbackFaceDetection(sp<Camera2Client> client,
+ camera_frame_metadata &metadata);
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.cpp b/services/camera/libcameraservice/camera2/JpegCompressor.cpp
new file mode 100644
index 00000000..702ef58b
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/JpegCompressor.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Camera2-JpegCompressor"
+
+#include <utils/Log.h>
+#include <ui/GraphicBufferMapper.h>
+
+#include "JpegCompressor.h"
+
+namespace android {
+namespace camera2 {
+
+JpegCompressor::JpegCompressor():
+ Thread(false),
+ mIsBusy(false),
+ mCaptureTime(0) {
+}
+
+JpegCompressor::~JpegCompressor() {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock lock(mMutex);
+}
+
+status_t JpegCompressor::start(Vector<CpuConsumer::LockedBuffer*> buffers,
+ nsecs_t captureTime) {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock busyLock(mBusyMutex);
+
+ if (mIsBusy) {
+ ALOGE("%s: Already processing a buffer!", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ mIsBusy = true;
+
+ mBuffers = buffers;
+ mCaptureTime = captureTime;
+
+ status_t res;
+ res = run("JpegCompressor");
+ if (res != OK) {
+ ALOGE("%s: Unable to start up compression thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ //delete mBuffers; // necessary?
+ }
+ return res;
+}
+
+status_t JpegCompressor::cancel() {
+ ALOGV("%s", __FUNCTION__);
+ requestExitAndWait();
+ return OK;
+}
+
+status_t JpegCompressor::readyToRun() {
+ ALOGV("%s", __FUNCTION__);
+ return OK;
+}
+
+bool JpegCompressor::threadLoop() {
+ ALOGV("%s", __FUNCTION__);
+
+ mAuxBuffer = mBuffers[0]; // input
+ mJpegBuffer = mBuffers[1]; // output
+
+ // Set up error management
+ mJpegErrorInfo = NULL;
+ JpegError error;
+ error.parent = this;
+
+ mCInfo.err = jpeg_std_error(&error);
+ mCInfo.err->error_exit = jpegErrorHandler;
+
+ jpeg_create_compress(&mCInfo);
+ if (checkError("Error initializing compression")) return false;
+
+ // Route compressed data straight to output stream buffer
+ JpegDestination jpegDestMgr;
+ jpegDestMgr.parent = this;
+ jpegDestMgr.init_destination = jpegInitDestination;
+ jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
+ jpegDestMgr.term_destination = jpegTermDestination;
+
+ mCInfo.dest = &jpegDestMgr;
+
+ // Set up compression parameters
+ mCInfo.image_width = mAuxBuffer->width;
+ mCInfo.image_height = mAuxBuffer->height;
+ mCInfo.input_components = 1; // 3;
+ mCInfo.in_color_space = JCS_GRAYSCALE; // JCS_RGB
+
+ ALOGV("%s: image_width = %d, image_height = %d", __FUNCTION__, mCInfo.image_width, mCInfo.image_height);
+
+ jpeg_set_defaults(&mCInfo);
+ if (checkError("Error configuring defaults")) return false;
+
+ // Do compression
+ jpeg_start_compress(&mCInfo, TRUE);
+ if (checkError("Error starting compression")) return false;
+
+ size_t rowStride = mAuxBuffer->stride;// * 3;
+ const size_t kChunkSize = 32;
+ while (mCInfo.next_scanline < mCInfo.image_height) {
+ JSAMPROW chunk[kChunkSize];
+ for (size_t i = 0 ; i < kChunkSize; i++) {
+ chunk[i] = (JSAMPROW)
+ (mAuxBuffer->data + (i + mCInfo.next_scanline) * rowStride);
+ }
+ jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
+ if (checkError("Error while compressing")) return false;
+ if (exitPending()) {
+ ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
+ cleanUp();
+ return false;
+ }
+ }
+
+ jpeg_finish_compress(&mCInfo);
+ if (checkError("Error while finishing compression")) return false;
+
+ cleanUp();
+ return false;
+}
+
+bool JpegCompressor::isBusy() {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock busyLock(mBusyMutex);
+ return mIsBusy;
+}
+
+// old function -- TODO: update for new buffer type
+bool JpegCompressor::isStreamInUse(uint32_t id) {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock lock(mBusyMutex);
+
+ if (mBuffers.size() && mIsBusy) {
+ for (size_t i = 0; i < mBuffers.size(); i++) {
+// if ( mBuffers[i].streamId == (int)id ) return true;
+ }
+ }
+ return false;
+}
+
+bool JpegCompressor::waitForDone(nsecs_t timeout) {
+ ALOGV("%s", __FUNCTION__);
+ Mutex::Autolock lock(mBusyMutex);
+ status_t res = OK;
+ if (mIsBusy) {
+ res = mDone.waitRelative(mBusyMutex, timeout);
+ }
+ return (res == OK);
+}
+
+bool JpegCompressor::checkError(const char *msg) {
+ ALOGV("%s", __FUNCTION__);
+ if (mJpegErrorInfo) {
+ char errBuffer[JMSG_LENGTH_MAX];
+ mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
+ ALOGE("%s: %s: %s",
+ __FUNCTION__, msg, errBuffer);
+ cleanUp();
+ mJpegErrorInfo = NULL;
+ return true;
+ }
+ return false;
+}
+
+void JpegCompressor::cleanUp() {
+ ALOGV("%s", __FUNCTION__);
+ jpeg_destroy_compress(&mCInfo);
+ Mutex::Autolock lock(mBusyMutex);
+ mIsBusy = false;
+ mDone.signal();
+}
+
+void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
+ ALOGV("%s", __FUNCTION__);
+ JpegError *error = static_cast<JpegError*>(cinfo->err);
+ error->parent->mJpegErrorInfo = cinfo;
+}
+
+void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
+ ALOGV("%s", __FUNCTION__);
+ JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
+ ALOGV("%s: Setting destination to %p, size %d",
+ __FUNCTION__, dest->parent->mJpegBuffer->data, kMaxJpegSize);
+ dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer->data);
+ dest->free_in_buffer = kMaxJpegSize;
+}
+
+boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) {
+ ALOGV("%s", __FUNCTION__);
+ ALOGE("%s: JPEG destination buffer overflow!",
+ __FUNCTION__);
+ return true;
+}
+
+void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
+ ALOGV("%s", __FUNCTION__);
+ ALOGV("%s: Done writing JPEG data. %d bytes left in buffer",
+ __FUNCTION__, cinfo->dest->free_in_buffer);
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.h b/services/camera/libcameraservice/camera2/JpegCompressor.h
new file mode 100644
index 00000000..945b1def
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/JpegCompressor.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 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.
+ */
+
+
+/**
+ * This class simulates a hardware JPEG compressor. It receives image buffers
+ * in RGBA_8888 format, processes them in a worker thread, and then pushes them
+ * out to their destination stream.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H
+#define ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H
+
+#include "utils/Thread.h"
+#include "utils/Mutex.h"
+#include "utils/Timers.h"
+#include "utils/Vector.h"
+//#include "Base.h"
+#include <stdio.h>
+#include <gui/CpuConsumer.h>
+
+extern "C" {
+#include <jpeglib.h>
+}
+
+
+namespace android {
+namespace camera2 {
+
+class JpegCompressor: private Thread, public virtual RefBase {
+ public:
+
+ JpegCompressor();
+ ~JpegCompressor();
+
+ // Start compressing COMPRESSED format buffers; JpegCompressor takes
+ // ownership of the Buffers vector.
+ status_t start(Vector<CpuConsumer::LockedBuffer*> buffers,
+ nsecs_t captureTime);
+
+ status_t cancel();
+
+ bool isBusy();
+ bool isStreamInUse(uint32_t id);
+
+ bool waitForDone(nsecs_t timeout);
+
+ // TODO: Measure this
+ static const size_t kMaxJpegSize = 300000;
+
+ private:
+ Mutex mBusyMutex;
+ Mutex mMutex;
+ bool mIsBusy;
+ Condition mDone;
+ nsecs_t mCaptureTime;
+
+ Vector<CpuConsumer::LockedBuffer*> mBuffers;
+ CpuConsumer::LockedBuffer *mJpegBuffer;
+ CpuConsumer::LockedBuffer *mAuxBuffer;
+ bool mFoundJpeg, mFoundAux;
+
+ jpeg_compress_struct mCInfo;
+
+ struct JpegError : public jpeg_error_mgr {
+ JpegCompressor *parent;
+ };
+ j_common_ptr mJpegErrorInfo;
+
+ struct JpegDestination : public jpeg_destination_mgr {
+ JpegCompressor *parent;
+ };
+
+ static void jpegErrorHandler(j_common_ptr cinfo);
+
+ static void jpegInitDestination(j_compress_ptr cinfo);
+ static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo);
+ static void jpegTermDestination(j_compress_ptr cinfo);
+
+ bool checkError(const char *msg);
+ void cleanUp();
+
+ /**
+ * Inherited Thread virtual overrides
+ */
+ private:
+ virtual status_t readyToRun();
+ virtual bool threadLoop();
+};
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.cpp b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
new file mode 100644
index 00000000..ffc072ba
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-JpegProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <netinet/in.h>
+
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "JpegProcessor.h"
+#include <gui/SurfaceTextureClient.h>
+#include "../Camera2Device.h"
+#include "../Camera2Client.h"
+
+
+namespace android {
+namespace camera2 {
+
+JpegProcessor::JpegProcessor(
+ wp<Camera2Client> client,
+ wp<CaptureSequencer> sequencer):
+ Thread(false),
+ mClient(client),
+ mSequencer(sequencer),
+ mCaptureAvailable(false),
+ mCaptureStreamId(NO_STREAM) {
+}
+
+JpegProcessor::~JpegProcessor() {
+ ALOGV("%s: Exit", __FUNCTION__);
+ deleteStream();
+}
+
+void JpegProcessor::onFrameAvailable() {
+ Mutex::Autolock l(mInputMutex);
+ if (!mCaptureAvailable) {
+ mCaptureAvailable = true;
+ mCaptureAvailableSignal.signal();
+ }
+}
+
+status_t JpegProcessor::updateStream(const Parameters &params) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ // Find out buffer size for JPEG
+ camera_metadata_ro_entry_t maxJpegSize =
+ params.staticInfo(ANDROID_JPEG_MAX_SIZE);
+ if (maxJpegSize.count == 0) {
+ ALOGE("%s: Camera %d: Can't find ANDROID_JPEG_MAX_SIZE!",
+ __FUNCTION__, client->getCameraId());
+ return INVALID_OPERATION;
+ }
+
+ if (mCaptureConsumer == 0) {
+ // Create CPU buffer queue endpoint
+ mCaptureConsumer = new CpuConsumer(1);
+ mCaptureConsumer->setFrameAvailableListener(this);
+ mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer"));
+ mCaptureWindow = new SurfaceTextureClient(
+ mCaptureConsumer->getProducerInterface());
+ // Create memory for API consumption
+ mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0,
+ "Camera2Client::CaptureHeap");
+ if (mCaptureHeap->getSize() == 0) {
+ ALOGE("%s: Camera %d: Unable to allocate memory for capture",
+ __FUNCTION__, client->getCameraId());
+ return NO_MEMORY;
+ }
+ }
+
+ if (mCaptureStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight;
+ res = device->getStreamInfo(mCaptureStreamId,
+ &currentWidth, &currentHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying capture output stream info: "
+ "%s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)params.pictureWidth ||
+ currentHeight != (uint32_t)params.pictureHeight) {
+ ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+ __FUNCTION__, client->getCameraId(), mCaptureStreamId);
+ res = device->deleteStream(mCaptureStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for capture: %s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mCaptureStreamId = NO_STREAM;
+ }
+ }
+
+ if (mCaptureStreamId == NO_STREAM) {
+ // Create stream for HAL production
+ res = device->createStream(mCaptureWindow,
+ params.pictureWidth, params.pictureHeight,
+ HAL_PIXEL_FORMAT_BLOB, maxJpegSize.data.i32[0],
+ &mCaptureStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create output stream for capture: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+
+ }
+ return OK;
+}
+
+status_t JpegProcessor::deleteStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ if (mCaptureStreamId != NO_STREAM) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ device->deleteStream(mCaptureStreamId);
+
+ mCaptureHeap.clear();
+ mCaptureWindow.clear();
+ mCaptureConsumer.clear();
+
+ mCaptureStreamId = NO_STREAM;
+ }
+ return OK;
+}
+
+int JpegProcessor::getStreamId() const {
+ Mutex::Autolock l(mInputMutex);
+ return mCaptureStreamId;
+}
+
+void JpegProcessor::dump(int fd, const Vector<String16>& args) const {
+}
+
+bool JpegProcessor::threadLoop() {
+ status_t res;
+
+ {
+ Mutex::Autolock l(mInputMutex);
+ while (!mCaptureAvailable) {
+ res = mCaptureAvailableSignal.waitRelative(mInputMutex,
+ kWaitDuration);
+ if (res == TIMED_OUT) return true;
+ }
+ mCaptureAvailable = false;
+ }
+
+ do {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ res = processNewCapture(client);
+ } while (res == OK);
+
+ return true;
+}
+
+status_t JpegProcessor::processNewCapture(sp<Camera2Client> &client) {
+ ATRACE_CALL();
+ status_t res;
+ sp<Camera2Heap> captureHeap;
+
+ CpuConsumer::LockedBuffer imgBuffer;
+
+ res = mCaptureConsumer->lockNextBuffer(&imgBuffer);
+ if (res != OK) {
+ if (res != BAD_VALUE) {
+ ALOGE("%s: Camera %d: Error receiving still image buffer: "
+ "%s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ }
+ return res;
+ }
+
+ ALOGV("%s: Camera %d: Still capture available", __FUNCTION__,
+ client->getCameraId());
+
+ if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) {
+ ALOGE("%s: Camera %d: Unexpected format for still image: "
+ "%x, expected %x", __FUNCTION__, client->getCameraId(),
+ imgBuffer.format,
+ HAL_PIXEL_FORMAT_BLOB);
+ mCaptureConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
+ // Find size of JPEG image
+ size_t jpegSize = findJpegSize(imgBuffer.data, imgBuffer.width);
+ if (jpegSize == 0) { // failed to find size, default to whole buffer
+ jpegSize = imgBuffer.width;
+ }
+ size_t heapSize = mCaptureHeap->getSize();
+ if (jpegSize > heapSize) {
+ ALOGW("%s: JPEG image is larger than expected, truncating "
+ "(got %d, expected at most %d bytes)",
+ __FUNCTION__, jpegSize, heapSize);
+ jpegSize = heapSize;
+ }
+
+ // TODO: Optimize this to avoid memcopy
+ sp<MemoryBase> captureBuffer = new MemoryBase(mCaptureHeap, 0, jpegSize);
+ void* captureMemory = mCaptureHeap->getBase();
+ memcpy(captureMemory, imgBuffer.data, jpegSize);
+
+ mCaptureConsumer->unlockBuffer(imgBuffer);
+
+ sp<CaptureSequencer> sequencer = mSequencer.promote();
+ if (sequencer != 0) {
+ sequencer->onCaptureAvailable(imgBuffer.timestamp, captureBuffer);
+ }
+
+ return OK;
+}
+
+/*
+ * JPEG FILE FORMAT OVERVIEW.
+ * http://www.jpeg.org/public/jfif.pdf
+ * (JPEG is the image compression algorithm, actual file format is called JFIF)
+ *
+ * "Markers" are 2-byte patterns used to distinguish parts of JFIF files. The
+ * first byte is always 0xFF, and the second byte is between 0x01 and 0xFE
+ * (inclusive). Because every marker begins with the same byte, they are
+ * referred to by the second byte's value.
+ *
+ * JFIF files all begin with the Start of Image (SOI) marker, which is 0xD8.
+ * Following it, "segment" sections begin with other markers, followed by a
+ * 2-byte length (in network byte order), then the segment data.
+ *
+ * For our purposes we will ignore the data, and just use the length to skip to
+ * the next segment. This is necessary because the data inside segments are
+ * allowed to contain the End of Image marker (0xFF 0xD9), preventing us from
+ * naievely scanning until the end.
+ *
+ * After all the segments are processed, the jpeg compressed image stream begins.
+ * This can be considered an opaque format with one requirement: all 0xFF bytes
+ * in this stream must be followed with a 0x00 byte. This prevents any of the
+ * image data to be interpreted as a segment. The only exception to this is at
+ * the end of the image stream there is an End of Image (EOI) marker, which is
+ * 0xFF followed by a non-zero (0xD9) byte.
+ */
+
+const uint8_t MARK = 0xFF; // First byte of marker
+const uint8_t SOI = 0xD8; // Start of Image
+const uint8_t EOI = 0xD9; // End of Image
+const size_t MARKER_LENGTH = 2; // length of a marker
+
+#pragma pack(push)
+#pragma pack(1)
+typedef struct segment {
+ uint8_t marker[MARKER_LENGTH];
+ uint16_t length;
+} segment_t;
+#pragma pack(pop)
+
+/* HELPER FUNCTIONS */
+
+// check for Start of Image marker
+bool checkJpegStart(uint8_t* buf) {
+ return buf[0] == MARK && buf[1] == SOI;
+}
+// check for End of Image marker
+bool checkJpegEnd(uint8_t *buf) {
+ return buf[0] == MARK && buf[1] == EOI;
+}
+// check for arbitrary marker, returns marker type (second byte)
+// returns 0 if no marker found. Note: 0x00 is not a valid marker type
+uint8_t checkJpegMarker(uint8_t *buf) {
+ if (buf[0] == MARK && buf[1] > 0 && buf[1] < 0xFF) {
+ return buf[1];
+ }
+ return 0;
+}
+
+// Return the size of the JPEG, 0 indicates failure
+size_t JpegProcessor::findJpegSize(uint8_t* jpegBuffer, size_t maxSize) {
+ size_t size;
+
+ // First check for JPEG transport header at the end of the buffer
+ uint8_t *header = jpegBuffer + (maxSize - sizeof(struct camera2_jpeg_blob));
+ struct camera2_jpeg_blob *blob = (struct camera2_jpeg_blob*)(header);
+ if (blob->jpeg_blob_id == CAMERA2_JPEG_BLOB_ID) {
+ size = blob->jpeg_size;
+ if (size > 0 && size <= maxSize - sizeof(struct camera2_jpeg_blob)) {
+ // Verify SOI and EOI markers
+ size_t offset = size - MARKER_LENGTH;
+ uint8_t *end = jpegBuffer + offset;
+ if (checkJpegStart(jpegBuffer) && checkJpegEnd(end)) {
+ ALOGV("Found JPEG transport header, img size %d", size);
+ return size;
+ } else {
+ ALOGW("Found JPEG transport header with bad Image Start/End");
+ }
+ } else {
+ ALOGW("Found JPEG transport header with bad size %d", size);
+ }
+ }
+
+ // Check Start of Image
+ if ( !checkJpegStart(jpegBuffer) ) {
+ ALOGE("Could not find start of JPEG marker");
+ return 0;
+ }
+
+ // Read JFIF segment markers, skip over segment data
+ size = 0;
+ while (size <= maxSize - MARKER_LENGTH) {
+ segment_t *segment = (segment_t*)(jpegBuffer + size);
+ uint8_t type = checkJpegMarker(segment->marker);
+ if (type == 0) { // invalid marker, no more segments, begin JPEG data
+ ALOGV("JPEG stream found beginning at offset %d", size);
+ break;
+ }
+ if (type == EOI || size > maxSize - sizeof(segment_t)) {
+ ALOGE("Got premature End before JPEG data, offset %d", size);
+ return 0;
+ }
+ size_t length = ntohs(segment->length);
+ ALOGV("JFIF Segment, type %x length %x", type, length);
+ size += length + MARKER_LENGTH;
+ }
+
+ // Find End of Image
+ // Scan JPEG buffer until End of Image (EOI)
+ bool foundEnd = false;
+ for (size; size <= maxSize - MARKER_LENGTH; size++) {
+ if ( checkJpegEnd(jpegBuffer + size) ) {
+ foundEnd = true;
+ size += MARKER_LENGTH;
+ break;
+ }
+ }
+ if (!foundEnd) {
+ ALOGE("Could not find end of JPEG marker");
+ return 0;
+ }
+
+ if (size > maxSize) {
+ ALOGW("JPEG size %d too large, reducing to maxSize %d", size, maxSize);
+ size = maxSize;
+ }
+ ALOGV("Final JPEG size %d", size);
+ return size;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
new file mode 100644
index 00000000..836bd02b
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_JPEGPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_JPEGPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+#include <gui/CpuConsumer.h>
+#include "Parameters.h"
+#include "CameraMetadata.h"
+
+namespace android {
+
+class Camera2Client;
+class MemoryHeapBase;
+
+namespace camera2 {
+
+class CaptureSequencer;
+
+/***
+ * Still image capture output image processing
+ */
+class JpegProcessor:
+ public Thread, public CpuConsumer::FrameAvailableListener {
+ public:
+ JpegProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
+ ~JpegProcessor();
+
+ void onFrameAvailable();
+
+ status_t updateStream(const Parameters &params);
+ status_t deleteStream();
+ int getStreamId() const;
+
+ void dump(int fd, const Vector<String16>& args) const;
+ private:
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ wp<Camera2Client> mClient;
+ wp<CaptureSequencer> mSequencer;
+
+ mutable Mutex mInputMutex;
+ bool mCaptureAvailable;
+ Condition mCaptureAvailableSignal;
+
+ enum {
+ NO_STREAM = -1
+ };
+
+ int mCaptureStreamId;
+ sp<CpuConsumer> mCaptureConsumer;
+ sp<ANativeWindow> mCaptureWindow;
+ sp<MemoryHeapBase> mCaptureHeap;
+
+ virtual bool threadLoop();
+
+ status_t processNewCapture(sp<Camera2Client> &client);
+ size_t findJpegSize(uint8_t* jpegBuffer, size_t maxSize);
+
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
new file mode 100644
index 00000000..278a19cb
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -0,0 +1,2431 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-Parameters"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+#include <utils/SortedVector.h>
+
+#include <math.h>
+#include <stdlib.h>
+#include <cutils/properties.h>
+
+#include "Parameters.h"
+#include "system/camera.h"
+
+namespace android {
+namespace camera2 {
+
+Parameters::Parameters(int cameraId,
+ int cameraFacing) :
+ cameraId(cameraId),
+ cameraFacing(cameraFacing),
+ info(NULL) {
+}
+
+Parameters::~Parameters() {
+}
+
+status_t Parameters::initialize(const CameraMetadata *info) {
+ status_t res;
+
+ if (info->entryCount() == 0) {
+ ALOGE("%s: No static information provided!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ Parameters::info = info;
+
+ res = buildFastInfo();
+ if (res != OK) return res;
+
+ res = buildQuirks();
+ if (res != OK) return res;
+
+ camera_metadata_ro_entry_t availableProcessedSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, 2);
+ if (!availableProcessedSizes.count) return NO_INIT;
+
+ // TODO: Pick more intelligently
+ previewWidth = availableProcessedSizes.data.i32[0];
+ previewHeight = availableProcessedSizes.data.i32[1];
+ videoWidth = previewWidth;
+ videoHeight = previewHeight;
+
+ params.setPreviewSize(previewWidth, previewHeight);
+ params.setVideoSize(videoWidth, videoHeight);
+ params.set(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO,
+ String8::format("%dx%d",
+ previewWidth, previewHeight));
+ {
+ String8 supportedPreviewSizes;
+ for (size_t i=0; i < availableProcessedSizes.count; i += 2) {
+ if (i != 0) supportedPreviewSizes += ",";
+ supportedPreviewSizes += String8::format("%dx%d",
+ availableProcessedSizes.data.i32[i],
+ availableProcessedSizes.data.i32[i+1]);
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
+ supportedPreviewSizes);
+ params.set(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES,
+ supportedPreviewSizes);
+ }
+
+ camera_metadata_ro_entry_t availableFpsRanges =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
+ if (!availableFpsRanges.count) return NO_INIT;
+
+ previewFpsRange[0] = availableFpsRanges.data.i32[0];
+ previewFpsRange[1] = availableFpsRanges.data.i32[1];
+
+ params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
+ String8::format("%d,%d",
+ previewFpsRange[0] * kFpsToApiScale,
+ previewFpsRange[1] * kFpsToApiScale));
+
+ {
+ String8 supportedPreviewFpsRange;
+ for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+ if (i != 0) supportedPreviewFpsRange += ",";
+ supportedPreviewFpsRange += String8::format("(%d,%d)",
+ availableFpsRanges.data.i32[i] * kFpsToApiScale,
+ availableFpsRanges.data.i32[i+1] * kFpsToApiScale);
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE,
+ supportedPreviewFpsRange);
+ }
+
+ previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP;
+ params.set(CameraParameters::KEY_PREVIEW_FORMAT,
+ formatEnumToString(previewFormat)); // NV21
+
+ previewTransform = degToTransform(0,
+ cameraFacing == CAMERA_FACING_FRONT);
+
+ camera_metadata_ro_entry_t availableFormats =
+ staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
+
+ {
+ String8 supportedPreviewFormats;
+ bool addComma = false;
+ for (size_t i=0; i < availableFormats.count; i++) {
+ if (addComma) supportedPreviewFormats += ",";
+ addComma = true;
+ switch (availableFormats.data.i32[i]) {
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_YUV422SP;
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_YUV420SP;
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_YUV422I;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_YUV420P;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_RGB565;
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ supportedPreviewFormats +=
+ CameraParameters::PIXEL_FORMAT_RGBA8888;
+ break;
+ // Not advertizing JPEG, RAW_SENSOR, etc, for preview formats
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ case HAL_PIXEL_FORMAT_BLOB:
+ addComma = false;
+ break;
+
+ default:
+ ALOGW("%s: Camera %d: Unknown preview format: %x",
+ __FUNCTION__, cameraId, availableFormats.data.i32[i]);
+ addComma = false;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS,
+ supportedPreviewFormats);
+ }
+
+ // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but
+ // still have to do something sane for them
+
+ // NOTE: Not scaled like FPS range values are.
+ previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
+ params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
+ previewFps);
+
+ {
+ SortedVector<int32_t> sortedPreviewFrameRates;
+
+ String8 supportedPreviewFrameRates;
+ for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+ // from the [min, max] fps range use the max value
+ int fps = fpsFromRange(availableFpsRanges.data.i32[i],
+ availableFpsRanges.data.i32[i+1]);
+
+ // de-dupe frame rates
+ if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) {
+ sortedPreviewFrameRates.add(fps);
+ }
+ else {
+ continue;
+ }
+
+ if (sortedPreviewFrameRates.size() > 1) {
+ supportedPreviewFrameRates += ",";
+ }
+
+ supportedPreviewFrameRates += String8::format("%d",
+ fps);
+
+ ALOGV("%s: Supported preview frame rates: %s",
+ __FUNCTION__, supportedPreviewFrameRates.string());
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES,
+ supportedPreviewFrameRates);
+ }
+
+ camera_metadata_ro_entry_t availableJpegSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, 2);
+ if (!availableJpegSizes.count) return NO_INIT;
+
+ // TODO: Pick maximum
+ pictureWidth = availableJpegSizes.data.i32[0];
+ pictureHeight = availableJpegSizes.data.i32[1];
+
+ params.setPictureSize(pictureWidth,
+ pictureHeight);
+
+ {
+ String8 supportedPictureSizes;
+ for (size_t i=0; i < availableJpegSizes.count; i += 2) {
+ if (i != 0) supportedPictureSizes += ",";
+ supportedPictureSizes += String8::format("%dx%d",
+ availableJpegSizes.data.i32[i],
+ availableJpegSizes.data.i32[i+1]);
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES,
+ supportedPictureSizes);
+ }
+
+ params.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
+ params.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
+ CameraParameters::PIXEL_FORMAT_JPEG);
+
+ camera_metadata_ro_entry_t availableJpegThumbnailSizes =
+ staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, 4);
+ if (!availableJpegThumbnailSizes.count) return NO_INIT;
+
+ // TODO: Pick default thumbnail size sensibly
+ jpegThumbSize[0] = availableJpegThumbnailSizes.data.i32[0];
+ jpegThumbSize[1] = availableJpegThumbnailSizes.data.i32[1];
+
+ params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
+ jpegThumbSize[0]);
+ params.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT,
+ jpegThumbSize[1]);
+
+ {
+ String8 supportedJpegThumbSizes;
+ for (size_t i=0; i < availableJpegThumbnailSizes.count; i += 2) {
+ if (i != 0) supportedJpegThumbSizes += ",";
+ supportedJpegThumbSizes += String8::format("%dx%d",
+ availableJpegThumbnailSizes.data.i32[i],
+ availableJpegThumbnailSizes.data.i32[i+1]);
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES,
+ supportedJpegThumbSizes);
+ }
+
+ jpegThumbQuality = 90;
+ params.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY,
+ jpegThumbQuality);
+ jpegQuality = 90;
+ params.set(CameraParameters::KEY_JPEG_QUALITY,
+ jpegQuality);
+ jpegRotation = 0;
+ params.set(CameraParameters::KEY_ROTATION,
+ jpegRotation);
+
+ gpsEnabled = false;
+ gpsCoordinates[0] = 0.0;
+ gpsCoordinates[1] = 0.0;
+ gpsCoordinates[2] = 0.0;
+ gpsTimestamp = 0;
+ gpsProcessingMethod = "unknown";
+ // GPS fields in CameraParameters are not set by implementation
+
+ wbMode = ANDROID_CONTROL_AWB_MODE_AUTO;
+ params.set(CameraParameters::KEY_WHITE_BALANCE,
+ CameraParameters::WHITE_BALANCE_AUTO);
+
+ camera_metadata_ro_entry_t availableWhiteBalanceModes =
+ staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
+ {
+ String8 supportedWhiteBalance;
+ bool addComma = false;
+ for (size_t i=0; i < availableWhiteBalanceModes.count; i++) {
+ if (addComma) supportedWhiteBalance += ",";
+ addComma = true;
+ switch (availableWhiteBalanceModes.data.u8[i]) {
+ case ANDROID_CONTROL_AWB_MODE_AUTO:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_AUTO;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_INCANDESCENT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_INCANDESCENT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_FLUORESCENT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_FLUORESCENT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_DAYLIGHT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_DAYLIGHT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_TWILIGHT:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_TWILIGHT;
+ break;
+ case ANDROID_CONTROL_AWB_MODE_SHADE:
+ supportedWhiteBalance +=
+ CameraParameters::WHITE_BALANCE_SHADE;
+ break;
+ // Skipping values not mappable to v1 API
+ case ANDROID_CONTROL_AWB_MODE_OFF:
+ addComma = false;
+ break;
+ default:
+ ALOGW("%s: Camera %d: Unknown white balance value: %d",
+ __FUNCTION__, cameraId,
+ availableWhiteBalanceModes.data.u8[i]);
+ addComma = false;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE,
+ supportedWhiteBalance);
+ }
+
+ effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF;
+ params.set(CameraParameters::KEY_EFFECT,
+ CameraParameters::EFFECT_NONE);
+
+ camera_metadata_ro_entry_t availableEffects =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS);
+ if (!availableEffects.count) return NO_INIT;
+ {
+ String8 supportedEffects;
+ bool addComma = false;
+ for (size_t i=0; i < availableEffects.count; i++) {
+ if (addComma) supportedEffects += ",";
+ addComma = true;
+ switch (availableEffects.data.u8[i]) {
+ case ANDROID_CONTROL_EFFECT_MODE_OFF:
+ supportedEffects +=
+ CameraParameters::EFFECT_NONE;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_MONO:
+ supportedEffects +=
+ CameraParameters::EFFECT_MONO;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_NEGATIVE:
+ supportedEffects +=
+ CameraParameters::EFFECT_NEGATIVE;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_SOLARIZE:
+ supportedEffects +=
+ CameraParameters::EFFECT_SOLARIZE;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_SEPIA:
+ supportedEffects +=
+ CameraParameters::EFFECT_SEPIA;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_POSTERIZE:
+ supportedEffects +=
+ CameraParameters::EFFECT_POSTERIZE;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD:
+ supportedEffects +=
+ CameraParameters::EFFECT_WHITEBOARD;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD:
+ supportedEffects +=
+ CameraParameters::EFFECT_BLACKBOARD;
+ break;
+ case ANDROID_CONTROL_EFFECT_MODE_AQUA:
+ supportedEffects +=
+ CameraParameters::EFFECT_AQUA;
+ break;
+ default:
+ ALOGW("%s: Camera %d: Unknown effect value: %d",
+ __FUNCTION__, cameraId, availableEffects.data.u8[i]);
+ addComma = false;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_EFFECTS, supportedEffects);
+ }
+
+ antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO;
+ params.set(CameraParameters::KEY_ANTIBANDING,
+ CameraParameters::ANTIBANDING_AUTO);
+
+ camera_metadata_ro_entry_t availableAntibandingModes =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES);
+ if (!availableAntibandingModes.count) return NO_INIT;
+ {
+ String8 supportedAntibanding;
+ bool addComma = false;
+ for (size_t i=0; i < availableAntibandingModes.count; i++) {
+ if (addComma) supportedAntibanding += ",";
+ addComma = true;
+ switch (availableAntibandingModes.data.u8[i]) {
+ case ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF:
+ supportedAntibanding +=
+ CameraParameters::ANTIBANDING_OFF;
+ break;
+ case ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ:
+ supportedAntibanding +=
+ CameraParameters::ANTIBANDING_50HZ;
+ break;
+ case ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ:
+ supportedAntibanding +=
+ CameraParameters::ANTIBANDING_60HZ;
+ break;
+ case ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO:
+ supportedAntibanding +=
+ CameraParameters::ANTIBANDING_AUTO;
+ break;
+ default:
+ ALOGW("%s: Camera %d: Unknown antibanding value: %d",
+ __FUNCTION__, cameraId,
+ availableAntibandingModes.data.u8[i]);
+ addComma = false;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING,
+ supportedAntibanding);
+ }
+
+ sceneMode = ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ params.set(CameraParameters::KEY_SCENE_MODE,
+ CameraParameters::SCENE_MODE_AUTO);
+
+ camera_metadata_ro_entry_t availableSceneModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
+ if (!availableSceneModes.count) return NO_INIT;
+ {
+ String8 supportedSceneModes(CameraParameters::SCENE_MODE_AUTO);
+ bool addComma = true;
+ bool noSceneModes = false;
+ for (size_t i=0; i < availableSceneModes.count; i++) {
+ if (addComma) supportedSceneModes += ",";
+ addComma = true;
+ switch (availableSceneModes.data.u8[i]) {
+ case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED:
+ noSceneModes = true;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY:
+ // Not in old API
+ addComma = false;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_ACTION:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_ACTION;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_PORTRAIT:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_PORTRAIT;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_LANDSCAPE:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_LANDSCAPE;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_NIGHT:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_NIGHT;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_NIGHT_PORTRAIT;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_THEATRE:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_THEATRE;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_BEACH:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_BEACH;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_SNOW:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_SNOW;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_SUNSET:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_SUNSET;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_STEADYPHOTO;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_FIREWORKS:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_FIREWORKS;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_SPORTS:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_SPORTS;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_PARTY:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_PARTY;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_CANDLELIGHT;
+ break;
+ case ANDROID_CONTROL_SCENE_MODE_BARCODE:
+ supportedSceneModes +=
+ CameraParameters::SCENE_MODE_BARCODE;
+ break;
+ default:
+ ALOGW("%s: Camera %d: Unknown scene mode value: %d",
+ __FUNCTION__, cameraId,
+ availableSceneModes.data.u8[i]);
+ addComma = false;
+ break;
+ }
+ }
+ if (!noSceneModes) {
+ params.set(CameraParameters::KEY_SUPPORTED_SCENE_MODES,
+ supportedSceneModes);
+ }
+ }
+
+ camera_metadata_ro_entry_t flashAvailable =
+ staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 1, 1);
+ if (!flashAvailable.count) return NO_INIT;
+
+ camera_metadata_ro_entry_t availableAeModes =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES);
+ if (!availableAeModes.count) return NO_INIT;
+
+ if (flashAvailable.data.u8[0]) {
+ flashMode = Parameters::FLASH_MODE_OFF;
+ params.set(CameraParameters::KEY_FLASH_MODE,
+ CameraParameters::FLASH_MODE_OFF);
+
+ String8 supportedFlashModes(CameraParameters::FLASH_MODE_OFF);
+ supportedFlashModes = supportedFlashModes +
+ "," + CameraParameters::FLASH_MODE_AUTO +
+ "," + CameraParameters::FLASH_MODE_ON +
+ "," + CameraParameters::FLASH_MODE_TORCH;
+ for (size_t i=0; i < availableAeModes.count; i++) {
+ if (availableAeModes.data.u8[i] ==
+ ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
+ supportedFlashModes = supportedFlashModes + "," +
+ CameraParameters::FLASH_MODE_RED_EYE;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES,
+ supportedFlashModes);
+ } else {
+ flashMode = Parameters::FLASH_MODE_OFF;
+ params.set(CameraParameters::KEY_FLASH_MODE,
+ CameraParameters::FLASH_MODE_OFF);
+ params.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES,
+ CameraParameters::FLASH_MODE_OFF);
+ }
+
+ camera_metadata_ro_entry_t minFocusDistance =
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 1, 1);
+ if (!minFocusDistance.count) return NO_INIT;
+
+ camera_metadata_ro_entry_t availableAfModes =
+ staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES);
+ if (!availableAfModes.count) return NO_INIT;
+
+ if (minFocusDistance.data.f[0] == 0) {
+ // Fixed-focus lens
+ focusMode = Parameters::FOCUS_MODE_FIXED;
+ params.set(CameraParameters::KEY_FOCUS_MODE,
+ CameraParameters::FOCUS_MODE_FIXED);
+ params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
+ CameraParameters::FOCUS_MODE_FIXED);
+ } else {
+ focusMode = Parameters::FOCUS_MODE_AUTO;
+ params.set(CameraParameters::KEY_FOCUS_MODE,
+ CameraParameters::FOCUS_MODE_AUTO);
+ String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY);
+ bool addComma = true;
+
+ for (size_t i=0; i < availableAfModes.count; i++) {
+ if (addComma) supportedFocusModes += ",";
+ addComma = true;
+ switch (availableAfModes.data.u8[i]) {
+ case ANDROID_CONTROL_AF_MODE_AUTO:
+ supportedFocusModes +=
+ CameraParameters::FOCUS_MODE_AUTO;
+ break;
+ case ANDROID_CONTROL_AF_MODE_MACRO:
+ supportedFocusModes +=
+ CameraParameters::FOCUS_MODE_MACRO;
+ break;
+ case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+ supportedFocusModes +=
+ CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
+ break;
+ case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+ supportedFocusModes +=
+ CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
+ break;
+ case ANDROID_CONTROL_AF_MODE_EDOF:
+ supportedFocusModes +=
+ CameraParameters::FOCUS_MODE_EDOF;
+ break;
+ // Not supported in old API
+ case ANDROID_CONTROL_AF_MODE_OFF:
+ addComma = false;
+ break;
+ default:
+ ALOGW("%s: Camera %d: Unknown AF mode value: %d",
+ __FUNCTION__, cameraId, availableAfModes.data.u8[i]);
+ addComma = false;
+ break;
+ }
+ }
+ params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
+ supportedFocusModes);
+ }
+ focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+ shadowFocusMode = FOCUS_MODE_INVALID;
+
+ camera_metadata_ro_entry_t max3aRegions =
+ staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1);
+ if (!max3aRegions.count) return NO_INIT;
+
+ int32_t maxNumFocusAreas = 0;
+ if (focusMode != Parameters::FOCUS_MODE_FIXED) {
+ maxNumFocusAreas = max3aRegions.data.i32[0];
+ }
+ params.set(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS, maxNumFocusAreas);
+ params.set(CameraParameters::KEY_FOCUS_AREAS,
+ "(0,0,0,0,0)");
+ focusingAreas.clear();
+ focusingAreas.add(Parameters::Area(0,0,0,0,0));
+
+ camera_metadata_ro_entry_t availableFocalLengths =
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
+ if (!availableFocalLengths.count) return NO_INIT;
+
+ float minFocalLength = availableFocalLengths.data.f[0];
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
+
+ camera_metadata_ro_entry_t sensorSize =
+ staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
+ if (!sensorSize.count) return NO_INIT;
+
+ // The fields of view here assume infinity focus, maximum wide angle
+ float horizFov = 180 / M_PI *
+ 2 * atanf(sensorSize.data.f[0] / (2 * minFocalLength));
+ float vertFov = 180 / M_PI *
+ 2 * atanf(sensorSize.data.f[1] / (2 * minFocalLength));
+ params.setFloat(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, horizFov);
+ params.setFloat(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, vertFov);
+
+ exposureCompensation = 0;
+ params.set(CameraParameters::KEY_EXPOSURE_COMPENSATION,
+ exposureCompensation);
+
+ camera_metadata_ro_entry_t exposureCompensationRange =
+ staticInfo(ANDROID_CONTROL_AE_COMPENSATION_RANGE, 2, 2);
+ if (!exposureCompensationRange.count) return NO_INIT;
+
+ params.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION,
+ exposureCompensationRange.data.i32[1]);
+ params.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION,
+ exposureCompensationRange.data.i32[0]);
+
+ camera_metadata_ro_entry_t exposureCompensationStep =
+ staticInfo(ANDROID_CONTROL_AE_COMPENSATION_STEP, 1, 1);
+ if (!exposureCompensationStep.count) return NO_INIT;
+
+ params.setFloat(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP,
+ (float)exposureCompensationStep.data.r[0].numerator /
+ exposureCompensationStep.data.r[0].denominator);
+
+ autoExposureLock = false;
+ params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK,
+ CameraParameters::FALSE);
+ params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED,
+ CameraParameters::TRUE);
+
+ autoWhiteBalanceLock = false;
+ params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK,
+ CameraParameters::FALSE);
+ params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED,
+ CameraParameters::TRUE);
+
+ meteringAreas.add(Parameters::Area(0, 0, 0, 0, 0));
+ params.set(CameraParameters::KEY_MAX_NUM_METERING_AREAS,
+ max3aRegions.data.i32[0]);
+ params.set(CameraParameters::KEY_METERING_AREAS,
+ "(0,0,0,0,0)");
+
+ zoom = 0;
+ params.set(CameraParameters::KEY_ZOOM, zoom);
+ params.set(CameraParameters::KEY_MAX_ZOOM, NUM_ZOOM_STEPS - 1);
+
+ camera_metadata_ro_entry_t maxDigitalZoom =
+ staticInfo(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, /*minCount*/1, /*maxCount*/1);
+ if (!maxDigitalZoom.count) return NO_INIT;
+
+ {
+ String8 zoomRatios;
+ float zoom = 1.f;
+ float zoomIncrement = (maxDigitalZoom.data.f[0] - zoom) /
+ (NUM_ZOOM_STEPS-1);
+ bool addComma = false;
+ for (size_t i=0; i < NUM_ZOOM_STEPS; i++) {
+ if (addComma) zoomRatios += ",";
+ addComma = true;
+ zoomRatios += String8::format("%d", static_cast<int>(zoom * 100));
+ zoom += zoomIncrement;
+ }
+ params.set(CameraParameters::KEY_ZOOM_RATIOS, zoomRatios);
+ }
+
+ params.set(CameraParameters::KEY_ZOOM_SUPPORTED,
+ CameraParameters::TRUE);
+ params.set(CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED,
+ CameraParameters::FALSE);
+
+ params.set(CameraParameters::KEY_FOCUS_DISTANCES,
+ "Infinity,Infinity,Infinity");
+
+ params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW,
+ fastInfo.maxFaces);
+ params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW,
+ 0);
+
+ params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
+ CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE);
+
+ recordingHint = false;
+ params.set(CameraParameters::KEY_RECORDING_HINT,
+ CameraParameters::FALSE);
+
+ params.set(CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED,
+ CameraParameters::TRUE);
+
+ videoStabilization = false;
+ params.set(CameraParameters::KEY_VIDEO_STABILIZATION,
+ CameraParameters::FALSE);
+
+ camera_metadata_ro_entry_t availableVideoStabilizationModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
+ if (!availableVideoStabilizationModes.count) return NO_INIT;
+
+ if (availableVideoStabilizationModes.count > 1) {
+ params.set(CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED,
+ CameraParameters::TRUE);
+ } else {
+ params.set(CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED,
+ CameraParameters::FALSE);
+ }
+
+ // Set up initial state for non-Camera.Parameters state variables
+
+ storeMetadataInBuffers = true;
+ playShutterSound = true;
+ enableFaceDetect = false;
+
+ enableFocusMoveMessages = false;
+ afTriggerCounter = 1;
+ currentAfTriggerId = -1;
+ afInMotion = false;
+
+ precaptureTriggerCounter = 1;
+
+ previewCallbackFlags = 0;
+ previewCallbackOneShot = false;
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get("camera.disable_zsl_mode", value, "0");
+ if (!strcmp(value,"1")) {
+ ALOGI("Camera %d: Disabling ZSL mode", cameraId);
+ zslMode = false;
+ } else {
+ zslMode = true;
+ }
+
+ lightFx = LIGHTFX_NONE;
+
+ state = STOPPED;
+
+ paramsFlattened = params.flatten();
+
+ return OK;
+}
+
+String8 Parameters::get() const {
+ return paramsFlattened;
+}
+
+status_t Parameters::buildFastInfo() {
+
+ camera_metadata_ro_entry_t activeArraySize =
+ staticInfo(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, 2, 2);
+ if (!activeArraySize.count) return NO_INIT;
+ int32_t arrayWidth = activeArraySize.data.i32[0];
+ int32_t arrayHeight = activeArraySize.data.i32[1];
+
+ camera_metadata_ro_entry_t availableFaceDetectModes =
+ staticInfo(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
+ if (!availableFaceDetectModes.count) return NO_INIT;
+
+ uint8_t bestFaceDetectMode =
+ ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ for (size_t i = 0 ; i < availableFaceDetectModes.count; i++) {
+ switch (availableFaceDetectModes.data.u8[i]) {
+ case ANDROID_STATISTICS_FACE_DETECT_MODE_OFF:
+ break;
+ case ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE:
+ if (bestFaceDetectMode !=
+ ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) {
+ bestFaceDetectMode =
+ ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE;
+ }
+ break;
+ case ANDROID_STATISTICS_FACE_DETECT_MODE_FULL:
+ bestFaceDetectMode =
+ ANDROID_STATISTICS_FACE_DETECT_MODE_FULL;
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unknown face detect mode %d:",
+ __FUNCTION__, cameraId,
+ availableFaceDetectModes.data.u8[i]);
+ return NO_INIT;
+ }
+ }
+
+ camera_metadata_ro_entry_t maxFacesDetected =
+ staticInfo(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, 1, 1);
+ if (!maxFacesDetected.count) return NO_INIT;
+
+ int32_t maxFaces = maxFacesDetected.data.i32[0];
+
+ camera_metadata_ro_entry_t availableSceneModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
+ camera_metadata_ro_entry_t sceneModeOverrides =
+ staticInfo(ANDROID_CONTROL_SCENE_MODE_OVERRIDES);
+ camera_metadata_ro_entry_t minFocusDistance =
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+ bool fixedLens = (minFocusDistance.data.f[0] == 0);
+
+ if (sceneModeOverrides.count > 0) {
+ // sceneModeOverrides is defined to have 3 entries for each scene mode,
+ // which are AE, AWB, and AF override modes the HAL wants for that scene
+ // mode.
+ const size_t kModesPerSceneMode = 3;
+ if (sceneModeOverrides.count !=
+ availableSceneModes.count * kModesPerSceneMode) {
+ ALOGE("%s: Camera %d: Scene mode override list is an "
+ "unexpected size: %d (expected %d)", __FUNCTION__,
+ cameraId, sceneModeOverrides.count,
+ availableSceneModes.count);
+ return NO_INIT;
+ }
+ for (size_t i = 0; i < availableSceneModes.count; i++) {
+ DeviceInfo::OverrideModes modes;
+ uint8_t aeMode =
+ sceneModeOverrides.data.u8[i * kModesPerSceneMode + 0];
+ switch(aeMode) {
+ case ANDROID_CONTROL_AE_MODE_ON:
+ modes.flashMode = FLASH_MODE_OFF;
+ break;
+ case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH:
+ modes.flashMode = FLASH_MODE_AUTO;
+ break;
+ case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+ modes.flashMode = FLASH_MODE_ON;
+ break;
+ case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+ modes.flashMode = FLASH_MODE_RED_EYE;
+ break;
+ default:
+ ALOGE("%s: Unknown override AE mode: %d", __FUNCTION__,
+ aeMode);
+ modes.flashMode = FLASH_MODE_INVALID;
+ break;
+ }
+ modes.wbMode =
+ sceneModeOverrides.data.u8[i * kModesPerSceneMode + 1];
+ uint8_t afMode =
+ sceneModeOverrides.data.u8[i * kModesPerSceneMode + 2];
+ switch(afMode) {
+ case ANDROID_CONTROL_AF_MODE_OFF:
+ modes.focusMode = fixedLens ?
+ FOCUS_MODE_FIXED : FOCUS_MODE_INFINITY;
+ break;
+ case ANDROID_CONTROL_AF_MODE_AUTO:
+ case ANDROID_CONTROL_AF_MODE_MACRO:
+ case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+ case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+ case ANDROID_CONTROL_AF_MODE_EDOF:
+ modes.focusMode = static_cast<focusMode_t>(afMode);
+ break;
+ default:
+ ALOGE("%s: Unknown override AF mode: %d", __FUNCTION__,
+ afMode);
+ modes.focusMode = FOCUS_MODE_INVALID;
+ break;
+ }
+ fastInfo.sceneModeOverrides.add(availableSceneModes.data.u8[i],
+ modes);
+ }
+ }
+
+ fastInfo.arrayWidth = arrayWidth;
+ fastInfo.arrayHeight = arrayHeight;
+ fastInfo.bestFaceDetectMode = bestFaceDetectMode;
+ fastInfo.maxFaces = maxFaces;
+ return OK;
+}
+
+status_t Parameters::buildQuirks() {
+ camera_metadata_ro_entry_t entry;
+ entry = info->find(ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO);
+ quirks.triggerAfWithAuto = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.triggerAfWithAuto, "Camera %d: Quirk triggerAfWithAuto enabled",
+ cameraId);
+
+ entry = info->find(ANDROID_QUIRKS_USE_ZSL_FORMAT);
+ quirks.useZslFormat = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.useZslFormat, "Camera %d: Quirk useZslFormat enabled",
+ cameraId);
+
+ entry = info->find(ANDROID_QUIRKS_METERING_CROP_REGION);
+ quirks.meteringCropRegion = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion"
+ " enabled", cameraId);
+
+ return OK;
+}
+
+camera_metadata_ro_entry_t Parameters::staticInfo(uint32_t tag,
+ size_t minCount, size_t maxCount) const {
+ status_t res;
+ camera_metadata_ro_entry_t entry = info->find(tag);
+
+ if (CC_UNLIKELY( entry.count == 0 )) {
+ const char* tagSection = get_camera_metadata_section_name(tag);
+ if (tagSection == NULL) tagSection = "<unknown>";
+ const char* tagName = get_camera_metadata_tag_name(tag);
+ if (tagName == NULL) tagName = "<unknown>";
+
+ ALOGE("Error finding static metadata entry '%s.%s' (%x)",
+ tagSection, tagName, tag);
+ } else if (CC_UNLIKELY(
+ (minCount != 0 && entry.count < minCount) ||
+ (maxCount != 0 && entry.count > maxCount) ) ) {
+ const char* tagSection = get_camera_metadata_section_name(tag);
+ if (tagSection == NULL) tagSection = "<unknown>";
+ const char* tagName = get_camera_metadata_tag_name(tag);
+ if (tagName == NULL) tagName = "<unknown>";
+ ALOGE("Malformed static metadata entry '%s.%s' (%x):"
+ "Expected between %d and %d values, but got %d values",
+ tagSection, tagName, tag, minCount, maxCount, entry.count);
+ }
+
+ return entry;
+}
+
+status_t Parameters::set(const String8& paramString) {
+ status_t res;
+
+ CameraParameters newParams(paramString);
+
+ // TODO: Currently ignoring any changes to supposedly read-only parameters
+ // such as supported preview sizes, etc. Should probably produce an error if
+ // they're changed.
+
+ /** Extract and verify new parameters */
+
+ size_t i;
+
+ Parameters validatedParams(*this);
+
+ // PREVIEW_SIZE
+ newParams.getPreviewSize(&validatedParams.previewWidth,
+ &validatedParams.previewHeight);
+
+ if (validatedParams.previewWidth != previewWidth ||
+ validatedParams.previewHeight != previewHeight) {
+ if (state >= PREVIEW) {
+ ALOGE("%s: Preview size cannot be updated when preview "
+ "is active! (Currently %d x %d, requested %d x %d",
+ __FUNCTION__,
+ previewWidth, previewHeight,
+ validatedParams.previewWidth, validatedParams.previewHeight);
+ return BAD_VALUE;
+ }
+ camera_metadata_ro_entry_t availablePreviewSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
+ for (i = 0; i < availablePreviewSizes.count; i += 2 ) {
+ if ((availablePreviewSizes.data.i32[i] ==
+ validatedParams.previewWidth) &&
+ (availablePreviewSizes.data.i32[i+1] ==
+ validatedParams.previewHeight)) break;
+ }
+ if (i == availablePreviewSizes.count) {
+ ALOGE("%s: Requested preview size %d x %d is not supported",
+ __FUNCTION__, validatedParams.previewWidth,
+ validatedParams.previewHeight);
+ return BAD_VALUE;
+ }
+ }
+
+ // RECORDING_HINT (always supported)
+ validatedParams.recordingHint = boolFromString(
+ newParams.get(CameraParameters::KEY_RECORDING_HINT) );
+ bool recordingHintChanged = validatedParams.recordingHint != recordingHint;
+ ALOGV_IF(recordingHintChanged, "%s: Recording hint changed to %d",
+ __FUNCTION__, recordingHintChanged);
+
+ // PREVIEW_FPS_RANGE
+ bool fpsRangeChanged = false;
+ newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0],
+ &validatedParams.previewFpsRange[1]);
+ validatedParams.previewFpsRange[0] /= kFpsToApiScale;
+ validatedParams.previewFpsRange[1] /= kFpsToApiScale;
+
+ if (validatedParams.previewFpsRange[0] != previewFpsRange[0] ||
+ validatedParams.previewFpsRange[1] != previewFpsRange[1]) {
+ fpsRangeChanged = true;
+ camera_metadata_ro_entry_t availablePreviewFpsRanges =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
+ for (i = 0; i < availablePreviewFpsRanges.count; i += 2) {
+ if ((availablePreviewFpsRanges.data.i32[i] ==
+ validatedParams.previewFpsRange[0]) &&
+ (availablePreviewFpsRanges.data.i32[i+1] ==
+ validatedParams.previewFpsRange[1]) ) {
+ break;
+ }
+ }
+ if (i == availablePreviewFpsRanges.count) {
+ ALOGE("%s: Requested preview FPS range %d - %d is not supported",
+ __FUNCTION__, validatedParams.previewFpsRange[0],
+ validatedParams.previewFpsRange[1]);
+ return BAD_VALUE;
+ }
+ validatedParams.previewFps =
+ fpsFromRange(validatedParams.previewFpsRange[0],
+ validatedParams.previewFpsRange[1]);
+ newParams.setPreviewFrameRate(validatedParams.previewFps);
+ }
+
+ // PREVIEW_FORMAT
+ validatedParams.previewFormat =
+ formatStringToEnum(newParams.getPreviewFormat());
+ if (validatedParams.previewFormat != previewFormat) {
+ if (state >= PREVIEW) {
+ ALOGE("%s: Preview format cannot be updated when preview "
+ "is active!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ camera_metadata_ro_entry_t availableFormats =
+ staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
+ for (i = 0; i < availableFormats.count; i++) {
+ if (availableFormats.data.i32[i] == validatedParams.previewFormat)
+ break;
+ }
+ if (i == availableFormats.count) {
+ ALOGE("%s: Requested preview format %s (0x%x) is not supported",
+ __FUNCTION__, newParams.getPreviewFormat(),
+ validatedParams.previewFormat);
+ return BAD_VALUE;
+ }
+ }
+
+ // PREVIEW_FRAME_RATE
+ // Deprecated, only use if the preview fps range is unchanged this time.
+ // The single-value FPS is the same as the minimum of the range.
+ if (!fpsRangeChanged) {
+ validatedParams.previewFps = newParams.getPreviewFrameRate();
+ if (validatedParams.previewFps != previewFps || recordingHintChanged) {
+ camera_metadata_ro_entry_t availableFrameRates =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+ /**
+ * If recording hint is set, find the range that encompasses
+ * previewFps with the largest min index.
+ *
+ * If recording hint is not set, find the range with previewFps
+ * with the smallest min index.
+ *
+ * Either way, in case of multiple ranges, break the tie by
+ * selecting the smaller range.
+ */
+ int targetFps = validatedParams.previewFps;
+ // all ranges which have targetFps
+ Vector<Range> candidateRanges;
+ for (i = 0; i < availableFrameRates.count; i+=2) {
+ Range r = {
+ availableFrameRates.data.i32[i],
+ availableFrameRates.data.i32[i+1]
+ };
+
+ if (r.min <= targetFps && targetFps <= r.max) {
+ candidateRanges.push(r);
+ }
+ }
+ if (candidateRanges.isEmpty()) {
+ ALOGE("%s: Requested preview frame rate %d is not supported",
+ __FUNCTION__, validatedParams.previewFps);
+ return BAD_VALUE;
+ }
+ // most applicable range with targetFps
+ Range bestRange = candidateRanges[0];
+ for (i = 1; i < candidateRanges.size(); ++i) {
+ Range r = candidateRanges[i];
+
+ // Find by largest minIndex in recording mode
+ if (validatedParams.recordingHint) {
+ if (r.min > bestRange.min) {
+ bestRange = r;
+ }
+ else if (r.min == bestRange.min && r.max < bestRange.max) {
+ bestRange = r;
+ }
+ }
+ // Find by smallest minIndex in preview mode
+ else {
+ if (r.min < bestRange.min) {
+ bestRange = r;
+ }
+ else if (r.min == bestRange.min && r.max < bestRange.max) {
+ bestRange = r;
+ }
+ }
+ }
+
+ validatedParams.previewFpsRange[0] =
+ bestRange.min;
+ validatedParams.previewFpsRange[1] =
+ bestRange.max;
+
+ ALOGV("%s: New preview FPS range: %d, %d, recordingHint = %d",
+ __FUNCTION__,
+ validatedParams.previewFpsRange[0],
+ validatedParams.previewFpsRange[1],
+ validatedParams.recordingHint);
+ }
+ newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
+ String8::format("%d,%d",
+ validatedParams.previewFpsRange[0] * kFpsToApiScale,
+ validatedParams.previewFpsRange[1] * kFpsToApiScale));
+
+ }
+
+ // PICTURE_SIZE
+ newParams.getPictureSize(&validatedParams.pictureWidth,
+ &validatedParams.pictureHeight);
+ if (validatedParams.pictureWidth == pictureWidth ||
+ validatedParams.pictureHeight == pictureHeight) {
+ camera_metadata_ro_entry_t availablePictureSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
+ for (i = 0; i < availablePictureSizes.count; i+=2) {
+ if ((availablePictureSizes.data.i32[i] ==
+ validatedParams.pictureWidth) &&
+ (availablePictureSizes.data.i32[i+1] ==
+ validatedParams.pictureHeight)) break;
+ }
+ if (i == availablePictureSizes.count) {
+ ALOGE("%s: Requested picture size %d x %d is not supported",
+ __FUNCTION__, validatedParams.pictureWidth,
+ validatedParams.pictureHeight);
+ return BAD_VALUE;
+ }
+ }
+
+ // JPEG_THUMBNAIL_WIDTH/HEIGHT
+ validatedParams.jpegThumbSize[0] =
+ newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
+ validatedParams.jpegThumbSize[1] =
+ newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
+ if (validatedParams.jpegThumbSize[0] != jpegThumbSize[0] ||
+ validatedParams.jpegThumbSize[1] != jpegThumbSize[1]) {
+ camera_metadata_ro_entry_t availableJpegThumbSizes =
+ staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for (i = 0; i < availableJpegThumbSizes.count; i+=2) {
+ if ((availableJpegThumbSizes.data.i32[i] ==
+ validatedParams.jpegThumbSize[0]) &&
+ (availableJpegThumbSizes.data.i32[i+1] ==
+ validatedParams.jpegThumbSize[1])) break;
+ }
+ if (i == availableJpegThumbSizes.count) {
+ ALOGE("%s: Requested JPEG thumbnail size %d x %d is not supported",
+ __FUNCTION__, validatedParams.jpegThumbSize[0],
+ validatedParams.jpegThumbSize[1]);
+ return BAD_VALUE;
+ }
+ }
+
+ // JPEG_THUMBNAIL_QUALITY
+ int quality = newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY);
+ // also makes sure quality fits in uint8_t
+ if (quality < 0 || quality > 100) {
+ ALOGE("%s: Requested JPEG thumbnail quality %d is not supported",
+ __FUNCTION__, quality);
+ return BAD_VALUE;
+ }
+ validatedParams.jpegThumbQuality = quality;
+
+ // JPEG_QUALITY
+ quality = newParams.getInt(CameraParameters::KEY_JPEG_QUALITY);
+ // also makes sure quality fits in uint8_t
+ if (quality < 0 || quality > 100) {
+ ALOGE("%s: Requested JPEG quality %d is not supported",
+ __FUNCTION__, quality);
+ return BAD_VALUE;
+ }
+ validatedParams.jpegQuality = quality;
+
+ // ROTATION
+ validatedParams.jpegRotation =
+ newParams.getInt(CameraParameters::KEY_ROTATION);
+ if (validatedParams.jpegRotation != 0 &&
+ validatedParams.jpegRotation != 90 &&
+ validatedParams.jpegRotation != 180 &&
+ validatedParams.jpegRotation != 270) {
+ ALOGE("%s: Requested picture rotation angle %d is not supported",
+ __FUNCTION__, validatedParams.jpegRotation);
+ return BAD_VALUE;
+ }
+
+ // GPS
+
+ const char *gpsLatStr =
+ newParams.get(CameraParameters::KEY_GPS_LATITUDE);
+ if (gpsLatStr != NULL) {
+ const char *gpsLongStr =
+ newParams.get(CameraParameters::KEY_GPS_LONGITUDE);
+ const char *gpsAltitudeStr =
+ newParams.get(CameraParameters::KEY_GPS_ALTITUDE);
+ const char *gpsTimeStr =
+ newParams.get(CameraParameters::KEY_GPS_TIMESTAMP);
+ const char *gpsProcMethodStr =
+ newParams.get(CameraParameters::KEY_GPS_PROCESSING_METHOD);
+ if (gpsLongStr == NULL ||
+ gpsAltitudeStr == NULL ||
+ gpsTimeStr == NULL ||
+ gpsProcMethodStr == NULL) {
+ ALOGE("%s: Incomplete set of GPS parameters provided",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ char *endPtr;
+ errno = 0;
+ validatedParams.gpsCoordinates[0] = strtod(gpsLatStr, &endPtr);
+ if (errno || endPtr == gpsLatStr) {
+ ALOGE("%s: Malformed GPS latitude: %s", __FUNCTION__, gpsLatStr);
+ return BAD_VALUE;
+ }
+ errno = 0;
+ validatedParams.gpsCoordinates[1] = strtod(gpsLongStr, &endPtr);
+ if (errno || endPtr == gpsLongStr) {
+ ALOGE("%s: Malformed GPS longitude: %s", __FUNCTION__, gpsLongStr);
+ return BAD_VALUE;
+ }
+ errno = 0;
+ validatedParams.gpsCoordinates[2] = strtod(gpsAltitudeStr, &endPtr);
+ if (errno || endPtr == gpsAltitudeStr) {
+ ALOGE("%s: Malformed GPS altitude: %s", __FUNCTION__,
+ gpsAltitudeStr);
+ return BAD_VALUE;
+ }
+ errno = 0;
+ validatedParams.gpsTimestamp = strtoll(gpsTimeStr, &endPtr, 10);
+ if (errno || endPtr == gpsTimeStr) {
+ ALOGE("%s: Malformed GPS timestamp: %s", __FUNCTION__, gpsTimeStr);
+ return BAD_VALUE;
+ }
+ validatedParams.gpsProcessingMethod = gpsProcMethodStr;
+
+ validatedParams.gpsEnabled = true;
+ } else {
+ validatedParams.gpsEnabled = false;
+ }
+
+ // EFFECT
+ validatedParams.effectMode = effectModeStringToEnum(
+ newParams.get(CameraParameters::KEY_EFFECT) );
+ if (validatedParams.effectMode != effectMode) {
+ camera_metadata_ro_entry_t availableEffectModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS);
+ for (i = 0; i < availableEffectModes.count; i++) {
+ if (validatedParams.effectMode == availableEffectModes.data.u8[i]) break;
+ }
+ if (i == availableEffectModes.count) {
+ ALOGE("%s: Requested effect mode \"%s\" is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_EFFECT) );
+ return BAD_VALUE;
+ }
+ }
+
+ // ANTIBANDING
+ validatedParams.antibandingMode = abModeStringToEnum(
+ newParams.get(CameraParameters::KEY_ANTIBANDING) );
+ if (validatedParams.antibandingMode != antibandingMode) {
+ camera_metadata_ro_entry_t availableAbModes =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES);
+ for (i = 0; i < availableAbModes.count; i++) {
+ if (validatedParams.antibandingMode == availableAbModes.data.u8[i])
+ break;
+ }
+ if (i == availableAbModes.count) {
+ ALOGE("%s: Requested antibanding mode \"%s\" is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_ANTIBANDING));
+ return BAD_VALUE;
+ }
+ }
+
+ // SCENE_MODE
+ validatedParams.sceneMode = sceneModeStringToEnum(
+ newParams.get(CameraParameters::KEY_SCENE_MODE) );
+ if (validatedParams.sceneMode != sceneMode &&
+ validatedParams.sceneMode !=
+ ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) {
+ camera_metadata_ro_entry_t availableSceneModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
+ for (i = 0; i < availableSceneModes.count; i++) {
+ if (validatedParams.sceneMode == availableSceneModes.data.u8[i])
+ break;
+ }
+ if (i == availableSceneModes.count) {
+ ALOGE("%s: Requested scene mode \"%s\" is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_SCENE_MODE));
+ return BAD_VALUE;
+ }
+ }
+ bool sceneModeSet =
+ validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+
+ // FLASH_MODE
+ if (sceneModeSet) {
+ validatedParams.flashMode =
+ fastInfo.sceneModeOverrides.
+ valueFor(validatedParams.sceneMode).flashMode;
+ } else {
+ validatedParams.flashMode = FLASH_MODE_INVALID;
+ }
+ if (validatedParams.flashMode == FLASH_MODE_INVALID) {
+ validatedParams.flashMode = flashModeStringToEnum(
+ newParams.get(CameraParameters::KEY_FLASH_MODE) );
+ }
+
+ if (validatedParams.flashMode != flashMode) {
+ camera_metadata_ro_entry_t flashAvailable =
+ staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 1, 1);
+ if (!flashAvailable.data.u8[0] &&
+ validatedParams.flashMode != Parameters::FLASH_MODE_OFF) {
+ ALOGE("%s: Requested flash mode \"%s\" is not supported: "
+ "No flash on device", __FUNCTION__,
+ newParams.get(CameraParameters::KEY_FLASH_MODE));
+ return BAD_VALUE;
+ } else if (validatedParams.flashMode == Parameters::FLASH_MODE_RED_EYE) {
+ camera_metadata_ro_entry_t availableAeModes =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES);
+ for (i = 0; i < availableAeModes.count; i++) {
+ if (validatedParams.flashMode == availableAeModes.data.u8[i])
+ break;
+ }
+ if (i == availableAeModes.count) {
+ ALOGE("%s: Requested flash mode \"%s\" is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_FLASH_MODE));
+ return BAD_VALUE;
+ }
+ } else if (validatedParams.flashMode == -1) {
+ ALOGE("%s: Requested flash mode \"%s\" is unknown",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_FLASH_MODE));
+ return BAD_VALUE;
+ }
+ // Update in case of override
+ newParams.set(CameraParameters::KEY_FLASH_MODE,
+ flashModeEnumToString(validatedParams.flashMode));
+ }
+
+ // WHITE_BALANCE
+ if (sceneModeSet) {
+ validatedParams.wbMode =
+ fastInfo.sceneModeOverrides.
+ valueFor(validatedParams.sceneMode).wbMode;
+ } else {
+ validatedParams.wbMode = ANDROID_CONTROL_AWB_MODE_OFF;
+ }
+ if (validatedParams.wbMode == ANDROID_CONTROL_AWB_MODE_OFF) {
+ validatedParams.wbMode = wbModeStringToEnum(
+ newParams.get(CameraParameters::KEY_WHITE_BALANCE) );
+ }
+ if (validatedParams.wbMode != wbMode) {
+ camera_metadata_ro_entry_t availableWbModes =
+ staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
+ for (i = 0; i < availableWbModes.count; i++) {
+ if (validatedParams.wbMode == availableWbModes.data.u8[i]) break;
+ }
+ if (i == availableWbModes.count) {
+ ALOGE("%s: Requested white balance mode %s is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_WHITE_BALANCE));
+ return BAD_VALUE;
+ }
+ // Update in case of override
+ newParams.set(CameraParameters::KEY_WHITE_BALANCE,
+ wbModeEnumToString(validatedParams.wbMode));
+ }
+
+ // FOCUS_MODE
+ if (sceneModeSet) {
+ validatedParams.focusMode =
+ fastInfo.sceneModeOverrides.
+ valueFor(validatedParams.sceneMode).focusMode;
+ } else {
+ validatedParams.focusMode = FOCUS_MODE_INVALID;
+ }
+ if (validatedParams.focusMode == FOCUS_MODE_INVALID) {
+ validatedParams.focusMode = focusModeStringToEnum(
+ newParams.get(CameraParameters::KEY_FOCUS_MODE) );
+ }
+ if (validatedParams.focusMode != focusMode) {
+ validatedParams.currentAfTriggerId = -1;
+ if (validatedParams.focusMode != Parameters::FOCUS_MODE_FIXED) {
+ camera_metadata_ro_entry_t minFocusDistance =
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+ if (minFocusDistance.data.f[0] == 0) {
+ ALOGE("%s: Requested focus mode \"%s\" is not available: "
+ "fixed focus lens",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_FOCUS_MODE));
+ return BAD_VALUE;
+ } else if (validatedParams.focusMode !=
+ Parameters::FOCUS_MODE_INFINITY) {
+ camera_metadata_ro_entry_t availableFocusModes =
+ staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES);
+ for (i = 0; i < availableFocusModes.count; i++) {
+ if (validatedParams.focusMode ==
+ availableFocusModes.data.u8[i]) break;
+ }
+ if (i == availableFocusModes.count) {
+ ALOGE("%s: Requested focus mode \"%s\" is not supported",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_FOCUS_MODE));
+ return BAD_VALUE;
+ }
+ }
+ }
+ validatedParams.focusState = ANDROID_CONTROL_AF_STATE_INACTIVE;
+ // Always reset shadow focus mode to avoid reverting settings
+ validatedParams.shadowFocusMode = FOCUS_MODE_INVALID;
+ // Update in case of override
+ newParams.set(CameraParameters::KEY_FOCUS_MODE,
+ focusModeEnumToString(validatedParams.focusMode));
+ } else {
+ validatedParams.currentAfTriggerId = currentAfTriggerId;
+ }
+
+ // FOCUS_AREAS
+ res = parseAreas(newParams.get(CameraParameters::KEY_FOCUS_AREAS),
+ &validatedParams.focusingAreas);
+ size_t max3aRegions =
+ (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1).data.i32[0];
+ if (res == OK) res = validateAreas(validatedParams.focusingAreas,
+ max3aRegions, AREA_KIND_FOCUS);
+ if (res != OK) {
+ ALOGE("%s: Requested focus areas are malformed: %s",
+ __FUNCTION__, newParams.get(CameraParameters::KEY_FOCUS_AREAS));
+ return BAD_VALUE;
+ }
+
+ // EXPOSURE_COMPENSATION
+ validatedParams.exposureCompensation =
+ newParams.getInt(CameraParameters::KEY_EXPOSURE_COMPENSATION);
+ camera_metadata_ro_entry_t exposureCompensationRange =
+ staticInfo(ANDROID_CONTROL_AE_COMPENSATION_RANGE);
+ if ((validatedParams.exposureCompensation <
+ exposureCompensationRange.data.i32[0]) ||
+ (validatedParams.exposureCompensation >
+ exposureCompensationRange.data.i32[1])) {
+ ALOGE("%s: Requested exposure compensation index is out of bounds: %d",
+ __FUNCTION__, validatedParams.exposureCompensation);
+ return BAD_VALUE;
+ }
+
+ // AUTO_EXPOSURE_LOCK (always supported)
+ validatedParams.autoExposureLock = boolFromString(
+ newParams.get(CameraParameters::KEY_AUTO_EXPOSURE_LOCK));
+
+ // AUTO_WHITEBALANCE_LOCK (always supported)
+ validatedParams.autoWhiteBalanceLock = boolFromString(
+ newParams.get(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK));
+
+ // METERING_AREAS
+ res = parseAreas(newParams.get(CameraParameters::KEY_METERING_AREAS),
+ &validatedParams.meteringAreas);
+ if (res == OK) {
+ res = validateAreas(validatedParams.meteringAreas, max3aRegions,
+ AREA_KIND_METERING);
+ }
+ if (res != OK) {
+ ALOGE("%s: Requested metering areas are malformed: %s",
+ __FUNCTION__,
+ newParams.get(CameraParameters::KEY_METERING_AREAS));
+ return BAD_VALUE;
+ }
+
+ // ZOOM
+ validatedParams.zoom = newParams.getInt(CameraParameters::KEY_ZOOM);
+ if (validatedParams.zoom < 0
+ || validatedParams.zoom >= (int)NUM_ZOOM_STEPS) {
+ ALOGE("%s: Requested zoom level %d is not supported",
+ __FUNCTION__, validatedParams.zoom);
+ return BAD_VALUE;
+ }
+
+ // VIDEO_SIZE
+ newParams.getVideoSize(&validatedParams.videoWidth,
+ &validatedParams.videoHeight);
+ if (validatedParams.videoWidth != videoWidth ||
+ validatedParams.videoHeight != videoHeight) {
+ if (state == RECORD) {
+ ALOGE("%s: Video size cannot be updated when recording is active!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ camera_metadata_ro_entry_t availableVideoSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
+ for (i = 0; i < availableVideoSizes.count; i += 2 ) {
+ if ((availableVideoSizes.data.i32[i] ==
+ validatedParams.videoWidth) &&
+ (availableVideoSizes.data.i32[i+1] ==
+ validatedParams.videoHeight)) break;
+ }
+ if (i == availableVideoSizes.count) {
+ ALOGE("%s: Requested video size %d x %d is not supported",
+ __FUNCTION__, validatedParams.videoWidth,
+ validatedParams.videoHeight);
+ return BAD_VALUE;
+ }
+ }
+
+ // VIDEO_STABILIZATION
+ validatedParams.videoStabilization = boolFromString(
+ newParams.get(CameraParameters::KEY_VIDEO_STABILIZATION) );
+ camera_metadata_ro_entry_t availableVideoStabilizationModes =
+ staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
+ if (validatedParams.videoStabilization &&
+ availableVideoStabilizationModes.count == 1) {
+ ALOGE("%s: Video stabilization not supported", __FUNCTION__);
+ }
+
+ /** Update internal parameters */
+
+ *this = validatedParams;
+
+ // Need to flatten again in case of overrides
+ paramsFlattened = newParams.flatten();
+ params = newParams;
+
+ return OK;
+}
+
+status_t Parameters::updateRequest(CameraMetadata *request) const {
+ ATRACE_CALL();
+ status_t res;
+
+ uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL;
+ res = request->update(ANDROID_REQUEST_METADATA_MODE,
+ &metadataMode, 1);
+ if (res != OK) return res;
+
+ res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ previewFpsRange, 2);
+ if (res != OK) return res;
+
+ uint8_t reqWbLock = autoWhiteBalanceLock ?
+ ANDROID_CONTROL_AWB_LOCK_ON : ANDROID_CONTROL_AWB_LOCK_OFF;
+ res = request->update(ANDROID_CONTROL_AWB_LOCK,
+ &reqWbLock, 1);
+
+ res = request->update(ANDROID_CONTROL_EFFECT_MODE,
+ &effectMode, 1);
+ if (res != OK) return res;
+ res = request->update(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ &antibandingMode, 1);
+ if (res != OK) return res;
+
+ // android.hardware.Camera requires that when face detect is enabled, the
+ // camera is in a face-priority mode. HAL2 splits this into separate parts
+ // (face detection statistics and face priority scene mode). Map from other
+ // to the other.
+ bool sceneModeActive =
+ sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ uint8_t reqControlMode = ANDROID_CONTROL_MODE_AUTO;
+ if (enableFaceDetect || sceneModeActive) {
+ reqControlMode = ANDROID_CONTROL_MODE_USE_SCENE_MODE;
+ }
+ res = request->update(ANDROID_CONTROL_MODE,
+ &reqControlMode, 1);
+ if (res != OK) return res;
+
+ uint8_t reqSceneMode =
+ sceneModeActive ? sceneMode :
+ enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY :
+ (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
+ res = request->update(ANDROID_CONTROL_SCENE_MODE,
+ &reqSceneMode, 1);
+ if (res != OK) return res;
+
+ uint8_t reqFlashMode = ANDROID_FLASH_MODE_OFF;
+ uint8_t reqAeMode = ANDROID_CONTROL_AE_MODE_OFF;
+ switch (flashMode) {
+ case Parameters::FLASH_MODE_OFF:
+ reqAeMode = ANDROID_CONTROL_AE_MODE_ON; break;
+ case Parameters::FLASH_MODE_AUTO:
+ reqAeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH; break;
+ case Parameters::FLASH_MODE_ON:
+ reqAeMode = ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH; break;
+ case Parameters::FLASH_MODE_TORCH:
+ reqAeMode = ANDROID_CONTROL_AE_MODE_ON;
+ reqFlashMode = ANDROID_FLASH_MODE_TORCH;
+ break;
+ case Parameters::FLASH_MODE_RED_EYE:
+ reqAeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; break;
+ default:
+ ALOGE("%s: Camera %d: Unknown flash mode %d", __FUNCTION__,
+ cameraId, flashMode);
+ return BAD_VALUE;
+ }
+ res = request->update(ANDROID_FLASH_MODE,
+ &reqFlashMode, 1);
+ if (res != OK) return res;
+ res = request->update(ANDROID_CONTROL_AE_MODE,
+ &reqAeMode, 1);
+ if (res != OK) return res;
+
+ uint8_t reqAeLock = autoExposureLock ?
+ ANDROID_CONTROL_AE_LOCK_ON : ANDROID_CONTROL_AE_LOCK_OFF;
+ res = request->update(ANDROID_CONTROL_AE_LOCK,
+ &reqAeLock, 1);
+ if (res != OK) return res;
+
+ res = request->update(ANDROID_CONTROL_AWB_MODE,
+ &wbMode, 1);
+ if (res != OK) return res;
+
+ float reqFocusDistance = 0; // infinity focus in diopters
+ uint8_t reqFocusMode = ANDROID_CONTROL_AF_MODE_OFF;
+ switch (focusMode) {
+ case Parameters::FOCUS_MODE_AUTO:
+ case Parameters::FOCUS_MODE_MACRO:
+ case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO:
+ case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE:
+ case Parameters::FOCUS_MODE_EDOF:
+ reqFocusMode = focusMode;
+ break;
+ case Parameters::FOCUS_MODE_INFINITY:
+ case Parameters::FOCUS_MODE_FIXED:
+ reqFocusMode = ANDROID_CONTROL_AF_MODE_OFF;
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unknown focus mode %d", __FUNCTION__,
+ cameraId, focusMode);
+ return BAD_VALUE;
+ }
+ res = request->update(ANDROID_LENS_FOCUS_DISTANCE,
+ &reqFocusDistance, 1);
+ if (res != OK) return res;
+ res = request->update(ANDROID_CONTROL_AF_MODE,
+ &reqFocusMode, 1);
+ if (res != OK) return res;
+
+ size_t reqFocusingAreasSize = focusingAreas.size() * 5;
+ int32_t *reqFocusingAreas = new int32_t[reqFocusingAreasSize];
+ for (size_t i = 0; i < reqFocusingAreasSize; i += 5) {
+ if (focusingAreas[i].weight != 0) {
+ reqFocusingAreas[i + 0] =
+ normalizedXToArray(focusingAreas[i].left);
+ reqFocusingAreas[i + 1] =
+ normalizedYToArray(focusingAreas[i].top);
+ reqFocusingAreas[i + 2] =
+ normalizedXToArray(focusingAreas[i].right);
+ reqFocusingAreas[i + 3] =
+ normalizedYToArray(focusingAreas[i].bottom);
+ } else {
+ reqFocusingAreas[i + 0] = 0;
+ reqFocusingAreas[i + 1] = 0;
+ reqFocusingAreas[i + 2] = 0;
+ reqFocusingAreas[i + 3] = 0;
+ }
+ reqFocusingAreas[i + 4] = focusingAreas[i].weight;
+ }
+ res = request->update(ANDROID_CONTROL_AF_REGIONS,
+ reqFocusingAreas, reqFocusingAreasSize);
+ if (res != OK) return res;
+ delete[] reqFocusingAreas;
+
+ res = request->update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ &exposureCompensation, 1);
+ if (res != OK) return res;
+
+ size_t reqMeteringAreasSize = meteringAreas.size() * 5;
+ int32_t *reqMeteringAreas = new int32_t[reqMeteringAreasSize];
+ for (size_t i = 0; i < reqMeteringAreasSize; i += 5) {
+ if (meteringAreas[i].weight != 0) {
+ reqMeteringAreas[i + 0] =
+ normalizedXToArray(meteringAreas[i].left);
+ reqMeteringAreas[i + 1] =
+ normalizedYToArray(meteringAreas[i].top);
+ reqMeteringAreas[i + 2] =
+ normalizedXToArray(meteringAreas[i].right);
+ reqMeteringAreas[i + 3] =
+ normalizedYToArray(meteringAreas[i].bottom);
+ } else {
+ reqMeteringAreas[i + 0] = 0;
+ reqMeteringAreas[i + 1] = 0;
+ reqMeteringAreas[i + 2] = 0;
+ reqMeteringAreas[i + 3] = 0;
+ }
+ reqMeteringAreas[i + 4] = meteringAreas[i].weight;
+ }
+ res = request->update(ANDROID_CONTROL_AE_REGIONS,
+ reqMeteringAreas, reqMeteringAreasSize);
+ if (res != OK) return res;
+
+ delete[] reqMeteringAreas;
+
+ /* don't include jpeg thumbnail size - it's valid for
+ it to be set to (0,0), meaning 'no thumbnail' */
+ CropRegion crop = calculateCropRegion( (CropRegion::Outputs)(
+ CropRegion::OUTPUT_PREVIEW |
+ CropRegion::OUTPUT_VIDEO |
+ CropRegion::OUTPUT_PICTURE ));
+ int32_t reqCropRegion[3] = {
+ static_cast<int32_t>(crop.left),
+ static_cast<int32_t>(crop.top),
+ static_cast<int32_t>(crop.width)
+ };
+ res = request->update(ANDROID_SCALER_CROP_REGION,
+ reqCropRegion, 3);
+ if (res != OK) return res;
+
+ uint8_t reqVstabMode = videoStabilization ?
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_ON :
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF;
+ res = request->update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ &reqVstabMode, 1);
+ if (res != OK) return res;
+
+ uint8_t reqFaceDetectMode = enableFaceDetect ?
+ fastInfo.bestFaceDetectMode :
+ (uint8_t)ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
+ res = request->update(ANDROID_STATISTICS_FACE_DETECT_MODE,
+ &reqFaceDetectMode, 1);
+ if (res != OK) return res;
+
+ return OK;
+}
+
+status_t Parameters::updateRequestJpeg(CameraMetadata *request) const {
+ status_t res;
+
+ res = request->update(ANDROID_JPEG_THUMBNAIL_SIZE,
+ jpegThumbSize, 2);
+ if (res != OK) return res;
+ res = request->update(ANDROID_JPEG_THUMBNAIL_QUALITY,
+ &jpegThumbQuality, 1);
+ if (res != OK) return res;
+ res = request->update(ANDROID_JPEG_QUALITY,
+ &jpegQuality, 1);
+ if (res != OK) return res;
+ res = request->update(
+ ANDROID_JPEG_ORIENTATION,
+ &jpegRotation, 1);
+ if (res != OK) return res;
+
+ if (gpsEnabled) {
+ res = request->update(
+ ANDROID_JPEG_GPS_COORDINATES,
+ gpsCoordinates, 3);
+ if (res != OK) return res;
+ res = request->update(
+ ANDROID_JPEG_GPS_TIMESTAMP,
+ &gpsTimestamp, 1);
+ if (res != OK) return res;
+ res = request->update(
+ ANDROID_JPEG_GPS_PROCESSING_METHOD,
+ gpsProcessingMethod);
+ if (res != OK) return res;
+ } else {
+ res = request->erase(ANDROID_JPEG_GPS_COORDINATES);
+ if (res != OK) return res;
+ res = request->erase(ANDROID_JPEG_GPS_TIMESTAMP);
+ if (res != OK) return res;
+ res = request->erase(ANDROID_JPEG_GPS_PROCESSING_METHOD);
+ if (res != OK) return res;
+ }
+ return OK;
+}
+
+
+const char* Parameters::getStateName(State state) {
+#define CASE_ENUM_TO_CHAR(x) case x: return(#x); break;
+ switch(state) {
+ CASE_ENUM_TO_CHAR(DISCONNECTED)
+ CASE_ENUM_TO_CHAR(STOPPED)
+ CASE_ENUM_TO_CHAR(WAITING_FOR_PREVIEW_WINDOW)
+ CASE_ENUM_TO_CHAR(PREVIEW)
+ CASE_ENUM_TO_CHAR(RECORD)
+ CASE_ENUM_TO_CHAR(STILL_CAPTURE)
+ CASE_ENUM_TO_CHAR(VIDEO_SNAPSHOT)
+ default:
+ return "Unknown state!";
+ break;
+ }
+#undef CASE_ENUM_TO_CHAR
+}
+
+int Parameters::formatStringToEnum(const char *format) {
+ return
+ !format ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP :
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420P) ?
+ HAL_PIXEL_FORMAT_YV12 : // YV12
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_RGB565) ?
+ HAL_PIXEL_FORMAT_RGB_565 : // RGB565
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_RGBA8888) ?
+ HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888
+ !strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) ?
+ HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data
+ -1;
+}
+
+const char* Parameters::formatEnumToString(int format) {
+ const char *fmt;
+ switch(format) {
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP: // NV16
+ fmt = CameraParameters::PIXEL_FORMAT_YUV422SP;
+ break;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21
+ fmt = CameraParameters::PIXEL_FORMAT_YUV420SP;
+ break;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I: // YUY2
+ fmt = CameraParameters::PIXEL_FORMAT_YUV422I;
+ break;
+ case HAL_PIXEL_FORMAT_YV12: // YV12
+ fmt = CameraParameters::PIXEL_FORMAT_YUV420P;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565: // RGB565
+ fmt = CameraParameters::PIXEL_FORMAT_RGB565;
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888: // RGBA8888
+ fmt = CameraParameters::PIXEL_FORMAT_RGBA8888;
+ break;
+ case HAL_PIXEL_FORMAT_RAW_SENSOR:
+ ALOGW("Raw sensor preview format requested.");
+ fmt = CameraParameters::PIXEL_FORMAT_BAYER_RGGB;
+ break;
+ default:
+ ALOGE("%s: Unknown preview format: %x",
+ __FUNCTION__, format);
+ fmt = NULL;
+ break;
+ }
+ return fmt;
+}
+
+int Parameters::wbModeStringToEnum(const char *wbMode) {
+ return
+ !wbMode ?
+ ANDROID_CONTROL_AWB_MODE_AUTO :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_AUTO) ?
+ ANDROID_CONTROL_AWB_MODE_AUTO :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_INCANDESCENT) ?
+ ANDROID_CONTROL_AWB_MODE_INCANDESCENT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_FLUORESCENT) ?
+ ANDROID_CONTROL_AWB_MODE_FLUORESCENT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT) ?
+ ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_DAYLIGHT) ?
+ ANDROID_CONTROL_AWB_MODE_DAYLIGHT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT) ?
+ ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_TWILIGHT) ?
+ ANDROID_CONTROL_AWB_MODE_TWILIGHT :
+ !strcmp(wbMode, CameraParameters::WHITE_BALANCE_SHADE) ?
+ ANDROID_CONTROL_AWB_MODE_SHADE :
+ -1;
+}
+
+const char* Parameters::wbModeEnumToString(uint8_t wbMode) {
+ switch (wbMode) {
+ case ANDROID_CONTROL_AWB_MODE_AUTO:
+ return CameraParameters::WHITE_BALANCE_AUTO;
+ case ANDROID_CONTROL_AWB_MODE_INCANDESCENT:
+ return CameraParameters::WHITE_BALANCE_INCANDESCENT;
+ case ANDROID_CONTROL_AWB_MODE_FLUORESCENT:
+ return CameraParameters::WHITE_BALANCE_FLUORESCENT;
+ case ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT:
+ return CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT;
+ case ANDROID_CONTROL_AWB_MODE_DAYLIGHT:
+ return CameraParameters::WHITE_BALANCE_DAYLIGHT;
+ case ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
+ return CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT;
+ case ANDROID_CONTROL_AWB_MODE_TWILIGHT:
+ return CameraParameters::WHITE_BALANCE_TWILIGHT;
+ case ANDROID_CONTROL_AWB_MODE_SHADE:
+ return CameraParameters::WHITE_BALANCE_SHADE;
+ default:
+ ALOGE("%s: Unknown AWB mode enum: %d",
+ __FUNCTION__, wbMode);
+ return "unknown";
+ }
+}
+
+int Parameters::effectModeStringToEnum(const char *effectMode) {
+ return
+ !effectMode ?
+ ANDROID_CONTROL_EFFECT_MODE_OFF :
+ !strcmp(effectMode, CameraParameters::EFFECT_NONE) ?
+ ANDROID_CONTROL_EFFECT_MODE_OFF :
+ !strcmp(effectMode, CameraParameters::EFFECT_MONO) ?
+ ANDROID_CONTROL_EFFECT_MODE_MONO :
+ !strcmp(effectMode, CameraParameters::EFFECT_NEGATIVE) ?
+ ANDROID_CONTROL_EFFECT_MODE_NEGATIVE :
+ !strcmp(effectMode, CameraParameters::EFFECT_SOLARIZE) ?
+ ANDROID_CONTROL_EFFECT_MODE_SOLARIZE :
+ !strcmp(effectMode, CameraParameters::EFFECT_SEPIA) ?
+ ANDROID_CONTROL_EFFECT_MODE_SEPIA :
+ !strcmp(effectMode, CameraParameters::EFFECT_POSTERIZE) ?
+ ANDROID_CONTROL_EFFECT_MODE_POSTERIZE :
+ !strcmp(effectMode, CameraParameters::EFFECT_WHITEBOARD) ?
+ ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD :
+ !strcmp(effectMode, CameraParameters::EFFECT_BLACKBOARD) ?
+ ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD :
+ !strcmp(effectMode, CameraParameters::EFFECT_AQUA) ?
+ ANDROID_CONTROL_EFFECT_MODE_AQUA :
+ -1;
+}
+
+int Parameters::abModeStringToEnum(const char *abMode) {
+ return
+ !abMode ?
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO :
+ !strcmp(abMode, CameraParameters::ANTIBANDING_AUTO) ?
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO :
+ !strcmp(abMode, CameraParameters::ANTIBANDING_OFF) ?
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF :
+ !strcmp(abMode, CameraParameters::ANTIBANDING_50HZ) ?
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ :
+ !strcmp(abMode, CameraParameters::ANTIBANDING_60HZ) ?
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ :
+ -1;
+}
+
+int Parameters::sceneModeStringToEnum(const char *sceneMode) {
+ return
+ !sceneMode ?
+ ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_AUTO) ?
+ ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_ACTION) ?
+ ANDROID_CONTROL_SCENE_MODE_ACTION :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_PORTRAIT) ?
+ ANDROID_CONTROL_SCENE_MODE_PORTRAIT :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_LANDSCAPE) ?
+ ANDROID_CONTROL_SCENE_MODE_LANDSCAPE :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_NIGHT) ?
+ ANDROID_CONTROL_SCENE_MODE_NIGHT :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_NIGHT_PORTRAIT) ?
+ ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_THEATRE) ?
+ ANDROID_CONTROL_SCENE_MODE_THEATRE :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_BEACH) ?
+ ANDROID_CONTROL_SCENE_MODE_BEACH :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_SNOW) ?
+ ANDROID_CONTROL_SCENE_MODE_SNOW :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_SUNSET) ?
+ ANDROID_CONTROL_SCENE_MODE_SUNSET :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_STEADYPHOTO) ?
+ ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_FIREWORKS) ?
+ ANDROID_CONTROL_SCENE_MODE_FIREWORKS :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_SPORTS) ?
+ ANDROID_CONTROL_SCENE_MODE_SPORTS :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_PARTY) ?
+ ANDROID_CONTROL_SCENE_MODE_PARTY :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_CANDLELIGHT) ?
+ ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT :
+ !strcmp(sceneMode, CameraParameters::SCENE_MODE_BARCODE) ?
+ ANDROID_CONTROL_SCENE_MODE_BARCODE:
+ -1;
+}
+
+Parameters::Parameters::flashMode_t Parameters::flashModeStringToEnum(
+ const char *flashMode) {
+ return
+ !flashMode ?
+ Parameters::FLASH_MODE_INVALID :
+ !strcmp(flashMode, CameraParameters::FLASH_MODE_OFF) ?
+ Parameters::FLASH_MODE_OFF :
+ !strcmp(flashMode, CameraParameters::FLASH_MODE_AUTO) ?
+ Parameters::FLASH_MODE_AUTO :
+ !strcmp(flashMode, CameraParameters::FLASH_MODE_ON) ?
+ Parameters::FLASH_MODE_ON :
+ !strcmp(flashMode, CameraParameters::FLASH_MODE_RED_EYE) ?
+ Parameters::FLASH_MODE_RED_EYE :
+ !strcmp(flashMode, CameraParameters::FLASH_MODE_TORCH) ?
+ Parameters::FLASH_MODE_TORCH :
+ Parameters::FLASH_MODE_INVALID;
+}
+
+const char *Parameters::flashModeEnumToString(flashMode_t flashMode) {
+ switch (flashMode) {
+ case FLASH_MODE_OFF:
+ return CameraParameters::FLASH_MODE_OFF;
+ case FLASH_MODE_AUTO:
+ return CameraParameters::FLASH_MODE_AUTO;
+ case FLASH_MODE_ON:
+ return CameraParameters::FLASH_MODE_ON;
+ case FLASH_MODE_RED_EYE:
+ return CameraParameters::FLASH_MODE_RED_EYE;
+ case FLASH_MODE_TORCH:
+ return CameraParameters::FLASH_MODE_TORCH;
+ default:
+ ALOGE("%s: Unknown flash mode enum %d",
+ __FUNCTION__, flashMode);
+ return "unknown";
+ }
+}
+
+Parameters::Parameters::focusMode_t Parameters::focusModeStringToEnum(
+ const char *focusMode) {
+ return
+ !focusMode ?
+ Parameters::FOCUS_MODE_INVALID :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_AUTO) ?
+ Parameters::FOCUS_MODE_AUTO :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_INFINITY) ?
+ Parameters::FOCUS_MODE_INFINITY :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_MACRO) ?
+ Parameters::FOCUS_MODE_MACRO :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_FIXED) ?
+ Parameters::FOCUS_MODE_FIXED :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_EDOF) ?
+ Parameters::FOCUS_MODE_EDOF :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO) ?
+ Parameters::FOCUS_MODE_CONTINUOUS_VIDEO :
+ !strcmp(focusMode, CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE) ?
+ Parameters::FOCUS_MODE_CONTINUOUS_PICTURE :
+ Parameters::FOCUS_MODE_INVALID;
+}
+
+const char *Parameters::focusModeEnumToString(focusMode_t focusMode) {
+ switch (focusMode) {
+ case FOCUS_MODE_AUTO:
+ return CameraParameters::FOCUS_MODE_AUTO;
+ case FOCUS_MODE_MACRO:
+ return CameraParameters::FOCUS_MODE_MACRO;
+ case FOCUS_MODE_CONTINUOUS_VIDEO:
+ return CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
+ case FOCUS_MODE_CONTINUOUS_PICTURE:
+ return CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
+ case FOCUS_MODE_EDOF:
+ return CameraParameters::FOCUS_MODE_EDOF;
+ case FOCUS_MODE_INFINITY:
+ return CameraParameters::FOCUS_MODE_INFINITY;
+ case FOCUS_MODE_FIXED:
+ return CameraParameters::FOCUS_MODE_FIXED;
+ default:
+ ALOGE("%s: Unknown focus mode enum: %d",
+ __FUNCTION__, focusMode);
+ return "unknown";
+ }
+}
+
+status_t Parameters::parseAreas(const char *areasCStr,
+ Vector<Parameters::Area> *areas) {
+ static const size_t NUM_FIELDS = 5;
+ areas->clear();
+ if (areasCStr == NULL) {
+ // If no key exists, use default (0,0,0,0,0)
+ areas->push();
+ return OK;
+ }
+ String8 areasStr(areasCStr);
+ ssize_t areaStart = areasStr.find("(", 0) + 1;
+ while (areaStart != 0) {
+ const char* area = areasStr.string() + areaStart;
+ char *numEnd;
+ int vals[NUM_FIELDS];
+ for (size_t i = 0; i < NUM_FIELDS; i++) {
+ errno = 0;
+ vals[i] = strtol(area, &numEnd, 10);
+ if (errno || numEnd == area) return BAD_VALUE;
+ area = numEnd + 1;
+ }
+ areas->push(Parameters::Area(
+ vals[0], vals[1], vals[2], vals[3], vals[4]) );
+ areaStart = areasStr.find("(", areaStart) + 1;
+ }
+ return OK;
+}
+
+status_t Parameters::validateAreas(const Vector<Parameters::Area> &areas,
+ size_t maxRegions,
+ AreaKind areaKind) const {
+ // Definition of valid area can be found in
+ // include/camera/CameraParameters.h
+ if (areas.size() == 0) return BAD_VALUE;
+ if (areas.size() == 1) {
+ if (areas[0].left == 0 &&
+ areas[0].top == 0 &&
+ areas[0].right == 0 &&
+ areas[0].bottom == 0 &&
+ areas[0].weight == 0) {
+ // Single (0,0,0,0,0) entry is always valid (== driver decides)
+ return OK;
+ }
+ }
+
+ // fixed focus can only set (0,0,0,0,0) focus area
+ if (areaKind == AREA_KIND_FOCUS && focusMode == FOCUS_MODE_FIXED) {
+ return BAD_VALUE;
+ }
+
+ if (areas.size() > maxRegions) {
+ ALOGE("%s: Too many areas requested: %d",
+ __FUNCTION__, areas.size());
+ return BAD_VALUE;
+ }
+
+ for (Vector<Parameters::Area>::const_iterator a = areas.begin();
+ a != areas.end(); a++) {
+ if (a->weight < 1 || a->weight > 1000) return BAD_VALUE;
+ if (a->left < -1000 || a->left > 1000) return BAD_VALUE;
+ if (a->top < -1000 || a->top > 1000) return BAD_VALUE;
+ if (a->right < -1000 || a->right > 1000) return BAD_VALUE;
+ if (a->bottom < -1000 || a->bottom > 1000) return BAD_VALUE;
+ if (a->left >= a->right) return BAD_VALUE;
+ if (a->top >= a->bottom) return BAD_VALUE;
+ }
+ return OK;
+}
+
+bool Parameters::boolFromString(const char *boolStr) {
+ return !boolStr ? false :
+ !strcmp(boolStr, CameraParameters::TRUE) ? true :
+ false;
+}
+
+int Parameters::degToTransform(int degrees, bool mirror) {
+ if (!mirror) {
+ if (degrees == 0) return 0;
+ else if (degrees == 90) return HAL_TRANSFORM_ROT_90;
+ else if (degrees == 180) return HAL_TRANSFORM_ROT_180;
+ else if (degrees == 270) return HAL_TRANSFORM_ROT_270;
+ } else { // Do mirror (horizontal flip)
+ if (degrees == 0) { // FLIP_H and ROT_0
+ return HAL_TRANSFORM_FLIP_H;
+ } else if (degrees == 90) { // FLIP_H and ROT_90
+ return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90;
+ } else if (degrees == 180) { // FLIP_H and ROT_180
+ return HAL_TRANSFORM_FLIP_V;
+ } else if (degrees == 270) { // FLIP_H and ROT_270
+ return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+ }
+ }
+ ALOGE("%s: Bad input: %d", __FUNCTION__, degrees);
+ return -1;
+}
+
+int Parameters::cropXToArray(int x) const {
+ ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds"
+ "(lower = 0)", x);
+
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' "
+ "is out of bounds (upper = %d)", x, previewCrop.width);
+
+ int ret = x + previewCrop.left;
+
+ ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayWidth),
+ "Calculated pixel array value X = '%d' is out of bounds (upper = %d)",
+ ret, fastInfo.arrayWidth);
+ return ret;
+}
+
+int Parameters::cropYToArray(int y) const {
+ ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds "
+ "(lower = 0)", y);
+
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is "
+ "out of bounds (upper = %d)", y, previewCrop.height);
+
+ int ret = y + previewCrop.top;
+
+ ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayHeight),
+ "Calculated pixel array value Y = '%d' is out of bounds (upper = %d)",
+ ret, fastInfo.arrayHeight);
+
+ return ret;
+
+}
+
+int Parameters::normalizedXToCrop(int x) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return (x + 1000) * (previewCrop.width - 1) / 2000;
+}
+
+int Parameters::normalizedYToCrop(int y) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return (y + 1000) * (previewCrop.height - 1) / 2000;
+}
+
+int Parameters::arrayXToCrop(int x) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return x - previewCrop.left;
+}
+
+int Parameters::arrayYToCrop(int y) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return y - previewCrop.top;
+}
+
+int Parameters::cropXToNormalized(int x) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return x * 2000 / (previewCrop.width - 1) - 1000;
+}
+
+int Parameters::cropYToNormalized(int y) const {
+ CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW);
+ return y * 2000 / (previewCrop.height - 1) - 1000;
+}
+
+int Parameters::arrayXToNormalized(int width) const {
+ int ret = cropXToNormalized(arrayXToCrop(width));
+
+ ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of "
+ "lower bounds %d", ret);
+ ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of "
+ "upper bounds %d", ret);
+
+ // Work-around for HAL pre-scaling the coordinates themselves
+ if (quirks.meteringCropRegion) {
+ return width * 2000 / (fastInfo.arrayWidth - 1) - 1000;
+ }
+
+ return ret;
+}
+
+int Parameters::arrayYToNormalized(int height) const {
+ int ret = cropYToNormalized(arrayYToCrop(height));
+
+ ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of lower bounds"
+ " %d", ret);
+ ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of upper bounds"
+ " %d", ret);
+
+ // Work-around for HAL pre-scaling the coordinates themselves
+ if (quirks.meteringCropRegion) {
+ return height * 2000 / (fastInfo.arrayHeight - 1) - 1000;
+ }
+
+ return ret;
+}
+
+int Parameters::normalizedXToArray(int x) const {
+
+ // Work-around for HAL pre-scaling the coordinates themselves
+ if (quirks.meteringCropRegion) {
+ return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000;
+ }
+
+ return cropXToArray(normalizedXToCrop(x));
+}
+
+int Parameters::normalizedYToArray(int y) const {
+ // Work-around for HAL pre-scaling the coordinates themselves
+ if (quirks.meteringCropRegion) {
+ return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000;
+ }
+
+ return cropYToArray(normalizedYToCrop(y));
+}
+
+Parameters::CropRegion Parameters::calculateCropRegion(
+ Parameters::CropRegion::Outputs outputs) const {
+
+ float zoomLeft, zoomTop, zoomWidth, zoomHeight;
+
+ // Need to convert zoom index into a crop rectangle. The rectangle is
+ // chosen to maximize its area on the sensor
+
+ camera_metadata_ro_entry_t maxDigitalZoom =
+ staticInfo(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
+ // For each zoom step by how many pixels more do we change the zoom
+ float zoomIncrement = (maxDigitalZoom.data.f[0] - 1) /
+ (NUM_ZOOM_STEPS-1);
+ // The desired activeAreaWidth/cropAreaWidth ratio (or height if h>w)
+ // via interpolating zoom step into a zoom ratio
+ float zoomRatio = 1 + zoomIncrement * zoom;
+ ALOG_ASSERT( (zoomRatio >= 1.f && zoomRatio <= maxDigitalZoom.data.f[0]),
+ "Zoom ratio calculated out of bounds. Expected 1 - %f, actual: %f",
+ maxDigitalZoom.data.f[0], zoomRatio);
+
+ ALOGV("Zoom maxDigital=%f, increment=%f, ratio=%f, previewWidth=%d, "
+ "previewHeight=%d, activeWidth=%d, activeHeight=%d",
+ maxDigitalZoom.data.f[0], zoomIncrement, zoomRatio, previewWidth,
+ previewHeight, fastInfo.arrayWidth, fastInfo.arrayHeight);
+
+ /*
+ * Assumption: On the HAL side each stream buffer calculates its crop
+ * rectangle as follows:
+ * cropRect = (zoomLeft, zoomRight,
+ * zoomWidth, zoomHeight * zoomWidth / outputWidth);
+ *
+ * Note that if zoomWidth > bufferWidth, the new cropHeight > zoomHeight
+ * (we can then get into trouble if the cropHeight > arrayHeight).
+ * By selecting the zoomRatio based on the smallest outputRatio, we
+ * guarantee this will never happen.
+ */
+
+ // Enumerate all possible output sizes, select the one with the smallest
+ // aspect ratio
+ float minOutputWidth, minOutputHeight, minOutputRatio;
+ {
+ float outputSizes[][2] = {
+ { static_cast<float>(previewWidth),
+ static_cast<float>(previewHeight) },
+ { static_cast<float>(videoWidth),
+ static_cast<float>(videoHeight) },
+ { static_cast<float>(jpegThumbSize[0]),
+ static_cast<float>(jpegThumbSize[1]) },
+ { static_cast<float>(pictureWidth),
+ static_cast<float>(pictureHeight) },
+ };
+
+ minOutputWidth = outputSizes[0][0];
+ minOutputHeight = outputSizes[0][1];
+ minOutputRatio = minOutputWidth / minOutputHeight;
+ for (unsigned int i = 0;
+ i < sizeof(outputSizes) / sizeof(outputSizes[0]);
+ ++i) {
+
+ // skip over outputs we don't want to consider for the crop region
+ if ( !((1 << i) & outputs) ) {
+ continue;
+ }
+
+ float outputWidth = outputSizes[i][0];
+ float outputHeight = outputSizes[i][1];
+ float outputRatio = outputWidth / outputHeight;
+
+ if (minOutputRatio > outputRatio) {
+ minOutputRatio = outputRatio;
+ minOutputWidth = outputWidth;
+ minOutputHeight = outputHeight;
+ }
+
+ // and then use this output ratio instead of preview output ratio
+ ALOGV("Enumerating output ratio %f = %f / %f, min is %f",
+ outputRatio, outputWidth, outputHeight, minOutputRatio);
+ }
+ }
+
+ /* Ensure that the width/height never go out of bounds
+ * by scaling across a diffent dimension if an out-of-bounds
+ * possibility exists.
+ *
+ * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by
+ * calculating the zoomWidth from zoomHeight we'll actually get a
+ * zoomheight > arrayheight
+ */
+ float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight;
+ if (minOutputRatio >= arrayRatio) {
+ // Adjust the height based on the width
+ zoomWidth = fastInfo.arrayWidth / zoomRatio;
+ zoomHeight = zoomWidth *
+ minOutputHeight / minOutputWidth;
+
+ } else {
+ // Adjust the width based on the height
+ zoomHeight = fastInfo.arrayHeight / zoomRatio;
+ zoomWidth = zoomHeight *
+ minOutputWidth / minOutputHeight;
+ }
+ // centering the zoom area within the active area
+ zoomLeft = (fastInfo.arrayWidth - zoomWidth) / 2;
+ zoomTop = (fastInfo.arrayHeight - zoomHeight) / 2;
+
+ ALOGV("Crop region calculated (x=%d,y=%d,w=%f,h=%f) for zoom=%d",
+ (int32_t)zoomLeft, (int32_t)zoomTop, zoomWidth, zoomHeight, this->zoom);
+
+
+ CropRegion crop = { zoomLeft, zoomTop, zoomWidth, zoomHeight };
+ return crop;
+}
+
+int32_t Parameters::fpsFromRange(int32_t min, int32_t max) const {
+ return max;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
new file mode 100644
index 00000000..dc2cdcf9
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2PARAMETERS_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2PARAMETERS_H
+
+#include <system/graphics.h>
+
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <camera/CameraParameters.h>
+
+#include "CameraMetadata.h"
+
+namespace android {
+namespace camera2 {
+
+/**
+ * Current camera state; this is the full state of the Camera under the old
+ * camera API (contents of the CameraParameters object in a more-efficient
+ * format, plus other state). The enum values are mostly based off the
+ * corresponding camera2 enums, not the camera1 strings. A few are defined here
+ * if they don't cleanly map to camera2 values.
+ */
+struct Parameters {
+ /**
+ * Parameters and other state
+ */
+ int cameraId;
+ int cameraFacing;
+
+ int previewWidth, previewHeight;
+ int32_t previewFpsRange[2];
+ int previewFps; // deprecated, here only for tracking changes
+ int previewFormat;
+
+ int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
+
+ int pictureWidth, pictureHeight;
+
+ int32_t jpegThumbSize[2];
+ uint8_t jpegQuality, jpegThumbQuality;
+ int32_t jpegRotation;
+
+ bool gpsEnabled;
+ double gpsCoordinates[3];
+ int64_t gpsTimestamp;
+ String8 gpsProcessingMethod;
+
+ uint8_t wbMode;
+ uint8_t effectMode;
+ uint8_t antibandingMode;
+ uint8_t sceneMode;
+
+ enum flashMode_t {
+ FLASH_MODE_OFF = 0,
+ FLASH_MODE_AUTO,
+ FLASH_MODE_ON,
+ FLASH_MODE_TORCH,
+ FLASH_MODE_RED_EYE = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE,
+ FLASH_MODE_INVALID = -1
+ } flashMode;
+
+ enum focusMode_t {
+ FOCUS_MODE_AUTO = ANDROID_CONTROL_AF_MODE_AUTO,
+ FOCUS_MODE_MACRO = ANDROID_CONTROL_AF_MODE_MACRO,
+ FOCUS_MODE_CONTINUOUS_VIDEO = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO,
+ FOCUS_MODE_CONTINUOUS_PICTURE = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE,
+ FOCUS_MODE_EDOF = ANDROID_CONTROL_AF_MODE_EDOF,
+ FOCUS_MODE_INFINITY,
+ FOCUS_MODE_FIXED,
+ FOCUS_MODE_INVALID = -1
+ } focusMode;
+
+ uint8_t focusState; // Latest focus state from HAL
+
+ // For use with triggerAfWithAuto quirk
+ focusMode_t shadowFocusMode;
+
+ struct Area {
+ int left, top, right, bottom;
+ int weight;
+ Area() {}
+ Area(int left, int top, int right, int bottom, int weight):
+ left(left), top(top), right(right), bottom(bottom),
+ weight(weight) {}
+ bool isEmpty() const {
+ return (left == 0) && (top == 0) && (right == 0) && (bottom == 0);
+ }
+ };
+ Vector<Area> focusingAreas;
+
+ int32_t exposureCompensation;
+ bool autoExposureLock;
+ bool autoWhiteBalanceLock;
+
+ Vector<Area> meteringAreas;
+
+ int zoom;
+
+ int videoWidth, videoHeight;
+
+ bool recordingHint;
+ bool videoStabilization;
+
+ enum lightFxMode_t {
+ LIGHTFX_NONE = 0,
+ LIGHTFX_LOWLIGHT,
+ LIGHTFX_HDR
+ } lightFx;
+
+ CameraParameters params;
+ String8 paramsFlattened;
+
+ // These parameters are also part of the camera API-visible state, but not
+ // directly listed in Camera.Parameters
+ bool storeMetadataInBuffers;
+ bool playShutterSound;
+ bool enableFaceDetect;
+
+ bool enableFocusMoveMessages;
+ int afTriggerCounter;
+ int currentAfTriggerId;
+ bool afInMotion;
+
+ int precaptureTriggerCounter;
+
+ uint32_t previewCallbackFlags;
+ bool previewCallbackOneShot;
+
+ bool zslMode;
+
+ // Overall camera state
+ enum State {
+ DISCONNECTED,
+ STOPPED,
+ WAITING_FOR_PREVIEW_WINDOW,
+ PREVIEW,
+ RECORD,
+ STILL_CAPTURE,
+ VIDEO_SNAPSHOT
+ } state;
+
+ // Number of zoom steps to simulate
+ static const unsigned int NUM_ZOOM_STEPS = 30;
+
+ // Full static camera info, object owned by someone else, such as
+ // Camera2Device.
+ const CameraMetadata *info;
+
+ // Fast-access static device information; this is a subset of the
+ // information available through the staticInfo() method, used for
+ // frequently-accessed values or values that have to be calculated from the
+ // static information.
+ struct DeviceInfo {
+ int32_t arrayWidth;
+ int32_t arrayHeight;
+ uint8_t bestFaceDetectMode;
+ int32_t maxFaces;
+ struct OverrideModes {
+ flashMode_t flashMode;
+ uint8_t wbMode;
+ focusMode_t focusMode;
+ OverrideModes():
+ flashMode(FLASH_MODE_INVALID),
+ wbMode(ANDROID_CONTROL_AWB_MODE_OFF),
+ focusMode(FOCUS_MODE_INVALID) {
+ }
+ };
+ DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
+ } fastInfo;
+
+ // Quirks information; these are short-lived flags to enable workarounds for
+ // incomplete HAL implementations
+ struct Quirks {
+ bool triggerAfWithAuto;
+ bool useZslFormat;
+ bool meteringCropRegion;
+ } quirks;
+
+ /**
+ * Parameter manipulation and setup methods
+ */
+
+ Parameters(int cameraId, int cameraFacing);
+ ~Parameters();
+
+ // Sets up default parameters
+ status_t initialize(const CameraMetadata *info);
+
+ // Build fast-access device static info from static info
+ status_t buildFastInfo();
+ // Query for quirks from static info
+ status_t buildQuirks();
+
+ // Get entry from camera static characteristics information. min/maxCount
+ // are used for error checking the number of values in the entry. 0 for
+ // max/minCount means to do no bounds check in that direction. In case of
+ // error, the entry data pointer is null and the count is 0.
+ camera_metadata_ro_entry_t staticInfo(uint32_t tag,
+ size_t minCount=0, size_t maxCount=0) const;
+
+ // Validate and update camera parameters based on new settings
+ status_t set(const String8 &paramString);
+
+ // Retrieve the current settings
+ String8 get() const;
+
+ // Update passed-in request for common parameters
+ status_t updateRequest(CameraMetadata *request) const;
+
+ // Add/update JPEG entries in metadata
+ status_t updateRequestJpeg(CameraMetadata *request) const;
+
+ // Calculate the crop region rectangle based on current stream sizes
+ struct CropRegion {
+ float left;
+ float top;
+ float width;
+ float height;
+
+ enum Outputs {
+ OUTPUT_PREVIEW = 0x01,
+ OUTPUT_VIDEO = 0x02,
+ OUTPUT_JPEG_THUMBNAIL = 0x04,
+ OUTPUT_PICTURE = 0x08,
+ };
+ };
+ CropRegion calculateCropRegion(CropRegion::Outputs outputs) const;
+
+ // Static methods for debugging and converting between camera1 and camera2
+ // parameters
+
+ static const char *getStateName(State state);
+
+ static int formatStringToEnum(const char *format);
+ static const char *formatEnumToString(int format);
+
+ static int wbModeStringToEnum(const char *wbMode);
+ static const char* wbModeEnumToString(uint8_t wbMode);
+ static int effectModeStringToEnum(const char *effectMode);
+ static int abModeStringToEnum(const char *abMode);
+ static int sceneModeStringToEnum(const char *sceneMode);
+ static flashMode_t flashModeStringToEnum(const char *flashMode);
+ static const char* flashModeEnumToString(flashMode_t flashMode);
+ static focusMode_t focusModeStringToEnum(const char *focusMode);
+ static const char* focusModeEnumToString(focusMode_t focusMode);
+ static status_t parseAreas(const char *areasCStr,
+ Vector<Area> *areas);
+
+ enum AreaKind
+ {
+ AREA_KIND_FOCUS,
+ AREA_KIND_METERING
+ };
+ status_t validateAreas(const Vector<Area> &areas,
+ size_t maxRegions,
+ AreaKind areaKind) const;
+ static bool boolFromString(const char *boolStr);
+
+ // Map from camera orientation + facing to gralloc transform enum
+ static int degToTransform(int degrees, bool mirror);
+
+ // API specifies FPS ranges are done in fixed point integer, with LSB = 0.001.
+ // Note that this doesn't apply to the (deprecated) single FPS value.
+ static const int kFpsToApiScale = 1000;
+
+ // Transform between (-1000,-1000)-(1000,1000) normalized coords from camera
+ // API and HAL2 (0,0)-(activePixelArray.width/height) coordinates
+ int arrayXToNormalized(int width) const;
+ int arrayYToNormalized(int height) const;
+ int normalizedXToArray(int x) const;
+ int normalizedYToArray(int y) const;
+
+ struct Range {
+ int min;
+ int max;
+ };
+
+ int32_t fpsFromRange(int32_t min, int32_t max) const;
+
+private:
+
+ // Convert between HAL2 sensor array coordinates and
+ // viewfinder crop-region relative array coordinates
+ int cropXToArray(int x) const;
+ int cropYToArray(int y) const;
+ int arrayXToCrop(int x) const;
+ int arrayYToCrop(int y) const;
+
+ // Convert between viewfinder crop-region relative array coordinates
+ // and camera API (-1000,1000)-(1000,1000) normalized coords
+ int cropXToNormalized(int x) const;
+ int cropYToNormalized(int y) const;
+ int normalizedXToCrop(int x) const;
+ int normalizedYToCrop(int y) const;
+};
+
+// This class encapsulates the Parameters class so that it can only be accessed
+// by constructing a Lock object, which locks the SharedParameter's mutex.
+class SharedParameters {
+ public:
+ SharedParameters(int cameraId, int cameraFacing):
+ mParameters(cameraId, cameraFacing) {
+ }
+
+ template<typename S, typename P>
+ class BaseLock {
+ public:
+ BaseLock(S &p):
+ mParameters(p.mParameters),
+ mSharedParameters(p) {
+ mSharedParameters.mLock.lock();
+ }
+
+ ~BaseLock() {
+ mSharedParameters.mLock.unlock();
+ }
+ P &mParameters;
+ private:
+ // Disallow copying, default construction
+ BaseLock();
+ BaseLock(const BaseLock &);
+ BaseLock &operator=(const BaseLock &);
+ S &mSharedParameters;
+ };
+ typedef BaseLock<SharedParameters, Parameters> Lock;
+ typedef BaseLock<const SharedParameters, const Parameters> ReadLock;
+
+ // Access static info, read-only and immutable, so no lock needed
+ camera_metadata_ro_entry_t staticInfo(uint32_t tag,
+ size_t minCount=0, size_t maxCount=0) const {
+ return mParameters.staticInfo(tag, minCount, maxCount);
+ }
+
+ // Only use for dumping or other debugging
+ const Parameters &unsafeAccess() {
+ return mParameters;
+ }
+ private:
+ Parameters mParameters;
+ mutable Mutex mLock;
+};
+
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
new file mode 100644
index 00000000..207f7807
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-StreamingProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <gui/SurfaceTextureClient.h>
+#include <media/hardware/MetadataBufferType.h>
+
+#include "StreamingProcessor.h"
+#include "Camera2Heap.h"
+#include "../Camera2Client.h"
+#include "../Camera2Device.h"
+
+namespace android {
+namespace camera2 {
+
+StreamingProcessor::StreamingProcessor(wp<Camera2Client> client):
+ mClient(client),
+ mActiveRequest(NONE),
+ mPreviewRequestId(Camera2Client::kPreviewRequestIdStart),
+ mPreviewStreamId(NO_STREAM),
+ mRecordingRequestId(Camera2Client::kRecordingRequestIdStart),
+ mRecordingStreamId(NO_STREAM),
+ mRecordingHeapCount(kDefaultRecordingHeapCount)
+{
+
+}
+
+StreamingProcessor::~StreamingProcessor() {
+ deletePreviewStream();
+ deleteRecordingStream();
+}
+
+status_t StreamingProcessor::setPreviewWindow(sp<ANativeWindow> window) {
+ ATRACE_CALL();
+ status_t res;
+
+ res = deletePreviewStream();
+ if (res != OK) return res;
+
+ Mutex::Autolock m(mMutex);
+
+ mPreviewWindow = window;
+
+ return OK;
+}
+
+bool StreamingProcessor::haveValidPreviewWindow() const {
+ Mutex::Autolock m(mMutex);
+ return mPreviewWindow != 0;
+}
+
+status_t StreamingProcessor::updatePreviewRequest(const Parameters &params) {
+ ATRACE_CALL();
+ status_t res;
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+
+ Mutex::Autolock m(mMutex);
+ if (mPreviewRequest.entryCount() == 0) {
+ res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ &mPreviewRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create default preview request: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = params.updateRequest(&mPreviewRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update common entries of preview "
+ "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+
+ res = mPreviewRequest.update(ANDROID_REQUEST_ID,
+ &mPreviewRequestId, 1);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update request id for preview: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t StreamingProcessor::updatePreviewStream(const Parameters &params) {
+ ATRACE_CALL();
+ Mutex::Autolock m(mMutex);
+
+ status_t res;
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ if (mPreviewStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight;
+ res = device->getStreamInfo(mPreviewStreamId,
+ &currentWidth, &currentHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying preview stream info: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)params.previewWidth ||
+ currentHeight != (uint32_t)params.previewHeight) {
+ ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
+ __FUNCTION__, client->getCameraId(), currentWidth, currentHeight,
+ params.previewWidth, params.previewHeight);
+ res = device->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error waiting for preview to drain: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ res = device->deleteStream(mPreviewStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for preview: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ mPreviewStreamId = NO_STREAM;
+ }
+ }
+
+ if (mPreviewStreamId == NO_STREAM) {
+ res = device->createStream(mPreviewWindow,
+ params.previewWidth, params.previewHeight,
+ CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0,
+ &mPreviewStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = device->setStreamTransform(mPreviewStreamId,
+ params.previewTransform);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set preview stream transform: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t StreamingProcessor::deletePreviewStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock m(mMutex);
+
+ if (mPreviewStreamId != NO_STREAM) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ ALOGV("%s: for cameraId %d on streamId %d",
+ __FUNCTION__, client->getCameraId(), mPreviewStreamId);
+
+ res = device->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Error waiting for preview to drain: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ res = device->deleteStream(mPreviewStreamId);
+ if (res != OK) {
+ ALOGE("%s: Unable to delete old preview stream: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ mPreviewStreamId = NO_STREAM;
+ }
+ return OK;
+}
+
+int StreamingProcessor::getPreviewStreamId() const {
+ Mutex::Autolock m(mMutex);
+ return mPreviewStreamId;
+}
+
+status_t StreamingProcessor::setRecordingBufferCount(size_t count) {
+ ATRACE_CALL();
+ // 32 is the current upper limit on the video buffer count for BufferQueue
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ if (count > 32) {
+ ALOGE("%s: Camera %d: Error setting %d as video buffer count value",
+ __FUNCTION__, client->getCameraId(), count);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock m(mMutex);
+
+ // Need to reallocate memory for heap
+ if (mRecordingHeapCount != count) {
+ if (mRecordingHeap != 0) {
+ mRecordingHeap.clear();
+ mRecordingHeap = NULL;
+ }
+ mRecordingHeapCount = count;
+ }
+
+ return OK;
+}
+
+status_t StreamingProcessor::updateRecordingRequest(const Parameters &params) {
+ ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock m(mMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+
+ if (mRecordingRequest.entryCount() == 0) {
+ res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD,
+ &mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create default recording request:"
+ " %s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = params.updateRequest(&mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update common entries of recording "
+ "request: %s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+
+ res = mRecordingRequest.update(ANDROID_REQUEST_ID,
+ &mRecordingRequestId, 1);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update request id for request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t StreamingProcessor::updateRecordingStream(const Parameters &params) {
+ ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock m(mMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ if (mRecordingConsumer == 0) {
+ // Create CPU buffer queue endpoint. We need one more buffer here so that we can
+ // always acquire and free a buffer when the heap is full; otherwise the consumer
+ // will have buffers in flight we'll never clear out.
+ mRecordingConsumer = new BufferItemConsumer(
+ GRALLOC_USAGE_HW_VIDEO_ENCODER,
+ mRecordingHeapCount + 1,
+ true);
+ mRecordingConsumer->setFrameAvailableListener(this);
+ mRecordingConsumer->setName(String8("Camera2-RecordingConsumer"));
+ mRecordingWindow = new SurfaceTextureClient(
+ mRecordingConsumer->getProducerInterface());
+ // Allocate memory later, since we don't know buffer size until receipt
+ }
+
+ if (mRecordingStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight;
+ res = device->getStreamInfo(mRecordingStreamId,
+ &currentWidth, &currentHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying recording output stream info: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)params.videoWidth ||
+ currentHeight != (uint32_t)params.videoHeight) {
+ // TODO: Should wait to be sure previous recording has finished
+ res = device->deleteStream(mRecordingStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for recording: %s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mRecordingStreamId = NO_STREAM;
+ }
+ }
+
+ if (mRecordingStreamId == NO_STREAM) {
+ mRecordingFrameCount = 0;
+ res = device->createStream(mRecordingWindow,
+ params.videoWidth, params.videoHeight,
+ CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create output stream for recording: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ return OK;
+}
+
+status_t StreamingProcessor::deleteRecordingStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock m(mMutex);
+
+ if (mRecordingStreamId != NO_STREAM) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ res = device->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Error waiting for HAL to drain: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ res = device->deleteStream(mRecordingStreamId);
+ if (res != OK) {
+ ALOGE("%s: Unable to delete recording stream: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ mRecordingStreamId = NO_STREAM;
+ }
+ return OK;
+}
+
+int StreamingProcessor::getRecordingStreamId() const {
+ return mRecordingStreamId;
+}
+
+status_t StreamingProcessor::startStream(StreamType type,
+ const Vector<uint8_t> &outputStreams) {
+ ATRACE_CALL();
+ status_t res;
+
+ if (type == NONE) return INVALID_OPERATION;
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+
+ ALOGV("%s: Camera %d: type = %d", __FUNCTION__, client->getCameraId(), type);
+
+ Mutex::Autolock m(mMutex);
+
+ CameraMetadata &request = (type == PREVIEW) ?
+ mPreviewRequest : mRecordingRequest;
+
+ res = request.update(
+ ANDROID_REQUEST_OUTPUT_STREAMS,
+ outputStreams);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
+ res = request.sort();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+
+ res = client->getCameraDevice()->setStreamingRequest(request);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set preview request to start preview: "
+ "%s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mActiveRequest = type;
+
+ return OK;
+}
+
+status_t StreamingProcessor::stopStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock m(mMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return INVALID_OPERATION;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ res = device->clearStreamingRequest();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't clear stream request: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mActiveRequest = NONE;
+
+ return OK;
+}
+
+int32_t StreamingProcessor::getActiveRequestId() const {
+ Mutex::Autolock m(mMutex);
+ switch (mActiveRequest) {
+ case NONE:
+ return 0;
+ case PREVIEW:
+ return mPreviewRequestId;
+ case RECORD:
+ return mRecordingRequestId;
+ default:
+ ALOGE("%s: Unexpected mode %d", __FUNCTION__, mActiveRequest);
+ return 0;
+ }
+}
+
+status_t StreamingProcessor::incrementStreamingIds() {
+ ATRACE_CALL();
+ Mutex::Autolock m(mMutex);
+
+ status_t res;
+ mPreviewRequestId++;
+ if (mPreviewRequestId >= Camera2Client::kPreviewRequestIdEnd) {
+ mPreviewRequestId = Camera2Client::kPreviewRequestIdStart;
+ }
+ mRecordingRequestId++;
+ if (mRecordingRequestId >= Camera2Client::kRecordingRequestIdEnd) {
+ mRecordingRequestId = Camera2Client::kRecordingRequestIdStart;
+ }
+ return OK;
+}
+
+void StreamingProcessor::onFrameAvailable() {
+ ATRACE_CALL();
+ status_t res;
+ sp<Camera2Heap> recordingHeap;
+ size_t heapIdx = 0;
+ nsecs_t timestamp;
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return;
+
+ {
+ /* acquire SharedParameters before mMutex so we don't dead lock
+ with Camera2Client code calling into StreamingProcessor */
+ SharedParameters::Lock l(client->getParameters());
+ Mutex::Autolock m(mMutex);
+ BufferItemConsumer::BufferItem imgBuffer;
+ res = mRecordingConsumer->acquireBuffer(&imgBuffer);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return;
+ }
+ timestamp = imgBuffer.mTimestamp;
+
+ mRecordingFrameCount++;
+ ALOGV("OnRecordingFrame: Frame %d", mRecordingFrameCount);
+
+ // TODO: Signal errors here upstream
+ if (l.mParameters.state != Parameters::RECORD &&
+ l.mParameters.state != Parameters::VIDEO_SNAPSHOT) {
+ ALOGV("%s: Camera %d: Discarding recording image buffers "
+ "received after recording done", __FUNCTION__,
+ client->getCameraId());
+ mRecordingConsumer->releaseBuffer(imgBuffer);
+ return;
+ }
+
+ if (mRecordingHeap == 0) {
+ const size_t bufferSize = 4 + sizeof(buffer_handle_t);
+ ALOGV("%s: Camera %d: Creating recording heap with %d buffers of "
+ "size %d bytes", __FUNCTION__, client->getCameraId(),
+ mRecordingHeapCount, bufferSize);
+
+ mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount,
+ "Camera2Client::RecordingHeap");
+ if (mRecordingHeap->mHeap->getSize() == 0) {
+ ALOGE("%s: Camera %d: Unable to allocate memory for recording",
+ __FUNCTION__, client->getCameraId());
+ mRecordingConsumer->releaseBuffer(imgBuffer);
+ return;
+ }
+ for (size_t i = 0; i < mRecordingBuffers.size(); i++) {
+ if (mRecordingBuffers[i].mBuf !=
+ BufferItemConsumer::INVALID_BUFFER_SLOT) {
+ ALOGE("%s: Camera %d: Non-empty recording buffers list!",
+ __FUNCTION__, client->getCameraId());
+ }
+ }
+ mRecordingBuffers.clear();
+ mRecordingBuffers.setCapacity(mRecordingHeapCount);
+ mRecordingBuffers.insertAt(0, mRecordingHeapCount);
+
+ mRecordingHeapHead = 0;
+ mRecordingHeapFree = mRecordingHeapCount;
+ }
+
+ if ( mRecordingHeapFree == 0) {
+ ALOGE("%s: Camera %d: No free recording buffers, dropping frame",
+ __FUNCTION__, client->getCameraId());
+ mRecordingConsumer->releaseBuffer(imgBuffer);
+ return;
+ }
+
+ heapIdx = mRecordingHeapHead;
+ mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount;
+ mRecordingHeapFree--;
+
+ ALOGV("%s: Camera %d: Timestamp %lld",
+ __FUNCTION__, client->getCameraId(), timestamp);
+
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap =
+ mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset,
+ &size);
+
+ uint8_t *data = (uint8_t*)heap->getBase() + offset;
+ uint32_t type = kMetadataBufferTypeGrallocSource;
+ *((uint32_t*)data) = type;
+ *((buffer_handle_t*)(data + 4)) = imgBuffer.mGraphicBuffer->handle;
+ ALOGV("%s: Camera %d: Sending out buffer_handle_t %p",
+ __FUNCTION__, client->getCameraId(),
+ imgBuffer.mGraphicBuffer->handle);
+ mRecordingBuffers.replaceAt(imgBuffer, heapIdx);
+ recordingHeap = mRecordingHeap;
+ }
+
+ // Call outside locked parameters to allow re-entrancy from notification
+ Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient);
+ if (l.mCameraClient != 0) {
+ l.mCameraClient->dataCallbackTimestamp(timestamp,
+ CAMERA_MSG_VIDEO_FRAME,
+ recordingHeap->mBuffers[heapIdx]);
+ }
+}
+
+void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) {
+ ATRACE_CALL();
+ status_t res;
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return;
+
+ Mutex::Autolock m(mMutex);
+ // Make sure this is for the current heap
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) {
+ ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release "
+ "(got %x, expected %x)", __FUNCTION__, client->getCameraId(),
+ heap->getHeapID(), mRecordingHeap->mHeap->getHeapID());
+ return;
+ }
+ uint8_t *data = (uint8_t*)heap->getBase() + offset;
+ uint32_t type = *(uint32_t*)data;
+ if (type != kMetadataBufferTypeGrallocSource) {
+ ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)",
+ __FUNCTION__, client->getCameraId(), type,
+ kMetadataBufferTypeGrallocSource);
+ return;
+ }
+
+ // Release the buffer back to the recording queue
+
+ buffer_handle_t imgHandle = *(buffer_handle_t*)(data + 4);
+
+ size_t itemIndex;
+ for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) {
+ const BufferItemConsumer::BufferItem item =
+ mRecordingBuffers[itemIndex];
+ if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT &&
+ item.mGraphicBuffer->handle == imgHandle) {
+ break;
+ }
+ }
+ if (itemIndex == mRecordingBuffers.size()) {
+ ALOGE("%s: Camera %d: Can't find buffer_handle_t %p in list of "
+ "outstanding buffers", __FUNCTION__, client->getCameraId(),
+ imgHandle);
+ return;
+ }
+
+ ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__,
+ client->getCameraId(), imgHandle);
+
+ res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to free recording frame "
+ "(buffer_handle_t: %p): %s (%d)", __FUNCTION__,
+ client->getCameraId(), imgHandle, strerror(-res), res);
+ return;
+ }
+ mRecordingBuffers.replaceAt(itemIndex);
+
+ mRecordingHeapFree++;
+}
+
+
+status_t StreamingProcessor::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+
+ result.append(" Current requests:\n");
+ if (mPreviewRequest.entryCount() != 0) {
+ result.append(" Preview request:\n");
+ write(fd, result.string(), result.size());
+ mPreviewRequest.dump(fd, 2, 6);
+ } else {
+ result.append(" Preview request: undefined\n");
+ write(fd, result.string(), result.size());
+ }
+
+ if (mRecordingRequest.entryCount() != 0) {
+ result = " Recording request:\n";
+ write(fd, result.string(), result.size());
+ mRecordingRequest.dump(fd, 2, 6);
+ } else {
+ result = " Recording request: undefined\n";
+ write(fd, result.string(), result.size());
+ }
+
+ return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h
new file mode 100644
index 00000000..96b100f0
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H
+
+#include <utils/Mutex.h>
+#include <utils/String16.h>
+#include <gui/BufferItemConsumer.h>
+
+#include "Parameters.h"
+#include "CameraMetadata.h"
+
+namespace android {
+
+class Camera2Client;
+class IMemory;
+
+namespace camera2 {
+
+class Camera2Heap;
+
+/**
+ * Management and processing for preview and recording streams
+ */
+class StreamingProcessor: public BufferItemConsumer::FrameAvailableListener {
+ public:
+ StreamingProcessor(wp<Camera2Client> client);
+ ~StreamingProcessor();
+
+ status_t setPreviewWindow(sp<ANativeWindow> window);
+
+ bool haveValidPreviewWindow() const;
+
+ status_t updatePreviewRequest(const Parameters &params);
+ status_t updatePreviewStream(const Parameters &params);
+ status_t deletePreviewStream();
+ int getPreviewStreamId() const;
+
+ status_t setRecordingBufferCount(size_t count);
+ status_t updateRecordingRequest(const Parameters &params);
+ status_t updateRecordingStream(const Parameters &params);
+ status_t deleteRecordingStream();
+ int getRecordingStreamId() const;
+
+ enum StreamType {
+ NONE,
+ PREVIEW,
+ RECORD
+ };
+ status_t startStream(StreamType type,
+ const Vector<uint8_t> &outputStreams);
+
+ status_t stopStream();
+
+ // Returns the request ID for the currently streaming request
+ // Returns 0 if there is no active request.
+ status_t getActiveRequestId() const;
+ status_t incrementStreamingIds();
+
+ // Callback for new recording frames from HAL
+ virtual void onFrameAvailable();
+ // Callback from stagefright which returns used recording frames
+ void releaseRecordingFrame(const sp<IMemory>& mem);
+
+ status_t dump(int fd, const Vector<String16>& args);
+
+ private:
+ mutable Mutex mMutex;
+
+ enum {
+ NO_STREAM = -1
+ };
+
+ wp<Camera2Client> mClient;
+
+ StreamType mActiveRequest;
+
+ // Preview-related members
+ int32_t mPreviewRequestId;
+ int mPreviewStreamId;
+ CameraMetadata mPreviewRequest;
+ sp<ANativeWindow> mPreviewWindow;
+
+ // Recording-related members
+ int32_t mRecordingRequestId;
+ int mRecordingStreamId;
+ int mRecordingFrameCount;
+ sp<BufferItemConsumer> mRecordingConsumer;
+ sp<ANativeWindow> mRecordingWindow;
+ CameraMetadata mRecordingRequest;
+ sp<camera2::Camera2Heap> mRecordingHeap;
+
+ static const size_t kDefaultRecordingHeapCount = 8;
+ size_t mRecordingHeapCount;
+ Vector<BufferItemConsumer::BufferItem> mRecordingBuffers;
+ size_t mRecordingHeapHead, mRecordingHeapFree;
+
+};
+
+
+}; // namespace camera2
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.cpp b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
new file mode 100644
index 00000000..1937955a
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "Camera2-ZslProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+//#define LOG_NNDEBUG 0
+
+#ifdef LOG_NNDEBUG
+#define ALOGVV(...) ALOGV(__VA_ARGS__)
+#else
+#define ALOGVV(...) ((void)0)
+#endif
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "ZslProcessor.h"
+#include <gui/SurfaceTextureClient.h>
+#include "../Camera2Device.h"
+#include "../Camera2Client.h"
+
+
+namespace android {
+namespace camera2 {
+
+ZslProcessor::ZslProcessor(
+ wp<Camera2Client> client,
+ wp<CaptureSequencer> sequencer):
+ Thread(false),
+ mState(RUNNING),
+ mClient(client),
+ mSequencer(sequencer),
+ mZslBufferAvailable(false),
+ mZslStreamId(NO_STREAM),
+ mZslReprocessStreamId(NO_STREAM),
+ mFrameListHead(0),
+ mZslQueueHead(0),
+ mZslQueueTail(0) {
+ mZslQueue.insertAt(0, kZslBufferDepth);
+ mFrameList.insertAt(0, kFrameListDepth);
+ sp<CaptureSequencer> captureSequencer = mSequencer.promote();
+ if (captureSequencer != 0) captureSequencer->setZslProcessor(this);
+}
+
+ZslProcessor::~ZslProcessor() {
+ ALOGV("%s: Exit", __FUNCTION__);
+ deleteStream();
+}
+
+void ZslProcessor::onFrameAvailable() {
+ Mutex::Autolock l(mInputMutex);
+ if (!mZslBufferAvailable) {
+ mZslBufferAvailable = true;
+ mZslBufferAvailableSignal.signal();
+ }
+}
+
+void ZslProcessor::onFrameAvailable(int32_t frameId, const CameraMetadata &frame) {
+ Mutex::Autolock l(mInputMutex);
+ camera_metadata_ro_entry_t entry;
+ entry = frame.find(ANDROID_SENSOR_TIMESTAMP);
+ nsecs_t timestamp = entry.data.i64[0];
+ ALOGVV("Got preview frame for timestamp %lld", timestamp);
+
+ if (mState != RUNNING) return;
+
+ mFrameList.editItemAt(mFrameListHead) = frame;
+ mFrameListHead = (mFrameListHead + 1) % kFrameListDepth;
+
+ findMatchesLocked();
+}
+
+void ZslProcessor::onBufferReleased(buffer_handle_t *handle) {
+ Mutex::Autolock l(mInputMutex);
+
+ // Verify that the buffer is in our queue
+ size_t i = 0;
+ for (; i < mZslQueue.size(); i++) {
+ if (&(mZslQueue[i].buffer.mGraphicBuffer->handle) == handle) break;
+ }
+ if (i == mZslQueue.size()) {
+ ALOGW("%s: Released buffer %p not found in queue",
+ __FUNCTION__, handle);
+ }
+
+ // Erase entire ZSL queue since we've now completed the capture and preview
+ // is stopped.
+ clearZslQueueLocked();
+
+ mState = RUNNING;
+}
+
+status_t ZslProcessor::updateStream(const Parameters &params) {
+ ATRACE_CALL();
+ ALOGV("%s: Configuring ZSL streams", __FUNCTION__);
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ if (mZslConsumer == 0) {
+ // Create CPU buffer queue endpoint
+ mZslConsumer = new BufferItemConsumer(
+ GRALLOC_USAGE_HW_CAMERA_ZSL,
+ kZslBufferDepth,
+ true);
+ mZslConsumer->setFrameAvailableListener(this);
+ mZslConsumer->setName(String8("Camera2Client::ZslConsumer"));
+ mZslWindow = new SurfaceTextureClient(
+ mZslConsumer->getProducerInterface());
+ }
+
+ if (mZslStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight;
+ res = device->getStreamInfo(mZslStreamId,
+ &currentWidth, &currentHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying capture output stream info: "
+ "%s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
+ currentHeight != (uint32_t)params.fastInfo.arrayHeight) {
+ res = device->deleteReprocessStream(mZslReprocessStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old reprocess stream "
+ "for ZSL: %s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+ __FUNCTION__, client->getCameraId(), mZslStreamId);
+ res = device->deleteStream(mZslStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for ZSL: %s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mZslStreamId = NO_STREAM;
+ }
+ }
+
+ if (mZslStreamId == NO_STREAM) {
+ // Create stream for HAL production
+ // TODO: Sort out better way to select resolution for ZSL
+ int streamType = params.quirks.useZslFormat ?
+ (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL :
+ (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ res = device->createStream(mZslWindow,
+ params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
+ streamType, 0,
+ &mZslStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create output stream for ZSL: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ res = device->createReprocessStreamFromStream(mZslStreamId,
+ &mZslReprocessStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create reprocess stream for ZSL: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ }
+ client->registerFrameListener(Camera2Client::kPreviewRequestIdStart,
+ Camera2Client::kPreviewRequestIdEnd,
+ this);
+
+ return OK;
+}
+
+status_t ZslProcessor::deleteStream() {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ if (mZslStreamId != NO_STREAM) {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<Camera2Device> device = client->getCameraDevice();
+
+ res = device->deleteReprocessStream(mZslReprocessStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Cannot delete ZSL reprocessing stream %d: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ mZslReprocessStreamId, strerror(-res), res);
+ return res;
+ }
+
+ mZslReprocessStreamId = NO_STREAM;
+ res = device->deleteStream(mZslStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Cannot delete ZSL output stream %d: "
+ "%s (%d)", __FUNCTION__, client->getCameraId(),
+ mZslStreamId, strerror(-res), res);
+ return res;
+ }
+
+ mZslWindow.clear();
+ mZslConsumer.clear();
+
+ mZslStreamId = NO_STREAM;
+ }
+ return OK;
+}
+
+int ZslProcessor::getStreamId() const {
+ Mutex::Autolock l(mInputMutex);
+ return mZslStreamId;
+}
+
+int ZslProcessor::getReprocessStreamId() const {
+ Mutex::Autolock l(mInputMutex);
+ return mZslReprocessStreamId;
+}
+
+status_t ZslProcessor::pushToReprocess(int32_t requestId) {
+ ALOGV("%s: Send in reprocess request with id %d",
+ __FUNCTION__, requestId);
+ Mutex::Autolock l(mInputMutex);
+ status_t res;
+ sp<Camera2Client> client = mClient.promote();
+
+ if (client == 0) return INVALID_OPERATION;
+
+ IF_ALOGV() {
+ dumpZslQueue(-1);
+ }
+
+ if (mZslQueueTail != mZslQueueHead) {
+ CameraMetadata request;
+ size_t index = mZslQueueTail;
+ while (index != mZslQueueHead) {
+ if (!mZslQueue[index].frame.isEmpty()) {
+ request = mZslQueue[index].frame;
+ break;
+ }
+ index = (index + 1) % kZslBufferDepth;
+ }
+ if (index == mZslQueueHead) {
+ ALOGV("%s: ZSL queue has no valid frames to send yet.",
+ __FUNCTION__);
+ return NOT_ENOUGH_DATA;
+ }
+ // Verify that the frame is reasonable for reprocessing
+
+ camera_metadata_entry_t entry;
+ entry = request.find(ANDROID_CONTROL_AE_STATE);
+ if (entry.count == 0) {
+ ALOGE("%s: ZSL queue frame has no AE state field!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_CONVERGED &&
+ entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_LOCKED) {
+ ALOGV("%s: ZSL queue frame AE state is %d, need full capture",
+ __FUNCTION__, entry.data.u8[0]);
+ return NOT_ENOUGH_DATA;
+ }
+
+ buffer_handle_t *handle =
+ &(mZslQueue[index].buffer.mGraphicBuffer->handle);
+
+ uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
+ res = request.update(ANDROID_REQUEST_TYPE,
+ &requestType, 1);
+ uint8_t inputStreams[1] = { mZslReprocessStreamId };
+ if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
+ inputStreams, 1);
+ uint8_t outputStreams[1] = { client->getCaptureStreamId() };
+ if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
+ outputStreams, 1);
+ res = request.update(ANDROID_REQUEST_ID,
+ &requestId, 1);
+
+ if (res != OK ) {
+ ALOGE("%s: Unable to update frame to a reprocess request", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ res = client->stopStream();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: "
+ "%s (%d)",
+ __FUNCTION__, client->getCameraId(), strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ // TODO: have push-and-clear be atomic
+ res = client->getCameraDevice()->pushReprocessBuffer(mZslReprocessStreamId,
+ handle, this);
+ if (res != OK) {
+ ALOGE("%s: Unable to push buffer for reprocessing: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ // Update JPEG settings
+ {
+ SharedParameters::Lock l(client->getParameters());
+ res = l.mParameters.updateRequestJpeg(&request);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update JPEG entries of ZSL "
+ "capture request: %s (%d)", __FUNCTION__,
+ client->getCameraId(),
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ mLatestCapturedRequest = request;
+ res = client->getCameraDevice()->capture(request);
+ if (res != OK ) {
+ ALOGE("%s: Unable to send ZSL reprocess request to capture: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ mState = LOCKED;
+ } else {
+ ALOGV("%s: No ZSL buffers yet", __FUNCTION__);
+ return NOT_ENOUGH_DATA;
+ }
+ return OK;
+}
+
+status_t ZslProcessor::clearZslQueue() {
+ Mutex::Autolock l(mInputMutex);
+ // If in middle of capture, can't clear out queue
+ if (mState == LOCKED) return OK;
+
+ return clearZslQueueLocked();
+}
+
+status_t ZslProcessor::clearZslQueueLocked() {
+ for (size_t i = 0; i < mZslQueue.size(); i++) {
+ if (mZslQueue[i].buffer.mTimestamp != 0) {
+ mZslConsumer->releaseBuffer(mZslQueue[i].buffer);
+ }
+ mZslQueue.replaceAt(i);
+ }
+ mZslQueueHead = 0;
+ mZslQueueTail = 0;
+ return OK;
+}
+
+void ZslProcessor::dump(int fd, const Vector<String16>& args) const {
+ Mutex::Autolock l(mInputMutex);
+ if (!mLatestCapturedRequest.isEmpty()) {
+ String8 result(" Latest ZSL capture request:\n");
+ write(fd, result.string(), result.size());
+ mLatestCapturedRequest.dump(fd, 2, 6);
+ } else {
+ String8 result(" Latest ZSL capture request: none yet\n");
+ write(fd, result.string(), result.size());
+ }
+ dumpZslQueue(fd);
+}
+
+bool ZslProcessor::threadLoop() {
+ status_t res;
+
+ {
+ Mutex::Autolock l(mInputMutex);
+ while (!mZslBufferAvailable) {
+ res = mZslBufferAvailableSignal.waitRelative(mInputMutex,
+ kWaitDuration);
+ if (res == TIMED_OUT) return true;
+ }
+ mZslBufferAvailable = false;
+ }
+
+ do {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return false;
+ res = processNewZslBuffer(client);
+ } while (res == OK);
+
+ return true;
+}
+
+status_t ZslProcessor::processNewZslBuffer(sp<Camera2Client> &client) {
+ ATRACE_CALL();
+ status_t res;
+
+ ALOGVV("Trying to get next buffer");
+ BufferItemConsumer::BufferItem item;
+ res = mZslConsumer->acquireBuffer(&item);
+ if (res != OK) {
+ if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
+ ALOGE("%s: Camera %d: Error receiving ZSL image buffer: "
+ "%s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ } else {
+ ALOGVV(" No buffer");
+ }
+ return res;
+ }
+
+ Mutex::Autolock l(mInputMutex);
+
+ if (mState == LOCKED) {
+ ALOGVV("In capture, discarding new ZSL buffers");
+ mZslConsumer->releaseBuffer(item);
+ return OK;
+ }
+
+ ALOGVV("Got ZSL buffer: head: %d, tail: %d", mZslQueueHead, mZslQueueTail);
+
+ if ( (mZslQueueHead + 1) % kZslBufferDepth == mZslQueueTail) {
+ ALOGVV("Releasing oldest buffer");
+ mZslConsumer->releaseBuffer(mZslQueue[mZslQueueTail].buffer);
+ mZslQueue.replaceAt(mZslQueueTail);
+ mZslQueueTail = (mZslQueueTail + 1) % kZslBufferDepth;
+ }
+
+ ZslPair &queueHead = mZslQueue.editItemAt(mZslQueueHead);
+
+ queueHead.buffer = item;
+ queueHead.frame.release();
+
+ mZslQueueHead = (mZslQueueHead + 1) % kZslBufferDepth;
+
+ ALOGVV(" Acquired buffer, timestamp %lld", queueHead.buffer.mTimestamp);
+
+ findMatchesLocked();
+
+ return OK;
+}
+
+void ZslProcessor::findMatchesLocked() {
+ ALOGVV("Scanning");
+ for (size_t i = 0; i < mZslQueue.size(); i++) {
+ ZslPair &queueEntry = mZslQueue.editItemAt(i);
+ nsecs_t bufferTimestamp = queueEntry.buffer.mTimestamp;
+ IF_ALOGV() {
+ camera_metadata_entry_t entry;
+ nsecs_t frameTimestamp = 0;
+ if (!queueEntry.frame.isEmpty()) {
+ entry = queueEntry.frame.find(ANDROID_SENSOR_TIMESTAMP);
+ frameTimestamp = entry.data.i64[0];
+ }
+ ALOGVV(" %d: b: %lld\tf: %lld", i,
+ bufferTimestamp, frameTimestamp );
+ }
+ if (queueEntry.frame.isEmpty() && bufferTimestamp != 0) {
+ // Have buffer, no matching frame. Look for one
+ for (size_t j = 0; j < mFrameList.size(); j++) {
+ bool match = false;
+ CameraMetadata &frame = mFrameList.editItemAt(j);
+ if (!frame.isEmpty()) {
+ camera_metadata_entry_t entry;
+ entry = frame.find(ANDROID_SENSOR_TIMESTAMP);
+ if (entry.count == 0) {
+ ALOGE("%s: Can't find timestamp in frame!",
+ __FUNCTION__);
+ continue;
+ }
+ nsecs_t frameTimestamp = entry.data.i64[0];
+ if (bufferTimestamp == frameTimestamp) {
+ ALOGVV("%s: Found match %lld", __FUNCTION__,
+ frameTimestamp);
+ match = true;
+ } else {
+ int64_t delta = abs(bufferTimestamp - frameTimestamp);
+ if ( delta < 1000000) {
+ ALOGVV("%s: Found close match %lld (delta %lld)",
+ __FUNCTION__, bufferTimestamp, delta);
+ match = true;
+ }
+ }
+ }
+ if (match) {
+ queueEntry.frame.acquire(frame);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void ZslProcessor::dumpZslQueue(int fd) const {
+ String8 header("ZSL queue contents:");
+ String8 indent(" ");
+ ALOGV("%s", header.string());
+ if (fd != -1) {
+ header = indent + header + "\n";
+ write(fd, header.string(), header.size());
+ }
+ for (size_t i = 0; i < mZslQueue.size(); i++) {
+ const ZslPair &queueEntry = mZslQueue[i];
+ nsecs_t bufferTimestamp = queueEntry.buffer.mTimestamp;
+ camera_metadata_ro_entry_t entry;
+ nsecs_t frameTimestamp = 0;
+ int frameAeState = -1;
+ if (!queueEntry.frame.isEmpty()) {
+ entry = queueEntry.frame.find(ANDROID_SENSOR_TIMESTAMP);
+ if (entry.count > 0) frameTimestamp = entry.data.i64[0];
+ entry = queueEntry.frame.find(ANDROID_CONTROL_AE_STATE);
+ if (entry.count > 0) frameAeState = entry.data.u8[0];
+ }
+ String8 result =
+ String8::format(" %d: b: %lld\tf: %lld, AE state: %d", i,
+ bufferTimestamp, frameTimestamp, frameAeState);
+ ALOGV("%s", result.string());
+ if (fd != -1) {
+ result = indent + result + "\n";
+ write(fd, result.string(), result.size());
+ }
+
+ }
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/camera2/ZslProcessor.h
new file mode 100644
index 00000000..c80e7f4d
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 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 ANDROID_SERVERS_CAMERA_CAMERA2_ZSLPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_ZSLPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+#include <gui/BufferItemConsumer.h>
+#include "Parameters.h"
+#include "FrameProcessor.h"
+#include "CameraMetadata.h"
+#include "Camera2Heap.h"
+#include "../Camera2Device.h"
+
+namespace android {
+
+class Camera2Client;
+
+namespace camera2 {
+
+class CaptureSequencer;
+
+/***
+ * ZSL queue processing
+ */
+class ZslProcessor:
+ virtual public Thread,
+ virtual public BufferItemConsumer::FrameAvailableListener,
+ virtual public FrameProcessor::FilteredListener,
+ virtual public Camera2Device::BufferReleasedListener {
+ public:
+ ZslProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer);
+ ~ZslProcessor();
+
+ // From mZslConsumer
+ virtual void onFrameAvailable();
+ // From FrameProcessor
+ virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+
+ virtual void onBufferReleased(buffer_handle_t *handle);
+
+ status_t updateStream(const Parameters &params);
+ status_t deleteStream();
+ int getStreamId() const;
+ int getReprocessStreamId() const;
+
+ status_t pushToReprocess(int32_t requestId);
+ status_t clearZslQueue();
+
+ void dump(int fd, const Vector<String16>& args) const;
+ private:
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+
+ enum {
+ RUNNING,
+ LOCKED
+ } mState;
+
+ wp<Camera2Client> mClient;
+ wp<CaptureSequencer> mSequencer;
+
+ mutable Mutex mInputMutex;
+ bool mZslBufferAvailable;
+ Condition mZslBufferAvailableSignal;
+
+ enum {
+ NO_STREAM = -1
+ };
+
+ int mZslStreamId;
+ int mZslReprocessStreamId;
+ sp<BufferItemConsumer> mZslConsumer;
+ sp<ANativeWindow> mZslWindow;
+
+ struct ZslPair {
+ BufferItemConsumer::BufferItem buffer;
+ CameraMetadata frame;
+ };
+
+ static const size_t kZslBufferDepth = 4;
+ static const size_t kFrameListDepth = kZslBufferDepth * 2;
+ Vector<CameraMetadata> mFrameList;
+ size_t mFrameListHead;
+
+ ZslPair mNextPair;
+
+ Vector<ZslPair> mZslQueue;
+ size_t mZslQueueHead;
+ size_t mZslQueueTail;
+
+ CameraMetadata mLatestCapturedRequest;
+
+ virtual bool threadLoop();
+
+ status_t processNewZslBuffer(sp<Camera2Client> &client);
+
+ // Match up entries from frame list to buffers in ZSL queue
+ void findMatchesLocked();
+
+ status_t clearZslQueueLocked();
+
+ void dumpZslQueue(int id) const;
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif