summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/Android.mk21
-rw-r--r--services/audioflinger/AudioFlinger.cpp292
-rw-r--r--services/audioflinger/AudioFlinger.h47
-rw-r--r--services/audioflinger/AudioMixer.cpp1
-rw-r--r--services/audioflinger/AudioPolicyService.cpp158
-rw-r--r--services/audioflinger/AudioPolicyService.h39
-rw-r--r--services/audioflinger/AudioResampler.h11
-rw-r--r--services/audioflinger/AudioWatchdog.cpp5
-rw-r--r--services/audioflinger/Configuration.h47
-rw-r--r--services/audioflinger/Effects.cpp116
-rw-r--r--services/audioflinger/Effects.h14
-rw-r--r--services/audioflinger/FastMixer.cpp246
-rw-r--r--services/audioflinger/FastMixer.h19
-rw-r--r--services/audioflinger/FastMixerState.cpp1
-rw-r--r--services/audioflinger/PlaybackTracks.h32
-rw-r--r--services/audioflinger/RecordTracks.h5
-rw-r--r--services/audioflinger/ServiceUtilities.cpp16
-rw-r--r--services/audioflinger/ServiceUtilities.h2
-rw-r--r--services/audioflinger/StateQueue.cpp1
-rw-r--r--services/audioflinger/StateQueue.h8
-rw-r--r--services/audioflinger/StateQueueInstantiations.cpp1
-rw-r--r--services/audioflinger/Threads.cpp1448
-rw-r--r--services/audioflinger/Threads.h193
-rw-r--r--services/audioflinger/TrackBase.h26
-rw-r--r--services/audioflinger/Tracks.cpp727
-rw-r--r--services/camera/libcameraservice/Android.mk50
-rw-r--r--services/camera/libcameraservice/CameraDeviceFactory.cpp71
-rw-r--r--services/camera/libcameraservice/CameraDeviceFactory.h45
-rw-r--r--services/camera/libcameraservice/CameraService.cpp260
-rw-r--r--services/camera/libcameraservice/CameraService.h66
-rw-r--r--services/camera/libcameraservice/api1/Camera2Client.cpp (renamed from services/camera/libcameraservice/Camera2Client.cpp)192
-rw-r--r--services/camera/libcameraservice/api1/Camera2Client.h (renamed from services/camera/libcameraservice/Camera2Client.h)38
-rw-r--r--services/camera/libcameraservice/api1/CameraClient.cpp (renamed from services/camera/libcameraservice/CameraClient.cpp)31
-rw-r--r--services/camera/libcameraservice/api1/CameraClient.h (renamed from services/camera/libcameraservice/CameraClient.h)5
-rw-r--r--services/camera/libcameraservice/api1/client2/BurstCapture.cpp (renamed from services/camera/libcameraservice/camera2/BurstCapture.cpp)4
-rw-r--r--services/camera/libcameraservice/api1/client2/BurstCapture.h (renamed from services/camera/libcameraservice/camera2/BurstCapture.h)5
-rw-r--r--services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp (renamed from services/camera/libcameraservice/camera2/CallbackProcessor.cpp)71
-rw-r--r--services/camera/libcameraservice/api1/client2/CallbackProcessor.h (renamed from services/camera/libcameraservice/camera2/CallbackProcessor.h)12
-rw-r--r--services/camera/libcameraservice/api1/client2/Camera2Heap.h (renamed from services/camera/libcameraservice/camera2/Camera2Heap.h)0
-rw-r--r--services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp (renamed from services/camera/libcameraservice/camera2/CaptureSequencer.cpp)48
-rw-r--r--services/camera/libcameraservice/api1/client2/CaptureSequencer.h (renamed from services/camera/libcameraservice/camera2/CaptureSequencer.h)5
-rw-r--r--services/camera/libcameraservice/api1/client2/FrameProcessor.cpp (renamed from services/camera/libcameraservice/camera2/FrameProcessor.cpp)162
-rw-r--r--services/camera/libcameraservice/api1/client2/FrameProcessor.h111
-rw-r--r--services/camera/libcameraservice/api1/client2/JpegCompressor.cpp (renamed from services/camera/libcameraservice/camera2/JpegCompressor.cpp)3
-rw-r--r--services/camera/libcameraservice/api1/client2/JpegCompressor.h (renamed from services/camera/libcameraservice/camera2/JpegCompressor.h)0
-rw-r--r--services/camera/libcameraservice/api1/client2/JpegProcessor.cpp (renamed from services/camera/libcameraservice/camera2/JpegProcessor.cpp)15
-rw-r--r--services/camera/libcameraservice/api1/client2/JpegProcessor.h (renamed from services/camera/libcameraservice/camera2/JpegProcessor.h)3
-rw-r--r--services/camera/libcameraservice/api1/client2/Parameters.cpp (renamed from services/camera/libcameraservice/camera2/Parameters.cpp)327
-rw-r--r--services/camera/libcameraservice/api1/client2/Parameters.h (renamed from services/camera/libcameraservice/camera2/Parameters.h)23
-rw-r--r--services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp (renamed from services/camera/libcameraservice/camera2/StreamingProcessor.cpp)27
-rw-r--r--services/camera/libcameraservice/api1/client2/StreamingProcessor.h (renamed from services/camera/libcameraservice/camera2/StreamingProcessor.h)10
-rw-r--r--services/camera/libcameraservice/api1/client2/ZslProcessor.cpp (renamed from services/camera/libcameraservice/camera2/ZslProcessor.cpp)29
-rw-r--r--services/camera/libcameraservice/api1/client2/ZslProcessor.h (renamed from services/camera/libcameraservice/camera2/ZslProcessor.h)14
-rw-r--r--services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp (renamed from services/camera/libcameraservice/camera2/ZslProcessor3.cpp)20
-rw-r--r--services/camera/libcameraservice/api1/client2/ZslProcessor3.h (renamed from services/camera/libcameraservice/camera2/ZslProcessor3.h)15
-rw-r--r--services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h (renamed from services/camera/libcameraservice/camera2/ZslProcessorInterface.h)0
-rw-r--r--services/camera/libcameraservice/api2/CameraDeviceClient.cpp680
-rw-r--r--services/camera/libcameraservice/api2/CameraDeviceClient.h154
-rw-r--r--services/camera/libcameraservice/api_pro/ProCamera2Client.cpp (renamed from services/camera/libcameraservice/ProCamera2Client.cpp)14
-rw-r--r--services/camera/libcameraservice/api_pro/ProCamera2Client.h (renamed from services/camera/libcameraservice/ProCamera2Client.h)12
-rw-r--r--services/camera/libcameraservice/camera2/FrameProcessor.h66
-rw-r--r--services/camera/libcameraservice/common/Camera2ClientBase.cpp (renamed from services/camera/libcameraservice/Camera2ClientBase.cpp)36
-rw-r--r--services/camera/libcameraservice/common/Camera2ClientBase.h (renamed from services/camera/libcameraservice/Camera2ClientBase.h)12
-rw-r--r--services/camera/libcameraservice/common/CameraDeviceBase.cpp (renamed from services/camera/libcameraservice/CameraDeviceBase.cpp)0
-rw-r--r--services/camera/libcameraservice/common/CameraDeviceBase.h (renamed from services/camera/libcameraservice/CameraDeviceBase.h)29
-rw-r--r--services/camera/libcameraservice/common/FrameProcessorBase.cpp (renamed from services/camera/libcameraservice/camera2/ProFrameProcessor.cpp)59
-rw-r--r--services/camera/libcameraservice/common/FrameProcessorBase.h (renamed from services/camera/libcameraservice/camera2/ProFrameProcessor.h)16
-rw-r--r--services/camera/libcameraservice/device1/CameraHardwareInterface.h (renamed from services/camera/libcameraservice/CameraHardwareInterface.h)0
-rw-r--r--services/camera/libcameraservice/device2/Camera2Device.cpp (renamed from services/camera/libcameraservice/Camera2Device.cpp)38
-rw-r--r--services/camera/libcameraservice/device2/Camera2Device.h (renamed from services/camera/libcameraservice/Camera2Device.h)12
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.cpp (renamed from services/camera/libcameraservice/Camera3Device.cpp)1034
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.h (renamed from services/camera/libcameraservice/Camera3Device.h)191
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp (renamed from services/camera/libcameraservice/camera3/Camera3IOStreamBase.cpp)80
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.h (renamed from services/camera/libcameraservice/camera3/Camera3IOStreamBase.h)3
-rw-r--r--services/camera/libcameraservice/device3/Camera3InputStream.cpp (renamed from services/camera/libcameraservice/camera3/Camera3InputStream.cpp)20
-rw-r--r--services/camera/libcameraservice/device3/Camera3InputStream.h (renamed from services/camera/libcameraservice/camera3/Camera3InputStream.h)9
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStream.cpp (renamed from services/camera/libcameraservice/camera3/Camera3OutputStream.cpp)40
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStream.h (renamed from services/camera/libcameraservice/camera3/Camera3OutputStream.h)3
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h (renamed from services/camera/libcameraservice/camera3/Camera3OutputStreamInterface.h)0
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.cpp (renamed from services/camera/libcameraservice/camera3/Camera3Stream.cpp)57
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.h (renamed from services/camera/libcameraservice/camera3/Camera3Stream.h)16
-rw-r--r--services/camera/libcameraservice/device3/Camera3StreamBufferListener.h (renamed from services/camera/libcameraservice/camera3/Camera3StreamBufferListener.h)0
-rw-r--r--services/camera/libcameraservice/device3/Camera3StreamInterface.h (renamed from services/camera/libcameraservice/camera3/Camera3StreamInterface.h)10
-rw-r--r--services/camera/libcameraservice/device3/Camera3ZslStream.cpp (renamed from services/camera/libcameraservice/camera3/Camera3ZslStream.cpp)8
-rw-r--r--services/camera/libcameraservice/device3/Camera3ZslStream.h (renamed from services/camera/libcameraservice/camera3/Camera3ZslStream.h)0
-rw-r--r--services/camera/libcameraservice/device3/StatusTracker.cpp219
-rw-r--r--services/camera/libcameraservice/device3/StatusTracker.h130
-rw-r--r--services/camera/libcameraservice/gui/RingBufferConsumer.cpp33
-rw-r--r--services/camera/libcameraservice/gui/RingBufferConsumer.h4
-rw-r--r--services/camera/libcameraservice/utils/CameraTraces.cpp94
-rw-r--r--services/camera/libcameraservice/utils/CameraTraces.h66
91 files changed, 6452 insertions, 1831 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 061a079..54377f1 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -27,9 +27,6 @@ LOCAL_SRC_FILES:= \
LOCAL_SRC_FILES += StateQueue.cpp
-# uncomment for debugging timing problems related to StateQueue::push()
-LOCAL_CFLAGS += -DSTATE_QUEUE_DUMP
-
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
@@ -56,24 +53,10 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_MODULE:= libaudioflinger
-LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp
-
-LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
-
-# uncomment to display CPU load adjusted for CPU frequency
-# LOCAL_CFLAGS += -DCPU_FREQUENCY_STATISTICS
+LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
-LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
-
-# uncomment to allow tee sink debugging to be enabled by property
-# LOCAL_CFLAGS += -DTEE_SINK
-
-# uncomment to enable the audio watchdog
-# LOCAL_SRC_FILES += AudioWatchdog.cpp
-# LOCAL_CFLAGS += -DAUDIO_WATCHDOG
-
# Define ANDROID_SMP appropriately. Used to get inline tracing fast-path.
ifeq ($(TARGET_CPU_SMP),true)
LOCAL_CFLAGS += -DANDROID_SMP=1
@@ -81,6 +64,8 @@ else
LOCAL_CFLAGS += -DANDROID_SMP=0
endif
+LOCAL_CFLAGS += -fvisibility=hidden
+
include $(BUILD_SHARED_LIBRARY)
#
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 87eb6aa..3132e54 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <dirent.h>
#include <math.h>
#include <signal.h>
@@ -36,10 +37,6 @@
#include <cutils/bitops.h>
#include <cutils/properties.h>
-#include <cutils/compiler.h>
-
-//#include <private/media/AudioTrackShared.h>
-//#include <private/media/AudioEffectShared.h>
#include <system/audio.h>
#include <hardware/audio.h>
@@ -58,12 +55,13 @@
#include <powermanager/PowerManager.h>
#include <common_time/cc_helper.h>
-//#include <common_time/local_clock.h>
#include <media/IMediaLogService.h>
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
+#include <media/AudioParameter.h>
+#include <private/android_filesystem_config.h>
// ----------------------------------------------------------------------------
@@ -100,6 +98,10 @@ size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
#endif
+// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
+// we define a minimum time during which a global effect is considered enabled.
+static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
+
// ----------------------------------------------------------------------------
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -141,7 +143,10 @@ AudioFlinger::AudioFlinger()
mMasterMute(false),
mNextUniqueId(1),
mMode(AUDIO_MODE_INVALID),
- mBtNrecIsOff(false)
+ mBtNrecIsOff(false),
+ mIsLowRamDevice(true),
+ mIsDeviceTypeKnown(false),
+ mGlobalEffectEnableTime(0)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -259,6 +264,12 @@ void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
}
}
+ result.append("Notification Clients:\n");
+ for (size_t i = 0; i < mNotificationClients.size(); ++i) {
+ snprintf(buffer, SIZE, " pid: %d\n", mNotificationClients.keyAt(i));
+ result.append(buffer);
+ }
+
result.append("Global session refs:\n");
result.append(" session pid count\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
@@ -436,6 +447,8 @@ sp<IAudioTrack> AudioFlinger::createTrack(
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
+ int clientUid,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -471,6 +484,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
pid_t pid = IPCThreadState::self()->getCallingPid();
+
client = registerPid_l(pid);
ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
@@ -498,7 +512,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
ALOGV("createTrack() lSessionId: %d", lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
+ channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
@@ -524,6 +538,9 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
}
if (lStatus == NO_ERROR) {
+ // s for server's pid, n for normal mixer name, f for fast index
+ name = String8::format("s:%d;n:%d;f:%d", getpid_cached, track->name() - AudioMixer::TRACK0,
+ track->fastIndex());
trackHandle = new TrackHandle(track);
} else {
// remove local strong reference to Client before deleting the Track so that the Client
@@ -981,11 +998,12 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- struct audio_config config = {
- sample_rate: sampleRate,
- channel_mask: channelMask,
- format: format,
- };
+ struct audio_config config;
+ memset(&config, 0, sizeof(config));
+ config.sample_rate = sampleRate;
+ config.channel_mask = channelMask;
+ config.format = format;
+
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
size_t size = dev->get_input_buffer_size(dev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
@@ -1201,13 +1219,17 @@ void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
// ----------------------------------------------------------------------------
+static bool deviceRequiresCaptureAudioOutputPermission(audio_devices_t inDevice) {
+ return audio_is_remote_submix_device(inDevice);
+}
+
sp<IAudioRecord> AudioFlinger::openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status)
@@ -1222,19 +1244,34 @@ sp<IAudioRecord> AudioFlinger::openRecord(
// check calling permissions
if (!recordingAllowed()) {
+ ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
+ if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("openRecord() invalid format %d", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
// add client to list
{ // scope for mLock
Mutex::Autolock _l(mLock);
thread = checkRecordThread_l(input);
if (thread == NULL) {
+ ALOGE("openRecord() checkRecordThread_l failed");
lStatus = BAD_VALUE;
goto Exit;
}
+ if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice())
+ && !captureAudioOutputAllowed()) {
+ ALOGE("openRecord() permission denied: capture not allowed");
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+
pid_t pid = IPCThreadState::self()->getCallingPid();
client = registerPid_l(pid);
@@ -1249,8 +1286,12 @@ sp<IAudioRecord> AudioFlinger::openRecord(
}
// create new record track.
// The record track uses one track in mHardwareMixerThread by convention.
+ // TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
- frameCount, lSessionId, flags, tid, &lStatus);
+ frameCount, lSessionId,
+ IPCThreadState::self()->getCallingUid(),
+ flags, tid, &lStatus);
+ LOG_ALWAYS_FATAL_IF((recordTrack != 0) != (lStatus == NO_ERROR));
}
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the RecordTrack so that the
@@ -1382,31 +1423,53 @@ size_t AudioFlinger::getPrimaryOutputFrameCount()
// ----------------------------------------------------------------------------
+status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)
+{
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid != AID_SYSTEM) {
+ return PERMISSION_DENIED;
+ }
+ Mutex::Autolock _l(mLock);
+ if (mIsDeviceTypeKnown) {
+ return INVALID_OPERATION;
+ }
+ mIsLowRamDevice = isLowRamDevice;
+ mIsDeviceTypeKnown = true;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
- status_t status;
PlaybackThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config config;
+ config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
+ config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
+ config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
+ if (offloadInfo) {
+ config.offload_info = *offloadInfo;
+ }
+
audio_stream_out_t *outStream = NULL;
AudioHwDevice *outHwDev;
- ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
+ ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x",
module,
(pDevices != NULL) ? *pDevices : 0,
config.sample_rate,
config.format,
config.channel_mask,
flags);
+ ALOGV("openOutput(), offloadInfo %p version 0x%04x",
+ offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version );
if (pDevices == NULL || *pDevices == 0) {
return 0;
@@ -1423,7 +1486,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status = hwDevHal->open_output_stream(hwDevHal,
+ status_t status = hwDevHal->open_output_stream(hwDevHal,
id,
*pDevices,
(audio_output_flags_t)flags,
@@ -1431,7 +1494,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
&outStream);
mHardwareStatus = AUDIO_HW_IDLE;
- ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, "
+ ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
"Channels %x, status %d",
outStream,
config.sample_rate,
@@ -1440,9 +1503,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
status);
if (status == NO_ERROR && outStream != NULL) {
- AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
+ AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);
- if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ thread = new OffloadThread(this, output, id, *pDevices);
+ ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
+ } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
(config.format != AUDIO_FORMAT_PCM_16_BIT) ||
(config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
@@ -1453,10 +1519,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = config.sample_rate;
- if (pFormat != NULL) *pFormat = config.format;
- if (pChannelMask != NULL) *pChannelMask = config.channel_mask;
- if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = config.sample_rate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = config.channel_mask;
+ }
+ if (pLatencyMs != NULL) {
+ *pLatencyMs = thread->latency();
+ }
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -1524,11 +1598,28 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
DuplicatingThread *dupThread =
(DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
dupThread->removeOutputTrack((MixerThread *)thread.get());
+
}
}
}
- audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
+
+
mPlaybackThreads.removeItem(output);
+ // save all effects to the default thread
+ if (mPlaybackThreads.size()) {
+ PlaybackThread *dstThread = checkPlaybackThread_l(mPlaybackThreads.keyAt(0));
+ if (dstThread != NULL) {
+ // audioflinger lock is held here so the acquisition order of thread locks does not
+ // matter
+ Mutex::Autolock _dl(dstThread->mLock);
+ Mutex::Autolock _sl(thread->mLock);
+ Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ moveEffectChain_l(effectChains[i]->sessionId(), thread.get(), dstThread, true);
+ }
+ }
+ }
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
}
thread->exit();
// The thread entity (active unit of execution) is no longer running here,
@@ -1583,11 +1674,11 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
{
status_t status;
RecordThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config config;
+ config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
+ config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
+ config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
+
uint32_t reqSamplingRate = config.sample_rate;
audio_format_t reqFormat = config.format;
audio_channel_mask_t reqChannels = config.channel_mask;
@@ -1683,7 +1774,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
// Start record thread
- // RecorThread require both input and output device indication to forward to audio
+ // RecordThread requires both input and output device indication to forward to audio
// pre processing modules
thread = new RecordThread(this,
input,
@@ -1698,9 +1789,15 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
);
mRecordThreads.add(id, thread);
ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
- if (pFormat != NULL) *pFormat = config.format;
- if (pChannelMask != NULL) *pChannelMask = reqChannels;
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = reqSamplingRate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = reqChannels;
+ }
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
@@ -1768,6 +1865,16 @@ void AudioFlinger::acquireAudioSessionId(int audioSession)
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("acquiring %d from %d", audioSession, caller);
+
+ // Ignore requests received from processes not known as notification client. The request
+ // is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be
+ // called from a different pid leaving a stale session reference. Also we don't know how
+ // to clear this reference if the client process dies.
+ if (mNotificationClients.indexOfKey(caller) < 0) {
+ ALOGV("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
+ return;
+ }
+
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
@@ -1800,7 +1907,9 @@ void AudioFlinger::releaseAudioSessionId(int audioSession)
return;
}
}
- ALOGW("session id %d not found for pid %d", audioSession, caller);
+ // If the caller is mediaserver it is likely that the session being released was acquired
+ // on behalf of a process not in notification clients and we ignore the warning.
+ ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
}
void AudioFlinger::purgeStaleEffects_l() {
@@ -2001,24 +2110,7 @@ sp<IEffect> AudioFlinger::createEffect(
goto Exit;
}
- if (io == 0) {
- if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
- // output must be specified by AudioPolicyManager when using session
- // AUDIO_SESSION_OUTPUT_STAGE
- lStatus = BAD_VALUE;
- goto Exit;
- } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- // if the output returned by getOutputForEffect() is removed before we lock the
- // mutex below, the call to checkPlaybackThread_l(io) below will detect it
- // and we will exit safely
- io = AudioSystem::getOutputForEffect(&desc);
- }
- }
-
{
- Mutex::Autolock _l(mLock);
-
-
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
@@ -2091,6 +2183,15 @@ sp<IEffect> AudioFlinger::createEffect(
// return effect descriptor
*pDesc = desc;
+ if (io == 0 && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ // if the output returned by getOutputForEffect() is removed before we lock the
+ // mutex below, the call to checkPlaybackThread_l(io) below will detect it
+ // and we will exit safely
+ io = AudioSystem::getOutputForEffect(&desc);
+ ALOGV("createEffect got output %d", io);
+ }
+
+ Mutex::Autolock _l(mLock);
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
@@ -2098,6 +2199,12 @@ sp<IEffect> AudioFlinger::createEffect(
// because of code checking output when entering the function.
// Note: io is never 0 when creating an effect on an input
if (io == 0) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ // output must be specified by AudioPolicyManager when using session
+ // AUDIO_SESSION_OUTPUT_STAGE
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
// look for the thread where the specified audio session is present
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
@@ -2171,9 +2278,7 @@ status_t AudioFlinger::moveEffects(int sessionId, audio_io_handle_t srcOutput,
Mutex::Autolock _dl(dstThread->mLock);
Mutex::Autolock _sl(srcThread->mLock);
- moveEffectChain_l(sessionId, srcThread, dstThread, false);
-
- return NO_ERROR;
+ return moveEffectChain_l(sessionId, srcThread, dstThread, false);
}
// moveEffectChain_l must be called with both srcThread and dstThread mLocks held
@@ -2200,13 +2305,18 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
- audio_io_handle_t dstOutput = dstThread->id();
sp<EffectChain> dstChain;
uint32_t strategy = 0; // prevent compiler warning
sp<EffectModule> effect = chain->getEffectFromId_l(0);
+ Vector< sp<EffectModule> > removed;
+ status_t status = NO_ERROR;
while (effect != 0) {
srcThread->removeEffect_l(effect);
- dstThread->addEffect_l(effect);
+ removed.add(effect);
+ status = dstThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ break;
+ }
// removeEffect_l() has stopped the effect if it was active so it must be restarted
if (effect->state() == EffectModule::ACTIVE ||
effect->state() == EffectModule::STOPPING) {
@@ -2218,23 +2328,71 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
dstChain = effect->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
- srcThread->addEffect_l(effect);
- return NO_INIT;
+ status = NO_INIT;
+ break;
}
strategy = dstChain->strategy();
}
if (reRegister) {
AudioSystem::unregisterEffect(effect->id());
AudioSystem::registerEffect(&effect->desc(),
- dstOutput,
+ dstThread->id(),
strategy,
sessionId,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
effect = chain->getEffectFromId_l(0);
}
- return NO_ERROR;
+ if (status != NO_ERROR) {
+ for (size_t i = 0; i < removed.size(); i++) {
+ srcThread->addEffect_l(removed[i]);
+ if (dstChain != 0 && reRegister) {
+ AudioSystem::unregisterEffect(removed[i]->id());
+ AudioSystem::registerEffect(&removed[i]->desc(),
+ srcThread->id(),
+ strategy,
+ sessionId,
+ removed[i]->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
+ }
+ }
+ }
+
+ return status;
+}
+
+bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l()
+{
+ if (mGlobalEffectEnableTime != 0 &&
+ ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
+ return true;
+ }
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<EffectChain> ec =
+ mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (ec != 0 && ec->isNonOffloadableEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::onNonOffloadableGlobalEffectEnable()
+{
+ Mutex::Autolock _l(mLock);
+
+ mGlobalEffectEnableTime = systemTime();
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (t->mType == ThreadBase::OFFLOAD) {
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ }
+
}
struct Entry {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index b0efef6..53e238e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -24,6 +24,8 @@
#include <common_time/cc_helper.h>
+#include <cutils/compiler.h>
+
#include <media/IAudioFlinger.h>
#include <media/IAudioFlingerClient.h>
#include <media/IAudioTrack.h>
@@ -54,6 +56,7 @@
#include <powermanager/IPowerManager.h>
#include <media/nbaio/NBLog.h>
+#include <private/media/AudioTrackShared.h>
namespace android {
@@ -89,7 +92,7 @@ class AudioFlinger :
{
friend class BinderService<AudioFlinger>; // for AudioFlinger()
public:
- static const char* getServiceName() { return "media.audio_flinger"; }
+ static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -105,6 +108,8 @@ public:
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
+ int clientUid,
status_t *status);
virtual sp<IAudioRecord> openRecord(
@@ -113,7 +118,7 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- IAudioFlinger::track_flags_t flags,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status);
@@ -157,7 +162,8 @@ public:
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags);
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo);
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2);
@@ -216,6 +222,8 @@ public:
virtual uint32_t getPrimaryOutputSamplingRate();
virtual size_t getPrimaryOutputFrameCount();
+ virtual status_t setLowRamDevice(bool isLowRamDevice);
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -278,7 +286,7 @@ private:
bool btNrecIsOff() const { return mBtNrecIsOff; }
- AudioFlinger();
+ AudioFlinger() ANDROID_API;
virtual ~AudioFlinger();
// call in any IAudioFlinger method that accesses mPrimaryHardwareDev
@@ -359,7 +367,9 @@ private:
class PlaybackThread;
class MixerThread;
class DirectOutputThread;
+ class OffloadThread;
class DuplicatingThread;
+ class AsyncCallbackThread;
class Track;
class RecordTrack;
class EffectModule;
@@ -401,8 +411,13 @@ private:
int64_t pts);
virtual status_t setMediaTimeTransform(const LinearTransform& xform,
int target);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual void signal(); // signal playback thread for a change in control block
+
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+
private:
const sp<PlaybackThread::Track> mTrack;
};
@@ -424,6 +439,7 @@ private:
void stop_nonvirtual();
};
+
PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
@@ -452,6 +468,9 @@ private:
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
+ bool isNonOffloadableGlobalEffectEnabled_l();
+ void onNonOffloadableGlobalEffectEnable();
+
class AudioHwDevice {
public:
enum Flags {
@@ -490,11 +509,12 @@ private:
struct AudioStreamOut {
AudioHwDevice* const audioHwDev;
audio_stream_out_t* const stream;
+ audio_output_flags_t flags;
audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
- AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
- audioHwDev(dev), stream(out) {}
+ AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out, audio_output_flags_t flags) :
+ audioHwDev(dev), stream(out), flags(flags) {}
};
struct AudioStreamIn {
@@ -588,12 +608,11 @@ private:
status_t closeOutput_nonvirtual(audio_io_handle_t output);
status_t closeInput_nonvirtual(audio_io_handle_t input);
-// do not use #ifdef here, since AudioFlinger.h is included by more than one module
-//#ifdef TEE_SINK
+#ifdef TEE_SINK
// all record threads serially share a common tee sink, which is re-created on format change
sp<NBAIO_Sink> mRecordTeeSink;
sp<NBAIO_Source> mRecordTeeSource;
-//#endif
+#endif
public:
@@ -618,6 +637,16 @@ public:
static const size_t kTeeSinkTrackFramesDefault = 0x1000;
#endif
+ // This method reads from a variable without mLock, but the variable is updated under mLock. So
+ // we might read a stale value, or a value that's inconsistent with respect to other variables.
+ // In this case, it's safe because the return value isn't used for making an important decision.
+ // The reason we don't want to take mLock is because it could block the caller for a long time.
+ bool isLowRamDevice() const { return mIsLowRamDevice; }
+
+private:
+ bool mIsLowRamDevice;
+ bool mIsDeviceTypeKnown;
+ nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 7d38f80..df4e029 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AudioMixer"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 94f22b1..646a317 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "AudioPolicyService"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#undef __STRICT_ANSI__
#define __STDINT_LIMITS
#define __STDC_LIMIT_MACROS
@@ -40,6 +41,7 @@
#include <system/audio_policy.h>
#include <hardware/audio_policy.h>
#include <audio_effects/audio_effects_conf.h>
+#include <media/AudioParameter.h>
namespace android {
@@ -49,7 +51,7 @@ 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
+static const nsecs_t kAudioCommandTimeout = 3000000000LL; // 3 seconds
namespace {
extern struct audio_policy_service_ops aps_ops;
@@ -68,10 +70,11 @@ AudioPolicyService::AudioPolicyService()
Mutex::Autolock _l(mLock);
// start tone playback thread
- mTonePlaybackThread = new AudioCommandThread(String8(""));
+ mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
// start audio commands thread
- mAudioCommandThread = new AudioCommandThread(String8("ApmCommand"));
-
+ mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
+ // start output activity command thread
+ mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
/* instantiate the audio policy manager */
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc)
@@ -222,15 +225,16 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
if (mpAudioPolicy == NULL) {
return 0;
}
ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channelMask,
- flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate,
+ format, channelMask, flags, offloadInfo);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -253,6 +257,15 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
return NO_INIT;
}
ALOGV("stopOutput()");
+ mOutputCommandThread->stopOutputCommand(output, stream, session);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ ALOGV("doStopOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
@@ -263,6 +276,12 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
return;
}
ALOGV("releaseOutput()");
+ mOutputCommandThread->releaseOutputCommand(output);
+}
+
+void AudioPolicyService::doReleaseOutput(audio_io_handle_t output)
+{
+ ALOGV("doReleaseOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
mpAudioPolicy->release_output(mpAudioPolicy, output);
}
@@ -277,9 +296,14 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return 0;
}
// already checked by client, but double-check in case the client wrapper is bypassed
- if (uint32_t(inputSource) >= AUDIO_SOURCE_CNT) {
+ if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) {
return 0;
}
+
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
+ 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,
@@ -289,7 +313,10 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return input;
}
// create audio pre processors according to input source
- ssize_t index = mInputSources.indexOfKey(inputSource);
+ audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
+ AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
+
+ ssize_t index = mInputSources.indexOfKey(aliasSource);
if (index < 0) {
return input;
}
@@ -638,8 +665,9 @@ status_t AudioPolicyService::onTransact(
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
-AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
- : Thread(false), mName(name)
+AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
+ const wp<AudioPolicyService>& service)
+ : Thread(false), mName(name), mService(service)
{
mpToneGenerator = NULL;
}
@@ -647,7 +675,7 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
AudioPolicyService::AudioCommandThread::~AudioCommandThread()
{
- if (mName != "" && !mAudioCommands.isEmpty()) {
+ if (!mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
mAudioCommands.clear();
@@ -656,11 +684,7 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread()
void AudioPolicyService::AudioCommandThread::onFirstRef()
{
- if (mName != "") {
- run(mName.string(), ANDROID_PRIORITY_AUDIO);
- } else {
- run("AudioCommand", ANDROID_PRIORITY_AUDIO);
- }
+ run(mName.string(), ANDROID_PRIORITY_AUDIO);
}
bool AudioPolicyService::AudioCommandThread::threadLoop()
@@ -735,6 +759,32 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}
delete data;
}break;
+ case STOP_OUTPUT: {
+ StopOutputData *data = (StopOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing stop output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doStopOutput(data->mIO, data->mStream, data->mSession);
+ mLock.lock();
+ delete data;
+ }break;
+ case RELEASE_OUTPUT: {
+ ReleaseOutputData *data = (ReleaseOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing release output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doReleaseOutput(data->mIO);
+ mLock.lock();
+ delete data;
+ }break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
@@ -746,7 +796,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}
}
// release delayed commands wake lock
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
ALOGV("AudioCommandThread() going to sleep");
@@ -890,17 +940,45 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
return status;
}
+void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = STOP_OUTPUT;
+ StopOutputData *data = new StopOutputData();
+ data->mIO = output;
+ data->mStream = stream;
+ data->mSession = session;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding stop output %d", output);
+ mWaitWorkCV.signal();
+}
+
+void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = RELEASE_OUTPUT;
+ ReleaseOutputData *data = new ReleaseOutputData();
+ data->mIO = output;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding release output %d", output);
+ mWaitWorkCV.signal();
+}
+
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
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
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
@@ -942,7 +1020,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
} else {
data2->mKeyValuePairs = param2.toString();
}
- time = command2->mTime;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case SET_VOLUME: {
@@ -953,7 +1034,10 @@ 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;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case START_TONE:
case STOP_TONE:
@@ -975,16 +1059,12 @@ 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) {
+ // wait for status only if delay is 0
+ if (delayMs == 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",
@@ -1055,6 +1135,21 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
}
+bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
+{
+ if (mpAudioPolicy == NULL) {
+ ALOGV("mpAudioPolicy == NULL");
+ return false;
+ }
+
+ if (mpAudioPolicy->is_offload_supported == NULL) {
+ ALOGV("HAL does not implement is_offload_supported");
+ return false;
+ }
+
+ return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
+}
+
// ----------------------------------------------------------------------------
// Audio pre-processing configuration
// ----------------------------------------------------------------------------
@@ -1395,7 +1490,8 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1403,7 +1499,7 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
return 0;
}
return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask,
- pLatencyMs, flags);
+ pLatencyMs, flags, offloadInfo);
}
static audio_io_handle_t aps_open_dup_output(void *service,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 35cf368..ae053a9 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -19,6 +19,7 @@
#include <cutils/misc.h>
#include <cutils/config_utils.h>
+#include <cutils/compiler.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
@@ -44,7 +45,7 @@ class AudioPolicyService :
public:
// for BinderService
- static const char *getServiceName() { return "media.audio_policy"; }
+ static const char *getServiceName() ANDROID_API { return "media.audio_policy"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -66,7 +67,8 @@ public:
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags =
- AUDIO_OUTPUT_FLAG_NONE);
+ AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
@@ -135,9 +137,15 @@ public:
virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream);
virtual status_t stopTone();
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+ virtual bool isOffloadSupported(const audio_offload_info_t &config);
+
+ status_t doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session = 0);
+ void doReleaseOutput(audio_io_handle_t output);
private:
- AudioPolicyService();
+ AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
status_t dumpInternals(int fd);
@@ -158,10 +166,12 @@ private:
STOP_TONE,
SET_VOLUME,
SET_PARAMETERS,
- SET_VOICE_VOLUME
+ SET_VOICE_VOLUME,
+ STOP_OUTPUT,
+ RELEASE_OUTPUT
};
- AudioCommandThread (String8 name);
+ AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
virtual ~AudioCommandThread();
status_t dump(int fd);
@@ -179,6 +189,11 @@ private:
status_t parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
+ void stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session);
+ void releaseOutputCommand(audio_io_handle_t output);
+
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
@@ -223,12 +238,25 @@ private:
float mVolume;
};
+ class StopOutputData {
+ public:
+ audio_io_handle_t mIO;
+ audio_stream_type_t mStream;
+ int mSession;
+ };
+
+ class ReleaseOutputData {
+ public:
+ audio_io_handle_t mIO;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector <AudioCommand *> mAudioCommands; // list of pending commands
ToneGenerator *mpToneGenerator; // the tone generator
AudioCommand mLastCommand; // last processed command (used by dump)
String8 mName; // string used by wake lock fo delayed commands
+ wp<AudioPolicyService> mService;
};
class EffectDesc {
@@ -313,6 +341,7 @@ private:
// device connection state or routing
sp<AudioCommandThread> mAudioCommandThread; // audio commands thread
sp<AudioCommandThread> mTonePlaybackThread; // tone playback thread
+ sp<AudioCommandThread> mOutputCommandThread; // process stop and release output
struct audio_policy_device *mpAudioPolicyDev;
struct audio_policy *mpAudioPolicy;
KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 2b8694f..33e64ce 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -19,13 +19,14 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/compiler.h>
#include <media/AudioBufferProvider.h>
namespace android {
// ----------------------------------------------------------------------------
-class AudioResampler {
+class ANDROID_API AudioResampler {
public:
// Determines quality of SRC.
// LOW_QUALITY: linear interpolator (1st order)
@@ -55,6 +56,14 @@ public:
// set the PTS of the next buffer output by the resampler
virtual void setPTS(int64_t pts);
+ // Resample int16_t samples from provider and accumulate into 'out'.
+ // A mono provider delivers a sequence of samples.
+ // A stereo provider delivers a sequence of interleaved pairs of samples.
+ // Multi-channel providers are not supported.
+ // In either case, 'out' holds interleaved pairs of fixed-point signed Q19.12.
+ // That is, for a mono provider, there is an implicit up-channeling.
+ // Since this method accumulates, the caller is responsible for clearing 'out' initially.
+ // FIXME assumes provider is always successful; it should return the actual frame count.
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 8f328ee..93d185e 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -17,9 +17,12 @@
#define LOG_TAG "AudioWatchdog"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <utils/Log.h>
#include "AudioWatchdog.h"
+#ifdef AUDIO_WATCHDOG
+
namespace android {
void AudioWatchdogDump::dump(int fd)
@@ -132,3 +135,5 @@ void AudioWatchdog::setDump(AudioWatchdogDump *dump)
}
} // namespace android
+
+#endif // AUDIO_WATCHDOG
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
new file mode 100644
index 0000000..bc2038a
--- /dev/null
+++ b/services/audioflinger/Configuration.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Put build-time configuration options here rather than Android.mk,
+// so that the instantiate for AudioFlinger service will pick up the same options.
+
+#ifndef ANDROID_AUDIOFLINGER_CONFIGURATION_H
+#define ANDROID_AUDIOFLINGER_CONFIGURATION_H
+
+// uncomment to enable detailed battery usage reporting (not debugged)
+//#define ADD_BATTERY_DATA
+
+// uncomment to enable the audio watchdog
+//#define AUDIO_WATCHDOG
+
+// uncomment to display CPU load adjusted for CPU frequency
+//#define CPU_FREQUENCY_STATISTICS
+
+// uncomment to enable fast mixer to take performance samples for later statistical analysis
+#define FAST_MIXER_STATISTICS
+
+// uncomment to allow fast tracks at non-native sample rate
+//#define FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
+
+// uncomment for debugging timing problems related to StateQueue::push()
+//#define STATE_QUEUE_DUMP
+
+// uncomment to allow tee sink debugging to be enabled by property
+//#define TEE_SINK
+
+// uncomment to log CPU statistics every n wall clock seconds
+//#define DEBUG_CPU_USAGE 10
+
+#endif // ANDROID_AUDIOFLINGER_CONFIGURATION_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 942ea35..a8a5169 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <utils/Log.h>
#include <audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
@@ -94,16 +95,7 @@ AudioFlinger::EffectModule::~EffectModule()
{
ALOGV("Destructor %p", this);
if (mEffectInterface != NULL) {
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->remove_audio_effect(stream, mEffectInterface);
- }
- }
- }
+ remove_effect_from_hal_l();
// release effect engine
EffectRelease(mEffectInterface);
}
@@ -487,7 +479,7 @@ status_t AudioFlinger::EffectModule::stop_l()
if (mStatus != NO_ERROR) {
return mStatus;
}
- status_t cmdStatus;
+ status_t cmdStatus = NO_ERROR;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_DISABLE,
@@ -495,12 +487,19 @@ status_t AudioFlinger::EffectModule::stop_l()
NULL,
&size,
&cmdStatus);
- if (status == 0) {
+ if (status == NO_ERROR) {
status = cmdStatus;
}
- if (status == 0 &&
- ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
+ if (status == NO_ERROR) {
+ status = remove_effect_from_hal_l();
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
+{
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
audio_stream_t *stream = thread->stream();
@@ -509,7 +508,7 @@ status_t AudioFlinger::EffectModule::stop_l()
}
}
}
- return status;
+ return NO_ERROR;
}
status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
@@ -765,6 +764,46 @@ bool AudioFlinger::EffectModule::purgeHandles()
return enabled;
}
+status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0) {
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ effect_offload_param_t cmd;
+
+ cmd.isOffload = offloaded;
+ cmd.ioHandle = io;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_OFFLOAD,
+ sizeof(effect_offload_param_t),
+ &cmd,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ mOffloaded = (status == NO_ERROR) ? offloaded : false;
+ } else {
+ if (offloaded) {
+ status = INVALID_OPERATION;
+ }
+ mOffloaded = false;
+ }
+ ALOGV("setOffloaded() offloaded %d io %d status %d", offloaded, io, status);
+ return status;
+}
+
+bool AudioFlinger::EffectModule::isOffloaded() const
+{
+ Mutex::Autolock _l(mLock);
+ return mOffloaded;
+}
+
void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -932,6 +971,23 @@ status_t AudioFlinger::EffectHandle::enable()
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
}
mEnabled = false;
+ } else {
+ if (thread != 0) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+ if (!mEffect->isOffloadable()) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
+ }
+ }
}
return status;
}
@@ -960,6 +1016,11 @@ status_t AudioFlinger::EffectHandle::disable()
sp<ThreadBase> thread = mEffect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
}
return status;
@@ -1217,9 +1278,7 @@ void AudioFlinger::EffectChain::clearInputBuffer()
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::clearInputBuffer_l(sp<ThreadBase> thread)
{
- size_t numSamples = thread->frameCount() * thread->channelCount();
- memset(mInBuffer, 0, numSamples * sizeof(int16_t));
-
+ memset(mInBuffer, 0, thread->frameCount() * thread->frameSize());
}
// Must be called with EffectChain::mLock locked
@@ -1232,9 +1291,10 @@ void AudioFlinger::EffectChain::process_l()
}
bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
(mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
- // always process effects unless no more tracks are on the session and the effect tail
- // has been rendered
- bool doProcess = true;
+ // never process effects when:
+ // - on an OFFLOAD thread
+ // - no more tracks are on the session and the effect tail has been rendered
+ bool doProcess = (thread->type() != ThreadBase::OFFLOAD);
if (!isGlobalSession) {
bool tracksOnSession = (trackCnt() != 0);
@@ -1720,4 +1780,16 @@ void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModul
}
}
+bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) {
+ return true;
+ }
+ }
+ return false;
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 91303ee..b717857 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -25,6 +25,10 @@
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
+// startOutput()...) should never be called with AudioFlinger or Threadbase mutex locked
+// to avoid cross deadlock with other clients calling AudioPolicyService methods that in turn
+// call AudioFlinger thus locking the same mutexes in the reverse order.
// The EffectModule class is a wrapper object controlling the effect engine implementation
// in the effect library. It prevents concurrent calls to process() and command() functions
@@ -111,6 +115,10 @@ public:
bool purgeHandles();
void lock() { mLock.lock(); }
void unlock() { mLock.unlock(); }
+ bool isOffloadable() const
+ { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
+ status_t setOffloaded(bool offloaded, audio_io_handle_t io);
+ bool isOffloaded() const;
void dump(int fd, const Vector<String16>& args);
@@ -126,6 +134,7 @@ protected:
status_t start_l();
status_t stop_l();
+ status_t remove_effect_from_hal_l();
mutable Mutex mLock; // mutex for process, commands and handles list protection
wp<ThreadBase> mThread; // parent thread
@@ -143,6 +152,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
+ bool mOffloaded; // effect is currently offloaded to the audio DSP
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -302,6 +312,10 @@ public:
void clearInputBuffer();
+ // At least one non offloadable effect in the chain is enabled
+ bool isNonOffloadableEnabled();
+
+
void dump(int fd, const Vector<String16>& args);
protected:
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 21df1d7..f27ea17 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -25,6 +25,7 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include "Configuration.h"
#include <sys/atomics.h>
#include <time.h>
#include <utils/Log.h>
@@ -44,6 +45,8 @@
#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup
#define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup
+#define FCC_2 2 // fixed channel count assumption
+
namespace android {
// Fast mixer thread
@@ -82,7 +85,7 @@ bool FastMixer::threadLoop()
struct timespec oldLoad = {0, 0}; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
bool oldLoadValid = false; // whether oldLoad is valid
uint32_t bounds = 0;
- bool full = false; // whether we have collected at least kSamplingN samples
+ bool full = false; // whether we have collected at least mSamplingN samples
#ifdef CPU_FREQUENCY_STATISTICS
ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz
#endif
@@ -93,6 +96,12 @@ bool FastMixer::threadLoop()
uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup
NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink
NBLog::Writer dummyLogWriter, *logWriter = &dummyLogWriter;
+ uint32_t totalNativeFramesWritten = 0; // copied to dumpState->mFramesWritten
+
+ // next 2 fields are valid only when timestampStatus == NO_ERROR
+ AudioTimestamp timestamp;
+ uint32_t nativeFramesWrittenButNotPresented = 0; // the = 0 is to silence the compiler
+ status_t timestampStatus = INVALID_OPERATION;
for (;;) {
@@ -142,7 +151,9 @@ bool FastMixer::threadLoop()
preIdle = *current;
current = &preIdle;
oldTsValid = false;
+#ifdef FAST_MIXER_STATISTICS
oldLoadValid = false;
+#endif
ignoreNextOverrun = true;
}
previous = current;
@@ -182,9 +193,12 @@ bool FastMixer::threadLoop()
warmupCycles = 0;
sleepNs = -1;
coldGen = current->mColdGen;
+#ifdef FAST_MIXER_STATISTICS
bounds = 0;
full = false;
+#endif
oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs);
+ timestampStatus = INVALID_OPERATION;
} else {
sleepNs = FAST_HOT_IDLE_NS;
}
@@ -220,7 +234,7 @@ bool FastMixer::threadLoop()
} else {
format = outputSink->format();
sampleRate = Format_sampleRate(format);
- ALOG_ASSERT(Format_channelCount(format) == 2);
+ ALOG_ASSERT(Format_channelCount(format) == FCC_2);
}
dumpState->mSampleRate = sampleRate;
}
@@ -236,7 +250,7 @@ bool FastMixer::threadLoop()
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
- mixBuffer = new short[frameCount * 2];
+ mixBuffer = new short[frameCount * FCC_2];
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
@@ -375,6 +389,31 @@ bool FastMixer::threadLoop()
i = __builtin_ctz(currentTrackMask);
currentTrackMask &= ~(1 << i);
const FastTrack* fastTrack = &current->mFastTracks[i];
+
+ // Refresh the per-track timestamp
+ if (timestampStatus == NO_ERROR) {
+ uint32_t trackFramesWrittenButNotPresented;
+ uint32_t trackSampleRate = fastTrack->mSampleRate;
+ // There is currently no sample rate conversion for fast tracks currently
+ if (trackSampleRate != 0 && trackSampleRate != sampleRate) {
+ trackFramesWrittenButNotPresented =
+ ((int64_t) nativeFramesWrittenButNotPresented * trackSampleRate) /
+ sampleRate;
+ } else {
+ trackFramesWrittenButNotPresented = nativeFramesWrittenButNotPresented;
+ }
+ uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
+ // Can't provide an AudioTimestamp before first frame presented,
+ // or during the brief 32-bit wraparound window
+ if (trackFramesWritten >= trackFramesWrittenButNotPresented) {
+ AudioTimestamp perTrackTimestamp;
+ perTrackTimestamp.mPosition =
+ trackFramesWritten - trackFramesWrittenButNotPresented;
+ perTrackTimestamp.mTime = timestamp.mTime;
+ fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
+ }
+ }
+
int name = fastTrackNames[i];
ALOG_ASSERT(name >= 0);
if (fastTrack->mVolumeProvider != NULL) {
@@ -433,7 +472,7 @@ bool FastMixer::threadLoop()
//bool didFullWrite = false; // dumpsys could display a count of partial writes
if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
if (mixBufferState == UNDEFINED) {
- memset(mixBuffer, 0, frameCount * 2 * sizeof(short));
+ memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short));
mixBufferState = ZEROED;
}
if (teeSink != NULL) {
@@ -448,7 +487,8 @@ bool FastMixer::threadLoop()
dumpState->mWriteSequence++;
if (framesWritten >= 0) {
ALOG_ASSERT((size_t) framesWritten <= frameCount);
- dumpState->mFramesWritten += framesWritten;
+ totalNativeFramesWritten += framesWritten;
+ dumpState->mFramesWritten = totalNativeFramesWritten;
//if ((size_t) framesWritten == frameCount) {
// didFullWrite = true;
//}
@@ -457,6 +497,18 @@ bool FastMixer::threadLoop()
}
attemptedWrite = true;
// FIXME count # of writes blocked excessively, CPU usage, etc. for dump
+
+ timestampStatus = outputSink->getTimestamp(timestamp);
+ if (timestampStatus == NO_ERROR) {
+ uint32_t totalNativeFramesPresented = timestamp.mPosition;
+ if (totalNativeFramesPresented <= totalNativeFramesWritten) {
+ nativeFramesWrittenButNotPresented =
+ totalNativeFramesWritten - totalNativeFramesPresented;
+ } else {
+ // HAL reported that more frames were presented than were written
+ timestampStatus = INVALID_OPERATION;
+ }
+ }
}
// To be exactly periodic, compute the next sleep time based on current time.
@@ -498,91 +550,91 @@ bool FastMixer::threadLoop()
}
}
sleepNs = -1;
- if (isWarm) {
- if (sec > 0 || nsec > underrunNs) {
- ATRACE_NAME("underrun");
- // FIXME only log occasionally
- ALOGV("underrun: time since last cycle %d.%03ld sec",
- (int) sec, nsec / 1000000L);
- dumpState->mUnderruns++;
- ignoreNextOverrun = true;
- } else if (nsec < overrunNs) {
- if (ignoreNextOverrun) {
- ignoreNextOverrun = false;
- } else {
+ if (isWarm) {
+ if (sec > 0 || nsec > underrunNs) {
+ ATRACE_NAME("underrun");
// FIXME only log occasionally
- ALOGV("overrun: time since last cycle %d.%03ld sec",
+ ALOGV("underrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
- dumpState->mOverruns++;
+ dumpState->mUnderruns++;
+ ignoreNextOverrun = true;
+ } else if (nsec < overrunNs) {
+ if (ignoreNextOverrun) {
+ ignoreNextOverrun = false;
+ } else {
+ // FIXME only log occasionally
+ ALOGV("overrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mOverruns++;
+ }
+ // This forces a minimum cycle time. It:
+ // - compensates for an audio HAL with jitter due to sample rate conversion
+ // - works with a variable buffer depth audio HAL that never pulls at a
+ // rate < than overrunNs per buffer.
+ // - recovers from overrun immediately after underrun
+ // It doesn't work with a non-blocking audio HAL.
+ sleepNs = forceNs - nsec;
+ } else {
+ ignoreNextOverrun = false;
}
- // This forces a minimum cycle time. It:
- // - compensates for an audio HAL with jitter due to sample rate conversion
- // - works with a variable buffer depth audio HAL that never pulls at a rate
- // < than overrunNs per buffer.
- // - recovers from overrun immediately after underrun
- // It doesn't work with a non-blocking audio HAL.
- sleepNs = forceNs - nsec;
- } else {
- ignoreNextOverrun = false;
}
- }
#ifdef FAST_MIXER_STATISTICS
- if (isWarm) {
- // advance the FIFO queue bounds
- size_t i = bounds & (FastMixerDumpState::kSamplingN - 1);
- bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
- if (full) {
- bounds += 0x10000;
- } else if (!(bounds & (FastMixerDumpState::kSamplingN - 1))) {
- full = true;
- }
- // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
- uint32_t monotonicNs = nsec;
- if (sec > 0 && sec < 4) {
- monotonicNs += sec * 1000000000;
- }
- // compute the raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
- uint32_t loadNs = 0;
- struct timespec newLoad;
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
- if (rc == 0) {
- if (oldLoadValid) {
- sec = newLoad.tv_sec - oldLoad.tv_sec;
- nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
- if (nsec < 0) {
- --sec;
- nsec += 1000000000;
- }
- loadNs = nsec;
- if (sec > 0 && sec < 4) {
- loadNs += sec * 1000000000;
+ if (isWarm) {
+ // advance the FIFO queue bounds
+ size_t i = bounds & (dumpState->mSamplingN - 1);
+ bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
+ if (full) {
+ bounds += 0x10000;
+ } else if (!(bounds & (dumpState->mSamplingN - 1))) {
+ full = true;
+ }
+ // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
+ uint32_t monotonicNs = nsec;
+ if (sec > 0 && sec < 4) {
+ monotonicNs += sec * 1000000000;
+ }
+ // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
+ uint32_t loadNs = 0;
+ struct timespec newLoad;
+ rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
+ if (rc == 0) {
+ if (oldLoadValid) {
+ sec = newLoad.tv_sec - oldLoad.tv_sec;
+ nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
+ if (nsec < 0) {
+ --sec;
+ nsec += 1000000000;
+ }
+ loadNs = nsec;
+ if (sec > 0 && sec < 4) {
+ loadNs += sec * 1000000000;
+ }
+ } else {
+ // first time through the loop
+ oldLoadValid = true;
}
- } else {
- // first time through the loop
- oldLoadValid = true;
+ oldLoad = newLoad;
}
- oldLoad = newLoad;
- }
#ifdef CPU_FREQUENCY_STATISTICS
- // get the absolute value of CPU clock frequency in kHz
- int cpuNum = sched_getcpu();
- uint32_t kHz = tcu.getCpukHz(cpuNum);
- kHz = (kHz << 4) | (cpuNum & 0xF);
+ // get the absolute value of CPU clock frequency in kHz
+ int cpuNum = sched_getcpu();
+ uint32_t kHz = tcu.getCpukHz(cpuNum);
+ kHz = (kHz << 4) | (cpuNum & 0xF);
#endif
- // save values in FIFO queues for dumpsys
- // these stores #1, #2, #3 are not atomic with respect to each other,
- // or with respect to store #4 below
- dumpState->mMonotonicNs[i] = monotonicNs;
- dumpState->mLoadNs[i] = loadNs;
+ // save values in FIFO queues for dumpsys
+ // these stores #1, #2, #3 are not atomic with respect to each other,
+ // or with respect to store #4 below
+ dumpState->mMonotonicNs[i] = monotonicNs;
+ dumpState->mLoadNs[i] = loadNs;
#ifdef CPU_FREQUENCY_STATISTICS
- dumpState->mCpukHz[i] = kHz;
+ dumpState->mCpukHz[i] = kHz;
#endif
- // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
- // the newest open and oldest closed halves are atomic with respect to each other
- dumpState->mBounds = bounds;
- ATRACE_INT("cycle_ms", monotonicNs / 1000000);
- ATRACE_INT("load_us", loadNs / 1000);
- }
+ // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
+ // the newest open & oldest closed halves are atomic with respect to each other
+ dumpState->mBounds = bounds;
+ ATRACE_INT("cycle_ms", monotonicNs / 1000000);
+ ATRACE_INT("load_us", loadNs / 1000);
+ }
#endif
} else {
// first time through the loop
@@ -603,25 +655,43 @@ bool FastMixer::threadLoop()
// never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
}
-FastMixerDumpState::FastMixerDumpState() :
+FastMixerDumpState::FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN
+#endif
+ ) :
mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0),
mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0),
mTrackMask(0)
#ifdef FAST_MIXER_STATISTICS
- , mBounds(0)
+ , mSamplingN(0), mBounds(0)
#endif
{
mMeasuredWarmupTs.tv_sec = 0;
mMeasuredWarmupTs.tv_nsec = 0;
+#ifdef FAST_MIXER_STATISTICS
+ increaseSamplingN(samplingN);
+#endif
+}
+
+#ifdef FAST_MIXER_STATISTICS
+void FastMixerDumpState::increaseSamplingN(uint32_t samplingN)
+{
+ if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) {
+ return;
+ }
+ uint32_t additional = samplingN - mSamplingN;
// sample arrays aren't accessed atomically with respect to the bounds,
// so clearing reduces chance for dumpsys to read random uninitialized samples
- memset(&mMonotonicNs, 0, sizeof(mMonotonicNs));
- memset(&mLoadNs, 0, sizeof(mLoadNs));
+ memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional);
+ memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional);
#ifdef CPU_FREQUENCY_STATISTICS
- memset(&mCpukHz, 0, sizeof(mCpukHz));
+ memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional);
#endif
+ mSamplingN = samplingN;
}
+#endif
FastMixerDumpState::~FastMixerDumpState()
{
@@ -641,7 +711,7 @@ static int compare_uint32_t(const void *pa, const void *pb)
}
}
-void FastMixerDumpState::dump(int fd)
+void FastMixerDumpState::dump(int fd) const
{
if (mCommand == FastMixerState::INITIAL) {
fdprintf(fd, "FastMixer not initialized\n");
@@ -692,9 +762,9 @@ void FastMixerDumpState::dump(int fd)
uint32_t newestOpen = bounds & 0xFFFF;
uint32_t oldestClosed = bounds >> 16;
uint32_t n = (newestOpen - oldestClosed) & 0xFFFF;
- if (n > kSamplingN) {
+ if (n > mSamplingN) {
ALOGE("too many samples %u", n);
- n = kSamplingN;
+ n = mSamplingN;
}
// statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency,
// and adjusted CPU load in MHz normalized for CPU clock frequency
@@ -710,7 +780,7 @@ void FastMixerDumpState::dump(int fd)
uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL;
// loop over all the samples
for (uint32_t j = 0; j < n; ++j) {
- size_t i = oldestClosed++ & (kSamplingN - 1);
+ size_t i = oldestClosed++ & (mSamplingN - 1);
uint32_t wallNs = mMonotonicNs[i];
if (tail != NULL) {
tail[j] = wallNs;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 2ab1d04..6158925 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -85,10 +85,14 @@ struct FastTrackDump {
// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks).
// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer.
struct FastMixerDumpState {
- FastMixerDumpState();
+ FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN = kSamplingNforLowRamDevice
+#endif
+ );
/*virtual*/ ~FastMixerDumpState();
- void dump(int fd); // should only be called on a stable copy, not the original
+ void dump(int fd) const; // should only be called on a stable copy, not the original
FastMixerState::Command mCommand; // current command
uint32_t mWriteSequence; // incremented before and after each write()
@@ -106,8 +110,15 @@ struct FastMixerDumpState {
#ifdef FAST_MIXER_STATISTICS
// Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency.
- // kSamplingN is the size of the sampling frame, and must be a power of 2 <= 0x8000.
+ // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000.
+ // The sample arrays are virtually allocated based on this compile-time constant,
+ // but are only initialized and used based on the runtime parameter mSamplingN.
static const uint32_t kSamplingN = 0x8000;
+ // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN.
+ // This value was chosen such that each array uses 1 small page (4 Kbytes).
+ static const uint32_t kSamplingNforLowRamDevice = 0x400;
+ // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN.
+ uint32_t mSamplingN;
// The bounds define the interval of valid samples, and are represented as follows:
// newest open (excluded) endpoint = lower 16 bits of bounds, modulo N
// oldest closed (included) endpoint = upper 16 bits of bounds, modulo N
@@ -119,6 +130,8 @@ struct FastMixerDumpState {
#ifdef CPU_FREQUENCY_STATISTICS
uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU#
#endif
+ // Increase sampling window after construction, must be a power of 2 <= kSamplingN
+ void increaseSamplingN(uint32_t samplingN);
#endif
};
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index c45c81b..737de97 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
namespace android {
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a749d7a..43b77f3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -31,6 +31,7 @@ public:
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags);
virtual ~Track();
@@ -46,15 +47,21 @@ public:
void destroy();
int name() const { return mName; }
+ virtual uint32_t sampleRate() const;
+
audio_stream_type_t streamType() const {
return mStreamType;
}
+ bool isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; }
+ status_t setParameters(const String8& keyValuePairs);
status_t attachAuxEffect(int EffectId);
void setAuxBuffer(int EffectId, int32_t *buffer);
int32_t *auxBuffer() const { return mAuxBuffer; }
void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
int16_t *mainBuffer() const { return mMainBuffer; }
int auxEffectId() const { return mAuxEffectId; }
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ void signal();
// implement FastMixerState::VolumeProvider interface
virtual uint32_t getVolumeLR();
@@ -66,6 +73,7 @@ protected:
friend class PlaybackThread;
friend class MixerThread;
friend class DirectOutputThread;
+ friend class OffloadThread;
Track(const Track&);
Track& operator = (const Track&);
@@ -75,7 +83,9 @@ protected:
int64_t pts = kInvalidPTS);
// releaseBuffer() not overridden
+ // ExtendedAudioBufferProvider interface
virtual size_t framesReady() const;
+ virtual size_t framesReleased() const;
bool isPausing() const { return mState == PAUSING; }
bool isPaused() const { return mState == PAUSED; }
@@ -101,6 +111,7 @@ public:
bool isInvalid() const { return mIsInvalid; }
virtual bool isTimedTrack() const { return false; }
bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+ int fastIndex() const { return mFastIndex; }
protected:
@@ -108,7 +119,10 @@ protected:
enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
mutable uint8_t mFillingUpStatus;
int8_t mRetryCount;
- const sp<IMemory> mSharedBuffer;
+
+ // see comment at AudioFlinger::PlaybackThread::Track::~Track for why this can't be const
+ sp<IMemory> mSharedBuffer;
+
bool mResetDone;
const audio_stream_type_t mStreamType;
int mName; // track name on the normal mixer,
@@ -134,11 +148,12 @@ private:
// but the slot is only used if track is active
FastTrackUnderruns mObservedUnderruns; // Most recently observed value of
// mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
- uint32_t mUnderrunCount; // Counter of total number of underruns, never reset
volatile float mCachedVolume; // combined master volume and stream type volume;
// 'volatile' means accessed without lock or
// barrier, but is read/written atomically
bool mIsInvalid; // non-resettable latch, set by invalidate()
+ AudioTrackServerProxy* mAudioTrackServerProxy;
+ bool mResumeToStopping; // track was paused in stopping state.
}; // end of Track
class TimedTrack : public Track {
@@ -151,7 +166,8 @@ class TimedTrack : public Track {
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~TimedTrack();
class TimedBuffer {
@@ -194,7 +210,8 @@ class TimedTrack : public Track {
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
void timedYieldSamples_l(AudioBufferProvider::Buffer* buffer);
void timedYieldSilence_l(uint32_t numFrames,
@@ -241,7 +258,8 @@ public:
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount);
+ size_t frameCount,
+ int uid);
virtual ~OutputTrack();
virtual status_t start(AudioSystem::sync_event_t event =
@@ -255,10 +273,6 @@ public:
private:
- enum {
- NO_MORE_BUFFERS = 0x80000001, // same in AudioTrack.h, ok to be different value
- };
-
status_t obtainBuffer(AudioBufferProvider::Buffer* buffer,
uint32_t waitTimeMs);
void clearBufferQueue();
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 6c0d1d3..57de568 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -28,7 +28,8 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~RecordTrack();
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
@@ -36,6 +37,7 @@ public:
void destroy();
+ void invalidate();
// clear the buffer overflow flag
void clearOverflow() { mOverflow = false; }
// set the buffer overflow flag and return previous value
@@ -57,4 +59,5 @@ private:
// releaseBuffer() not overridden
bool mOverflow; // overflow on most recent attempt to fill client buffer
+ AudioRecordServerProxy* mAudioRecordServerProxy;
};
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index d15bd04..152455d 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -34,6 +34,22 @@ bool recordingAllowed() {
return ok;
}
+bool captureAudioOutputAllowed() {
+ if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+ static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
+ // don't use PermissionCache; this is not a system permission
+ bool ok = checkCallingPermission(sCaptureAudioOutput);
+ if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
+ return ok;
+}
+
+bool captureHotwordAllowed() {
+ static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
+ bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+ if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
+ return ok;
+}
+
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 80cecba..531bc56 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -21,6 +21,8 @@ namespace android {
extern pid_t getpid_cached;
bool recordingAllowed();
+bool captureAudioOutputAllowed();
+bool captureHotwordAllowed();
bool settingsAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 3e891a5..c2d3bbd 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "StateQueue"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <time.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index e33b3c6..9cde642 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -31,8 +31,14 @@
// and this may result in an audible artifact
// needs read-only access to a recent stable state,
// but not necessarily the most current one
+// only allocate and free memory when configuration changes
+// avoid conventional logging, as this is a form of I/O and could block
+// defer computation to other threads when feasible; for example
+// cycle times are collected by fast mixer thread but the floating-point
+// statistical calculations on these cycle times are computed by normal mixer
+// these requirements also apply to callouts such as AudioBufferProvider and VolumeProvider
// Normal mixer thread:
-// periodic with typical period ~40 ms
+// periodic with typical period ~20 ms
// SCHED_OTHER scheduling policy and nice priority == urgent audio
// ok to block, but prefer to avoid as much as possible
// needs read/write access to state
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 077582f..0d5cd0c 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
#include "StateQueue.h"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 0b88c0e..2f71db7 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -20,11 +20,12 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include "Configuration.h"
#include <math.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cutils/properties.h>
-#include <cutils/compiler.h>
+#include <media/AudioParameter.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -53,14 +54,11 @@
#include "ServiceUtilities.h"
#include "SchedulingPolicyService.h"
-#undef ADD_BATTERY_DATA
-
#ifdef ADD_BATTERY_DATA
#include <media/IMediaPlayerService.h>
#include <media/IMediaDeathNotifier.h>
#endif
-// #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds
#ifdef DEBUG_CPU_USAGE
#include <cpustats/CentralTendencyStatistics.h>
#include <cpustats/ThreadCpuUsage.h>
@@ -111,6 +109,9 @@ static const uint32_t kMinNormalMixBufferSizeMs = 20;
// maximum normal mix buffer size
static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+// Offloaded output thread standby delay: allows track transition without going to standby
+static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
+
// Whether to use fast mixer
static const enum {
FastMixer_Never, // never initialize or use: for debugging only
@@ -134,10 +135,10 @@ 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.
+// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
+// So for now we just assume that client is double-buffered for fast tracks.
+// FIXME It would be better for client to tell AudioFlinger the value of N,
+// so AudioFlinger could allocate the right amount of memory.
// See the client's minBufCount and mNotificationFramesAct calculations for details.
static const int kFastTrackMultiplier = 2;
@@ -267,11 +268,11 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio
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),
+ mAudioFlinger(audioFlinger),
+ // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are
+ // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
mParamStatus(NO_ERROR),
+ //FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
@@ -281,6 +282,12 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio
AudioFlinger::ThreadBase::~ThreadBase()
{
+ // mConfigEvents should be empty, but just in case it isn't, free the memory it owns
+ for (size_t i = 0; i < mConfigEvents.size(); i++) {
+ delete mConfigEvents[i];
+ }
+ mConfigEvents.clear();
+
mParamCond.broadcast();
// do not lock the mutex in destructor
releaseWakeLock_l();
@@ -420,9 +427,7 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "HAL frame count: %d\n", mFrameCount);
result.append(buffer);
- snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
+ snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount);
result.append(buffer);
snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
result.append(buffer);
@@ -472,30 +477,49 @@ void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>&
}
}
-void AudioFlinger::ThreadBase::acquireWakeLock()
+void AudioFlinger::ThreadBase::acquireWakeLock(int uid)
{
Mutex::Autolock _l(mLock);
- acquireWakeLock_l();
+ acquireWakeLock_l(uid);
}
-void AudioFlinger::ThreadBase::acquireWakeLock_l()
+String16 AudioFlinger::ThreadBase::getWakeLockTag()
{
- if (mPowerManager == 0) {
- // use checkService() to avoid blocking if power service is not up yet
- sp<IBinder> binder =
- defaultServiceManager()->checkService(String16("power"));
- if (binder == 0) {
- ALOGW("Thread %s cannot connect to the power manager service", mName);
- } else {
- mPowerManager = interface_cast<IPowerManager>(binder);
- binder->linkToDeath(mDeathRecipient);
- }
+ switch (mType) {
+ case MIXER:
+ return String16("AudioMix");
+ case DIRECT:
+ return String16("AudioDirectOut");
+ case DUPLICATING:
+ return String16("AudioDup");
+ case RECORD:
+ return String16("AudioIn");
+ case OFFLOAD:
+ return String16("AudioOffload");
+ default:
+ ALOG_ASSERT(false);
+ return String16("AudioUnknown");
}
+}
+
+void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
+{
+ getPowerManager_l();
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- String16(mName));
+ status_t status;
+ if (uid >= 0) {
+ status = mPowerManager->acquireWakeLockWithUid(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"),
+ uid);
+ } else {
+ status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"));
+ }
if (status == NO_ERROR) {
mWakeLockToken = binder;
}
@@ -520,6 +544,41 @@ void AudioFlinger::ThreadBase::releaseWakeLock_l()
}
}
+void AudioFlinger::ThreadBase::updateWakeLockUids(const SortedVector<int> &uids) {
+ Mutex::Autolock _l(mLock);
+ updateWakeLockUids_l(uids);
+}
+
+void AudioFlinger::ThreadBase::getPowerManager_l() {
+
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("Thread %s cannot connect to the power manager service", mName);
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
+
+ getPowerManager_l();
+ if (mWakeLockToken == NULL) {
+ ALOGE("no wake lock to update!");
+ return;
+ }
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status;
+ status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array());
+ ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+ }
+}
+
void AudioFlinger::ThreadBase::clearPowerManager()
{
Mutex::Autolock _l(mLock);
@@ -697,14 +756,22 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
goto Exit;
}
- // Do not allow effects with session ID 0 on direct output or duplicating threads
- // TODO: add rule for hw accelerated effects on direct outputs with non PCM format
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) {
- ALOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
- desc->name, sessionId);
- lStatus = BAD_VALUE;
- goto Exit;
+ // Allow global effects only on offloaded and mixer threads
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ switch (mType) {
+ case MIXER:
+ case OFFLOAD:
+ break;
+ case DIRECT:
+ case DUPLICATING:
+ case RECORD:
+ default:
+ ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
}
+
// Only Pre processor effects are allowed on input threads and only on input threads
if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
@@ -747,6 +814,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
if (lStatus != NO_ERROR) {
goto Exit;
}
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
lStatus = chain->addEffect_l(effect);
if (lStatus != NO_ERROR) {
goto Exit;
@@ -808,6 +877,10 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
sp<EffectChain> chain = getEffectChain_l(sessionId);
bool chainCreated = false;
+ ALOGD_IF((mType == OFFLOAD) && !effect->isOffloadable(),
+ "addEffect_l() on offloaded thread %p: effect %s does not support offload flags %x",
+ this, effect->desc().name, effect->desc().flags);
+
if (chain == 0) {
// create a new chain for this session
ALOGV("addEffect_l() new effect chain for session %d", sessionId);
@@ -824,6 +897,8 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
return BAD_VALUE;
}
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
status_t status = chain->addEffect_l(effect);
if (status != NO_ERROR) {
if (chainCreated) {
@@ -926,16 +1001,26 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
audio_devices_t device,
type_t type)
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
- mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mNormalFrameCount(0), mMixBuffer(NULL),
+ mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mActiveTracksGeneration(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
mMixerStatusIgnoringFastTracks(MIXER_IDLE),
standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
+ mBytesRemaining(0),
+ mCurrentWriteLength(0),
+ mUseAsyncWrite(false),
+ mWriteAckSequence(0),
+ mDrainSequence(0),
+ mSignalPending(false),
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
- mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1)
+ mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ // mLatchD, mLatchQ,
+ mLatchDValid(false), mLatchQValid(false)
{
snprintf(mName, kNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
@@ -975,7 +1060,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
AudioFlinger::PlaybackThread::~PlaybackThread()
{
mAudioFlinger->unregisterWriter(mNBLogWriter);
- delete [] mMixBuffer;
+ delete [] mAllocMixBuffer;
}
void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
@@ -1043,6 +1128,8 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>&
snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
result.append(buffer);
+ snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
+ result.append(buffer);
snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n",
ns2ms(systemTime() - mLastWriteTime));
result.append(buffer);
@@ -1100,6 +1187,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status)
{
sp<Track> track;
@@ -1122,7 +1210,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
(
(tid != -1) &&
((frameCount == 0) ||
- (frameCount >= (mFrameCount * kFastTrackMultiplier)))
+ (frameCount >= mFrameCount))
)
) &&
// PCM data
@@ -1181,7 +1269,22 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
goto Exit;
}
}
+ } else if (mType == OFFLOAD) {
+ if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+ ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
+ "for output %p with format %d",
+ sampleRate, format, channelMask, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
} else {
+ if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) {
+ ALOGE("createTrack_l() Bad parameter: format %d \""
+ "for output %p with format %d",
+ format, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
// Resampler implementation limits input sampling rate to 2 x output sampling rate.
if (sampleRate > mSampleRate*2) {
ALOGE("Sample rate out of range: %u mSampleRate %u", sampleRate, mSampleRate);
@@ -1218,15 +1321,16 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
if (!isTimed) {
track = new Track(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId, *flags);
+ channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
} else {
track = TimedTrack::create(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId);
+ channelMask, frameCount, sharedBuffer, sessionId, uid);
}
if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
lStatus = NO_MEMORY;
goto Exit;
}
+
mTracks.add(track);
sp<EffectChain> chain = getEffectChain_l(sessionId);
@@ -1301,12 +1405,14 @@ void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, f
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].volume = value;
+ broadcast_l();
}
void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].mute = muted;
+ broadcast_l();
}
float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
@@ -1326,10 +1432,37 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
// the track is newly added, make sure it fills up all its
// buffers before playing. This is to ensure the client will
// effectively get the latency it requested.
- track->mFillingUpStatus = Track::FS_FILLING;
+ if (!track->isOutputTrack()) {
+ TrackBase::track_state state = track->mState;
+ mLock.unlock();
+ status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ // abort track was stopped/paused while we released the lock
+ if (state != track->mState) {
+ if (status == NO_ERROR) {
+ mLock.unlock();
+ AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ }
+ return INVALID_OPERATION;
+ }
+ // abort if start is rejected by audio policy manager
+ if (status != NO_ERROR) {
+ return PERMISSION_DENIED;
+ }
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+#endif
+ }
+
+ track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
+ mWakeLockUids.add(track->uid());
+ mActiveTracksGeneration++;
+ mLatestActiveTrack = track;
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
@@ -1340,20 +1473,25 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
status = NO_ERROR;
}
- ALOGV("mWaitWorkCV.broadcast");
- mWaitWorkCV.broadcast();
+ ALOGV("signal playback thread");
+ broadcast_l();
return status;
}
-// destroyTrack_l() must be called with ThreadBase::mLock held
-void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
+bool AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
{
- track->mState = TrackBase::TERMINATED;
+ track->terminate();
// active tracks are removed by threadLoop()
- if (mActiveTracks.indexOf(track) < 0) {
+ bool trackActive = (mActiveTracks.indexOf(track) >= 0);
+ track->mState = TrackBase::STOPPED;
+ if (!trackActive) {
removeTrack_l(track);
+ } else if (track->isFastTrack() || track->isOffloaded()) {
+ track->mState = TrackBase::STOPPING_1;
}
+
+ return trackActive;
}
void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
@@ -1377,18 +1515,25 @@ void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
}
}
-String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+void AudioFlinger::PlaybackThread::broadcast_l()
{
- String8 out_s8 = String8("");
- char *s;
+ // Thread could be blocked waiting for async
+ // so signal it to handle state changes immediately
+ // If threadLoop is currently unlocked a signal of mWaitWorkCV will
+ // be lost so we also flag to prevent it blocking on mWaitWorkCV
+ mSignalPending = true;
+ mWaitWorkCV.broadcast();
+}
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {
- return out_s8;
+ return String8();
}
- s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
- out_s8 = String8(s);
+ char *s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
+ const String8 out_s8(s);
free(s);
return out_s8;
}
@@ -1404,7 +1549,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
switch (event) {
case AudioSystem::OUTPUT_OPENED:
case AudioSystem::OUTPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
+ desc.channelMask = mChannelMask;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mNormalFrameCount; // FIXME see
@@ -1422,12 +1567,80 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
+void AudioFlinger::PlaybackThread::writeCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetWriteBlocked();
+}
+
+void AudioFlinger::PlaybackThread::drainCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetDraining();
+}
+
+void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
+ mWriteAckSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+ mDrainSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+// static
+int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event,
+ void *param,
+ void *cookie)
+{
+ AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie;
+ ALOGV("asyncCallback() event %d", event);
+ switch (event) {
+ case STREAM_CBK_EVENT_WRITE_READY:
+ me->writeCallback();
+ break;
+ case STREAM_CBK_EVENT_DRAIN_READY:
+ me->drainCallback();
+ break;
+ default:
+ ALOGW("asyncCallback() unknown event %d", event);
+ break;
+ }
+ return 0;
+}
+
void AudioFlinger::PlaybackThread::readOutputParameters()
{
+ // unfortunately we have no way of recovering from errors here, hence the LOG_FATAL
mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
+ if (!audio_is_output_channel(mChannelMask)) {
+ LOG_FATAL("HAL channel mask %#x not valid for output", mChannelMask);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mChannelMask != AUDIO_CHANNEL_OUT_STEREO) {
+ LOG_FATAL("HAL channel mask %#x not supported for mixed output; "
+ "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask);
+ }
+ mChannelCount = popcount(mChannelMask);
mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
+ if (!audio_is_valid_format(mFormat)) {
+ LOG_FATAL("HAL format %d not valid for output", mFormat);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ LOG_FATAL("HAL format %d not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT",
+ mFormat);
+ }
mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
if (mFrameCount & 15) {
@@ -1435,6 +1648,15 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
mFrameCount);
}
+ if ((mOutput->flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) &&
+ (mOutput->stream->set_callback != NULL)) {
+ if (mOutput->stream->set_callback(mOutput->stream,
+ AudioFlinger::PlaybackThread::asyncCallback, this) == 0) {
+ mUseAsyncWrite = true;
+ mCallbackThread = new AudioFlinger::AsyncCallbackThread(this);
+ }
+ }
+
// Calculate size of normal mix buffer relative to the HAL output buffer size
double multiplier = 1.0;
if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
@@ -1477,9 +1699,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters()
ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
mNormalFrameCount);
- delete[] mMixBuffer;
- mMixBuffer = new int16_t[mNormalFrameCount * mChannelCount];
- memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
+ delete[] mAllocMixBuffer;
+ size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize;
+ mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1];
+ mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align);
+ memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize);
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
@@ -1613,16 +1837,21 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
const Vector< sp<Track> >& tracksToRemove)
{
size_t count = tracksToRemove.size();
- if (CC_UNLIKELY(count)) {
+ if (count) {
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
- if ((track->sharedBuffer() != 0) &&
- (track->mState == TrackBase::ACTIVE || track->mState == TrackBase::RESUMING)) {
+ if (!track->isOutputTrack()) {
AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
+#endif
+ if (track->isTerminated()) {
+ AudioSystem::releaseOutput(mId);
+ }
}
}
}
-
}
void AudioFlinger::PlaybackThread::checkSilentMode_l()
@@ -1643,17 +1872,18 @@ void AudioFlinger::PlaybackThread::checkSilentMode_l()
}
// shared by MIXER and DIRECT, overridden by DUPLICATING
-void AudioFlinger::PlaybackThread::threadLoop_write()
+ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
- int bytesWritten;
+ ssize_t bytesWritten;
// If an NBAIO sink is present, use it to write the normal mixer's submix
if (mNormalSink != 0) {
#define mBitShift 2 // FIXME
- size_t count = mixBufferSize >> mBitShift;
+ size_t count = mBytesRemaining >> mBitShift;
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) >> 1;
ATRACE_BEGIN("write");
// update the setpoint when AudioFlinger::mScreenState changes
uint32_t screenState = AudioFlinger::mScreenState;
@@ -1665,24 +1895,70 @@ void AudioFlinger::PlaybackThread::threadLoop_write()
(pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
}
}
- ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
+ ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count);
ATRACE_END();
if (framesWritten > 0) {
bytesWritten = framesWritten << mBitShift;
} else {
bytesWritten = framesWritten;
}
+ status_t status = mNormalSink->getTimestamp(mLatchD.mTimestamp);
+ if (status == NO_ERROR) {
+ size_t totalFramesWritten = mNormalSink->framesWritten();
+ if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
+ mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
+ mLatchDValid = true;
+ }
+ }
// otherwise use the HAL / AudioStreamOut directly
} else {
- // Direct output thread.
- bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
+ // Direct output and offload threads
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
+ mWriteAckSequence += 2;
+ mWriteAckSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
+ // FIXME We should have an implementation of timestamps for direct output threads.
+ // They are used e.g for multichannel PCM playback over HDMI.
+ bytesWritten = mOutput->stream->write(mOutput->stream,
+ mMixBuffer + offset, mBytesRemaining);
+ if (mUseAsyncWrite &&
+ ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
+ // do not wait for async callback in case of error of full write
+ mWriteAckSequence &= ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
}
- if (bytesWritten > 0) {
- mBytesWritten += mixBufferSize;
- }
mNumWrites++;
mInWrite = false;
+ mStandby = false;
+ return bytesWritten;
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_drain()
+{
+ if (mOutput->stream->drain) {
+ ALOGV("draining %s", (mMixerStatus == MIXER_DRAIN_TRACK) ? "early" : "full");
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mDrainSequence & 1, "threadLoop_drain(): out of sequence drain request");
+ mDrainSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
+ mOutput->stream->drain(mOutput->stream,
+ (mMixerStatus == MIXER_DRAIN_TRACK) ? AUDIO_DRAIN_EARLY_NOTIFY
+ : AUDIO_DRAIN_ALL);
+ }
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_exit()
+{
+ // Default implementation has nothing to do
}
/*
@@ -1713,7 +1989,7 @@ void AudioFlinger::PlaybackThread::cacheParameters_l()
void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
{
- ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
+ ALOGV("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
this, streamType, mTracks.size());
Mutex::Autolock _l(mLock);
@@ -1882,6 +2158,8 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// FIXME could this be made local to while loop?
writeFrames = 0;
+ int lastGeneration = 0;
+
cacheParameters_l();
sleepTime = idleSleepTime;
@@ -1899,6 +2177,8 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// and then that string will be logged at the next convenient opportunity.
const char *logString = NULL;
+ checkSilentMode_l();
+
while (!exitPending())
{
cpuStats.sample(myName);
@@ -1917,16 +2197,40 @@ bool AudioFlinger::PlaybackThread::threadLoop()
logString = NULL;
}
+ if (mLatchDValid) {
+ mLatchQ = mLatchD;
+ mLatchDValid = false;
+ mLatchQValid = true;
+ }
+
if (checkForNewParameters_l()) {
cacheParameters_l();
}
saveOutputTracks();
+ if (mSignalPending) {
+ // A signal was raised while we were unlocked
+ mSignalPending = false;
+ } else if (waitingAsyncCallback_l()) {
+ if (exitPending()) {
+ break;
+ }
+ releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
+ ALOGV("wait async completion");
+ mWaitWorkCV.wait(mLock);
+ ALOGV("async completion/wake");
+ acquireWakeLock_l();
+ standbyTime = systemTime() + standbyDelay;
+ sleepTime = 0;
- // put audio hardware into standby after short delay
- if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
- isSuspended())) {
- if (!mStandby) {
+ continue;
+ }
+ if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ isSuspended()) {
+ // put audio hardware into standby after short delay
+ if (shouldStandby_l()) {
threadLoop_standby();
@@ -1944,6 +2248,8 @@ bool AudioFlinger::PlaybackThread::threadLoop()
}
releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
// wait until we have something to do...
ALOGV("%s going to sleep", myName.string());
mWaitWorkCV.wait(mLock);
@@ -1953,7 +2259,7 @@ bool AudioFlinger::PlaybackThread::threadLoop()
mMixerStatus = MIXER_IDLE;
mMixerStatusIgnoringFastTracks = MIXER_IDLE;
mBytesWritten = 0;
-
+ mBytesRemaining = 0;
checkSilentMode_l();
standbyTime = systemTime() + standbyDelay;
@@ -1965,29 +2271,56 @@ bool AudioFlinger::PlaybackThread::threadLoop()
continue;
}
}
-
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
+ // compare with previously applied list
+ if (lastGeneration != mActiveTracksGeneration) {
+ // update wakelock
+ updateWakeLockUids_l(mWakeLockUids);
+ lastGeneration = mActiveTracksGeneration;
+ }
+
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
// or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
- }
-
- if (CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {
- threadLoop_mix();
- } else {
- threadLoop_sleepTime();
- }
+ } // mLock scope ends
+
+ if (mBytesRemaining == 0) {
+ mCurrentWriteLength = 0;
+ if (mMixerStatus == MIXER_TRACKS_READY) {
+ // threadLoop_mix() sets mCurrentWriteLength
+ threadLoop_mix();
+ } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
+ && (mMixerStatus != MIXER_DRAIN_ALL)) {
+ // threadLoop_sleepTime sets sleepTime to 0 if data
+ // must be written to HAL
+ threadLoop_sleepTime();
+ if (sleepTime == 0) {
+ mCurrentWriteLength = mixBufferSize;
+ }
+ }
+ mBytesRemaining = mCurrentWriteLength;
+ if (isSuspended()) {
+ sleepTime = suspendSleepTimeUs();
+ // simulate write to HAL when suspended
+ mBytesWritten += mixBufferSize;
+ mBytesRemaining = 0;
+ }
- if (isSuspended()) {
- sleepTime = suspendSleepTimeUs();
- mBytesWritten += mixBufferSize;
+ // only process effects if we're going to write
+ if (sleepTime == 0 && mType != OFFLOAD) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ }
}
-
- // only process effects if we're going to write
- if (sleepTime == 0) {
+ // Process effect chains for offloaded thread even if no audio
+ // was read from audio track: process only updates effect state
+ // and thus does have to be synchronized with audio writes but may have
+ // to be called while waiting for async write callback
+ if (mType == OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
effectChains[i]->process_l();
}
@@ -1996,29 +2329,39 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// enable changes in effect chain
unlockEffectChains(effectChains);
- // sleepTime == 0 means we must write to audio hardware
- if (sleepTime == 0) {
-
- threadLoop_write();
-
+ if (!waitingAsyncCallback()) {
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ if (mBytesRemaining) {
+ ssize_t ret = threadLoop_write();
+ if (ret < 0) {
+ mBytesRemaining = 0;
+ } else {
+ mBytesWritten += ret;
+ mBytesRemaining -= ret;
+ }
+ } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
+ (mMixerStatus == MIXER_DRAIN_ALL)) {
+ threadLoop_drain();
+ }
if (mType == MIXER) {
- // write blocked detection
- nsecs_t now = systemTime();
- nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
- mNumDelayedWrites++;
- if ((now - lastWarning) > kWarningThrottleNs) {
- ATRACE_NAME("underrun");
- ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
- ns2ms(delta), mNumDelayedWrites, this);
- lastWarning = now;
+ // write blocked detection
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastWriteTime;
+ if (!mStandby && delta > maxPeriod) {
+ mNumDelayedWrites++;
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ATRACE_NAME("underrun");
+ ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+ ns2ms(delta), mNumDelayedWrites, this);
+ lastWarning = now;
+ }
}
- }
}
- mStandby = false;
- } else {
- usleep(sleepTime);
+ } else {
+ usleep(sleepTime);
+ }
}
// Finally let go of removed track(s), without the lock held
@@ -2040,8 +2383,10 @@ if (mType == MIXER) {
// is now local to this block, but will keep it for now (at least until merge done).
}
+ threadLoop_exit();
+
// for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ...
- if (mType == MIXER || mType == DIRECT) {
+ if (mType == MIXER || mType == DIRECT || mType == OFFLOAD) {
// put output stream into standby mode
if (!mStandby) {
mOutput->stream->common.standby(&mOutput->stream->common);
@@ -2049,12 +2394,54 @@ if (mType == MIXER) {
}
releaseWakeLock();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
ALOGV("Thread %p type %d exiting", this, mType);
return false;
}
+// removeTracks_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
+{
+ size_t count = tracksToRemove.size();
+ if (count) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track>& track = tracksToRemove.itemAt(i);
+ mActiveTracks.remove(track);
+ mWakeLockUids.remove(track->uid());
+ mActiveTracksGeneration++;
+ ALOGV("removeTracks_l removing track on session %d", track->sessionId());
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
+ track->sessionId());
+ chain->decActiveTrackCnt();
+ }
+ if (track->isTerminated()) {
+ removeTrack_l(track);
+ }
+ }
+ }
+
+}
+status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
+{
+ if (mNormalSink != 0) {
+ return mNormalSink->getTimestamp(timestamp);
+ }
+ if (mType == OFFLOAD && mOutput->stream->get_presentation_position) {
+ uint64_t position64;
+ int ret = mOutput->stream->get_presentation_position(
+ mOutput->stream, &position64, &timestamp.mTime);
+ if (ret == 0) {
+ timestamp.mPosition = (uint32_t)position64;
+ return NO_ERROR;
+ }
+ }
+ return INVALID_OPERATION;
+}
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
@@ -2068,7 +2455,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
// mNormalSink below
{
ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
- ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%d, mFormat=%d, mFrameSize=%u, "
+ ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%d, mFrameSize=%u, "
"mFrameCount=%d, mNormalFrameCount=%d",
mSampleRate, mChannelMask, mChannelCount, mFormat, mFrameSize, mFrameCount,
mNormalFrameCount);
@@ -2258,7 +2645,7 @@ void AudioFlinger::MixerThread::threadLoop_removeTracks(const Vector< sp<Track>
PlaybackThread::threadLoop_removeTracks(tracksToRemove);
}
-void AudioFlinger::MixerThread::threadLoop_write()
+ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
// FIXME we should only do one push per cycle; confirm this is true
// Start the fast mixer if it's not already running
@@ -2279,6 +2666,8 @@ void AudioFlinger::MixerThread::threadLoop_write()
#endif
}
state->mCommand = FastMixerState::MIX_WRITE;
+ mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
+ FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
sq->end();
sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
if (kUseFastMixer == FastMixer_Dynamic) {
@@ -2288,7 +2677,7 @@ void AudioFlinger::MixerThread::threadLoop_write()
sq->end(false /*didModify*/);
}
}
- PlaybackThread::threadLoop_write();
+ return PlaybackThread::threadLoop_write();
}
void AudioFlinger::MixerThread::threadLoop_standby()
@@ -2320,11 +2709,41 @@ void AudioFlinger::MixerThread::threadLoop_standby()
PlaybackThread::threadLoop_standby();
}
+// Empty implementation for standard mixer
+// Overridden for offloaded playback
+void AudioFlinger::PlaybackThread::flushOutput_l()
+{
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l()
+{
+ return false;
+}
+
+bool AudioFlinger::PlaybackThread::shouldStandby_l()
+{
+ return !mStandby;
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
+ if (mUseAsyncWrite != 0) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
}
void AudioFlinger::MixerThread::threadLoop_mix()
@@ -2345,6 +2764,7 @@ void AudioFlinger::MixerThread::threadLoop_mix()
// mix buffers...
mAudioMixer->process(pts);
+ mCurrentWriteLength = mixBufferSize;
// increase sleep time progressively when application underrun condition clears.
// Only increase sleep time if the mixer is ready for two consecutive times to avoid
// that a steady state of alternating ready/not ready conditions keeps the sleep time
@@ -2426,7 +2846,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
for (size_t i=0 ; i<count ; i++) {
- sp<Track> t = mActiveTracks[i].promote();
+ const sp<Track> t = mActiveTracks[i].promote();
if (t == 0) {
continue;
}
@@ -2462,8 +2882,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
track->mObservedUnderruns = underruns;
// don't count underruns that occur while stopping or pausing
// or stopped which can occur when flush() is called while active
- if (!(track->isStopping() || track->isPausing() || track->isStopped())) {
- track->mUnderrunCount += recentUnderruns;
+ if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
+ recentUnderruns > 0) {
+ // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
}
// This is similar to the state machine for normal tracks,
@@ -2472,7 +2894,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
switch (track->mState) {
case TrackBase::STOPPING_1:
// track stays active in STOPPING_1 state until first underrun
- if (recentUnderruns > 0) {
+ if (recentUnderruns > 0 || track->isTerminated()) {
track->mState = TrackBase::STOPPING_2;
}
break;
@@ -2506,7 +2928,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED, &track->mCblk->flags);
+ android_atomic_or(CBLK_DISABLED, &track->mCblk->mFlags);
// remove from active list, but state remains ACTIVE [confusing but true]
isActive = false;
break;
@@ -2514,7 +2936,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// fall through
case TrackBase::STOPPING_2:
case TrackBase::PAUSED:
- case TrackBase::TERMINATED:
case TrackBase::STOPPED:
case TrackBase::FLUSHED: // flush() while active
// Check for presentation complete if track is inactive
@@ -2595,28 +3016,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// app does not call stop() and relies on underrun to stop:
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
+ size_t desiredFrames;
+ uint32_t sr = track->sampleRate();
+ if (sr == mSampleRate) {
+ desiredFrames = mNormalFrameCount;
+ } else {
+ // +1 for rounding and +1 for additional sample needed for interpolation
+ desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
+ // add frames already consumed but not yet released by the resampler
+ // because cblk->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+ // the minimum track buffer size is normally twice the number of frames necessary
+ // to fill one buffer and the resampler should not leave more than one buffer worth
+ // of unreleased frames after each pass, but just in case...
+ ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+ }
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
- if (t->sampleRate() == mSampleRate) {
- minFrames = mNormalFrameCount;
- } else {
- // +1 for rounding and +1 for additional sample needed for interpolation
- minFrames = (mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
- // add frames already consumed but not yet released by the resampler
- // because cblk->framesReady() will include these frames
- minFrames += mAudioMixer->getUnreleasedFrames(track->name());
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(minFrames <= cblk->frameCount_);
- }
+ minFrames = desiredFrames;
}
- if ((track->framesReady() >= minFrames) && track->isReady() &&
+ // It's not safe to call framesReady() for a static buffer track, so assume it's ready
+ size_t framesReady;
+ if (track->sharedBuffer() == 0) {
+ framesReady = track->framesReady();
+ } else if (track->isStopped()) {
+ framesReady = 0;
+ } else {
+ framesReady = 1;
+ }
+ if ((framesReady >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
- ALOGVV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server,
- this);
+ ALOGVV("track %d s=%08x [OK] on thread %p", name, cblk->mServer, this);
mixedTracks++;
@@ -2645,7 +3077,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
param = AudioMixer::RAMP_VOLUME;
}
mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
- } else if (cblk->server != 0) {
+ // FIXME should not make a decision based on mServer
+ } else if (cblk->mServer != 0) {
// If the track is stopped before the first frame was mixed,
// do not apply ramp
param = AudioMixer::RAMP_VOLUME;
@@ -2663,7 +3096,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// read original volumes with volume control
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
- ServerProxy *proxy = track->mServerProxy;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
uint32_t vlr = proxy->getVolumeLR();
vl = vlr & 0xFFFF;
vr = vlr >> 16;
@@ -2690,6 +3123,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
va = (uint32_t)(v * sendLevel);
}
+
// Delegate volume control to effect in track effect chain if needed
if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
// Do not ramp volume if volume is controlled by effect
@@ -2736,7 +3170,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
// limit track sample rate to 2 x output sample rate, which changes at re-configuration
uint32_t maxSampleRate = mSampleRate * 2;
- uint32_t reqSampleRate = track->mServerProxy->getSampleRate();
+ uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
if (reqSampleRate == 0) {
reqSampleRate = mSampleRate;
} else if (reqSampleRate > maxSampleRate) {
@@ -2767,6 +3201,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
mixerStatus = MIXER_TRACKS_READY;
}
} else {
+ if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ }
// clear effect chain input buffer if an active track underruns to avoid sending
// previous audio buffer again to effects
chain = getEffectChain_l(track->sessionId());
@@ -2774,8 +3211,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
chain->clearInputBuffer();
}
- ALOGVV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user,
- cblk->server, this);
+ ALOGVV("track %d s=%08x [NOT READY] on thread %p", name, cblk->mServer, this);
if ((track->sharedBuffer() != 0) || track->isTerminated() ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
@@ -2791,7 +3227,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
tracksToRemove->add(track);
}
} else {
- track->mUnderrunCount++;
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
@@ -2799,7 +3234,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun;
// it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED, &cblk->flags);
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
// If one track is not ready, mark the mixer also not ready if:
// - the mixer was ready during previous round OR
// - no other track is ready
@@ -2861,30 +3296,13 @@ track_is_ready: ;
}
// remove all the tracks that need to be...
- count = tracksToRemove->size();
- if (CC_UNLIKELY(count)) {
- for (size_t i=0 ; i<count ; i++) {
- const sp<Track>& track = tracksToRemove->itemAt(i);
- mActiveTracks.remove(track);
- if (track->mainBuffer() != mMixBuffer) {
- chain = getEffectChain_l(track->sessionId());
- if (chain != 0) {
- ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
- track->sessionId());
- chain->decActiveTrackCnt();
- }
- }
- if (track->isTerminated()) {
- removeTrack_l(track);
- }
- }
- }
+ removeTracks_l(*tracksToRemove);
// mix buffer must be cleared if all tracks are connected to an
// effect chain as in this case the mixer will not write to
// mix buffer and track effects will accumulate into it
- if ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
- (mixedTracks == 0 && fastTracks > 0)) {
+ if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) ||
+ (mixedTracks == 0 && fastTracks > 0))) {
// FIXME as a performance optimization, should remember previous zero status
memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
}
@@ -2948,7 +3366,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if (value != AUDIO_CHANNEL_OUT_STEREO) {
+ if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
status = BAD_VALUE;
} else {
reconfig = true;
@@ -3009,10 +3427,8 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l()
keyValuePair.string());
}
if (status == NO_ERROR && reconfig) {
- delete mAudioMixer;
- // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
- mAudioMixer = NULL;
readOutputParameters();
+ delete mAudioMixer;
mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
@@ -3061,7 +3477,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar
write(fd, result.string(), result.size());
// Make a non-atomic copy of fast mixer dump state so it won't change underneath us
- FastMixerDumpState copy = mFastMixerDumpState;
+ const FastMixerDumpState copy(mFastMixerDumpState);
copy.dump(fd);
#ifdef STATE_QUEUE_DUMP
@@ -3116,10 +3532,63 @@ AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& aud
{
}
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
+ ThreadBase::type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type)
+ // mLeftVolFloat, mRightVolFloat
+{
+}
+
AudioFlinger::DirectOutputThread::~DirectOutputThread()
{
}
+void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
+{
+ audio_track_cblk_t* cblk = track->cblk();
+ float left, right;
+
+ if (mMasterMute || mStreamTypes[track->streamType()].mute) {
+ left = right = 0;
+ } else {
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = mMasterVolume * typeVolume;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
+ float v_clamped = v * (vlr & 0xFFFF);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * (vlr >> 16);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
+
+ if (lastTrack) {
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
+
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
+
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!mEffectChains.isEmpty()) {
+ mEffectChains[0]->setVolume_l(&vl, &vr);
+ left = (float)vl / (1 << 24);
+ right = (float)vr / (1 << 24);
+ }
+ if (mOutput->stream->set_volume) {
+ mOutput->stream->set_volume(mOutput->stream, left, right);
+ }
+ }
+ }
+}
+
+
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
Vector< sp<Track> > *tracksToRemove
)
@@ -3137,6 +3606,12 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
// The first time a track is added we wait
// for all its buffers to be filled before processing it
@@ -3146,66 +3621,24 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
} else {
minFrames = 1;
}
+
if ((track->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
{
- ALOGVV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ ALOGVV("track %d s=%08x [OK]", track->name(), cblk->mServer);
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- mLeftVolFloat = mRightVolFloat = 0;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
if (track->mState == TrackBase::RESUMING) {
track->mState = TrackBase::ACTIVE;
}
}
// compute volume for this track
- float left, right;
- if (mMasterMute || track->isPausing() || mStreamTypes[track->streamType()].mute) {
- left = right = 0;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = mMasterVolume * typeVolume;
- uint32_t vlr = track->mServerProxy->getVolumeLR();
- float v_clamped = v * (vlr & 0xFFFF);
- if (v_clamped > MAX_GAIN) {
- v_clamped = MAX_GAIN;
- }
- left = v_clamped/MAX_GAIN;
- v_clamped = v * (vlr >> 16);
- if (v_clamped > MAX_GAIN) {
- v_clamped = MAX_GAIN;
- }
- right = v_clamped/MAX_GAIN;
- }
- // Only consider last track started for volume and mixer state control.
- // This is the last entry in mActiveTracks unless a track underruns.
- // As we only care about the transition phase between two tracks on a
- // direct output, it is not a problem to ignore the underrun case.
- if (i == (count - 1)) {
- if (left != mLeftVolFloat || right != mRightVolFloat) {
- mLeftVolFloat = left;
- mRightVolFloat = right;
-
- // Convert volumes from float to 8.24
- uint32_t vl = (uint32_t)(left * (1 << 24));
- uint32_t vr = (uint32_t)(right * (1 << 24));
-
- // Delegate volume control to effect in track effect chain if needed
- // only one effect chain can be present on DirectOutputThread, so if
- // there is one, the track is connected to it
- if (!mEffectChains.isEmpty()) {
- // Do not ramp volume if volume is controlled by effect
- mEffectChains[0]->setVolume_l(&vl, &vr);
- left = (float)vl / (1 << 24);
- right = (float)vr / (1 << 24);
- }
- mOutput->stream->set_volume(mOutput->stream, left, right);
- }
-
+ processVolume_l(track, last);
+ if (last) {
// reset retry count
track->mRetryCount = kMaxTrackRetriesDirect;
mActiveTrack = t;
@@ -3214,11 +3647,11 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
} else {
// clear effect chain input buffer if the last active track started underruns
// to avoid sending previous audio buffer again to effects
- if (!mEffectChains.isEmpty() && (i == (count -1))) {
+ if (!mEffectChains.isEmpty() && last) {
mEffectChains[0]->clearInputBuffer();
}
- ALOGVV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ ALOGVV("track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
if ((track->sharedBuffer() != 0) || track->isTerminated() ||
track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
@@ -3226,7 +3659,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
// TODO: implement behavior for compressed audio
size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
size_t framesWritten = mBytesWritten / mFrameSize;
- if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (mStandby || !last ||
+ track->presentationComplete(framesWritten, audioHALFrames)) {
if (track->isStopped()) {
track->reset();
}
@@ -3239,7 +3673,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
tracksToRemove->add(track);
- } else if (i == (count -1)){
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
+ } else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
}
@@ -3247,35 +3684,21 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
}
// remove all the tracks that need to be...
- count = tracksToRemove->size();
- if (CC_UNLIKELY(count)) {
- for (size_t i = 0 ; i < count ; i++) {
- const sp<Track>& track = tracksToRemove->itemAt(i);
- mActiveTracks.remove(track);
- if (!mEffectChains.isEmpty()) {
- ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
- track->sessionId());
- mEffectChains[0]->decActiveTrackCnt();
- }
- if (track->isTerminated()) {
- removeTrack_l(track);
- }
- }
- }
+ removeTracks_l(*tracksToRemove);
return mixerStatus;
}
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
- AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
int8_t *curBuf = (int8_t *)mMixBuffer;
// output audio to hardware
while (frameCount) {
+ AudioBufferProvider::Buffer buffer;
buffer.frameCount = frameCount;
mActiveTrack->getNextBuffer(&buffer);
- if (CC_UNLIKELY(buffer.raw == NULL)) {
+ if (buffer.raw == NULL) {
memset(curBuf, 0, frameCount * mFrameSize);
break;
}
@@ -3284,10 +3707,10 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
curBuf += buffer.frameCount * mFrameSize;
mActiveTrack->releaseBuffer(&buffer);
}
+ mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer;
sleepTime = 0;
standbyTime = systemTime() + standbyDelay;
mActiveTrack.clear();
-
}
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
@@ -3403,7 +3826,382 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l()
// use shorter standby delay as on normal output to release
// hardware resources as soon as possible
- standbyDelay = microseconds(activeSleepTime*2);
+ if (audio_is_linear_pcm(mFormat)) {
+ standbyDelay = microseconds(activeSleepTime*2);
+ } else {
+ standbyDelay = kOffloadStandbyDelayNs;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
+ const wp<AudioFlinger::PlaybackThread>& playbackThread)
+ : Thread(false /*canCallJava*/),
+ mPlaybackThread(playbackThread),
+ mWriteAckSequence(0),
+ mDrainSequence(0)
+{
+}
+
+AudioFlinger::AsyncCallbackThread::~AsyncCallbackThread()
+{
+}
+
+void AudioFlinger::AsyncCallbackThread::onFirstRef()
+{
+ run("Offload Cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool AudioFlinger::AsyncCallbackThread::threadLoop()
+{
+ while (!exitPending()) {
+ uint32_t writeAckSequence;
+ uint32_t drainSequence;
+
+ {
+ Mutex::Autolock _l(mLock);
+ while (!((mWriteAckSequence & 1) ||
+ (mDrainSequence & 1) ||
+ exitPending())) {
+ mWaitWorkCV.wait(mLock);
+ }
+
+ if (exitPending()) {
+ break;
+ }
+ ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ writeAckSequence = mWriteAckSequence;
+ mWriteAckSequence &= ~1;
+ drainSequence = mDrainSequence;
+ mDrainSequence &= ~1;
+ }
+ {
+ sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
+ if (playbackThread != 0) {
+ if (writeAckSequence & 1) {
+ playbackThread->resetWriteBlocked(writeAckSequence >> 1);
+ }
+ if (drainSequence & 1) {
+ playbackThread->resetDraining(drainSequence >> 1);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::AsyncCallbackThread::exit()
+{
+ ALOGV("AsyncCallbackThread::exit");
+ Mutex::Autolock _l(mLock);
+ requestExit();
+ mWaitWorkCV.broadcast();
+}
+
+void AudioFlinger::AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mWriteAckSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetWriteBlocked()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mWriteAckSequence & 2) {
+ mWriteAckSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::AsyncCallbackThread::setDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mDrainSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetDraining()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mDrainSequence & 2) {
+ mDrainSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
+ : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
+ mHwPaused(false),
+ mFlushPending(false),
+ mPausedBytesRemaining(0)
+{
+ //FIXME: mStandby should be set to true by ThreadBase constructor
+ mStandby = true;
+}
+
+void AudioFlinger::OffloadThread::threadLoop_exit()
+{
+ if (mFlushPending || mHwPaused) {
+ // If a flush is pending or track was paused, just discard buffered data
+ flushHw_l();
+ } else {
+ mMixerStatus = MIXER_DRAIN_ALL;
+ threadLoop_drain();
+ }
+ mCallbackThread->exit();
+ PlaybackThread::threadLoop_exit();
+}
+
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTracks_l(
+ Vector< sp<Track> > *tracksToRemove
+)
+{
+ size_t count = mActiveTracks.size();
+
+ mixer_state mixerStatus = MIXER_IDLE;
+ bool doHwPause = false;
+ bool doHwResume = false;
+
+ ALOGV("OffloadThread::prepareTracks_l active tracks %d", count);
+
+ // find out which tracks need to be processed
+ for (size_t i = 0; i < count; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
+ // The track died recently
+ if (t == 0) {
+ continue;
+ }
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
+
+ if (track->isPausing()) {
+ track->setPaused();
+ if (last) {
+ if (!mHwPaused) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
+ // If we were part way through writing the mixbuffer to
+ // the HAL we must save this until we resume
+ // BUG - this will be wrong if a different track is made active,
+ // in that case we want to discard the pending data in the
+ // mixbuffer and tell the client to present it again when the
+ // track is resumed
+ mPausedWriteLength = mCurrentWriteLength;
+ mPausedBytesRemaining = mBytesRemaining;
+ mBytesRemaining = 0; // stop writing
+ }
+ tracksToRemove->add(track);
+ } else if (track->framesReady() && track->isReady() &&
+ !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
+ ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ if (last) {
+ if (mPausedBytesRemaining) {
+ // Need to continue write that was interrupted
+ mCurrentWriteLength = mPausedWriteLength;
+ mBytesRemaining = mPausedBytesRemaining;
+ mPausedBytesRemaining = 0;
+ }
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ // threadLoop_mix() will handle the case that we need to
+ // resume an interrupted write
+ }
+ // enable write to audio HAL
+ sleepTime = 0;
+ }
+ }
+ }
+
+ if (last) {
+ sp<Track> previousTrack = mPreviousTrack.promote();
+ if (previousTrack != 0) {
+ if (track != previousTrack.get()) {
+ // Flush any data still being written from last track
+ mBytesRemaining = 0;
+ if (mPausedBytesRemaining) {
+ // Last track was paused so we also need to flush saved
+ // mixbuffer state and invalidate track so that it will
+ // re-submit that unwritten data when it is next resumed
+ mPausedBytesRemaining = 0;
+ // Invalidate is a bit drastic - would be more efficient
+ // to have a flag to tell client that some of the
+ // previously written data was lost
+ previousTrack->invalidate();
+ }
+ // flush data already sent to the DSP if changing audio session as audio
+ // comes from a different source. Also invalidate previous track to force a
+ // seek when resuming.
+ if (previousTrack->sessionId() != track->sessionId()) {
+ previousTrack->invalidate();
+ mFlushPending = true;
+ }
+ }
+ }
+ mPreviousTrack = track;
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ mActiveTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ } else {
+ ALOGVV("OffloadThread: track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
+ if (track->isStopping_1()) {
+ // Hardware buffer can hold a large amount of audio so we must
+ // wait for all current track's data to drain before we say
+ // that the track is stopped.
+ if (mBytesRemaining == 0) {
+ // Only start draining when all data in mixbuffer
+ // has been written
+ ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
+ track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
+ // do not drain if no data was ever sent to HAL (mStandby == true)
+ if (last && !mStandby) {
+ // do not modify drain sequence if we are already draining. This happens
+ // when resuming from pause after drain.
+ if ((mDrainSequence & 1) == 0) {
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ }
+ if (mHwPaused) {
+ // It is possible to move from PAUSED to STOPPING_1 without
+ // a resume so we must ensure hardware is running
+ doHwResume = true;
+ mHwPaused = false;
+ }
+ }
+ }
+ } else if (track->isStopping_2()) {
+ // Drain has completed or we are in standby, signal presentation complete
+ if (!(mDrainSequence & 1) || !last || mStandby) {
+ track->mState = TrackBase::STOPPED;
+ size_t audioHALFrames =
+ (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t framesWritten =
+ mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
+ track->presentationComplete(framesWritten, audioHALFrames);
+ track->reset();
+ tracksToRemove->add(track);
+ }
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
+ track->name());
+ tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
+ } else if (last){
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ }
+ // compute volume for this track
+ processVolume_l(track, last);
+ }
+
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) {
+ mOutput->stream->pause(mOutput->stream);
+ if (!doHwPause) {
+ doHwResume = true;
+ }
+ }
+ if (mFlushPending) {
+ flushHw_l();
+ mFlushPending = false;
+ }
+ if (!mStandby && doHwResume) {
+ mOutput->stream->resume(mOutput->stream);
+ }
+
+ // remove all the tracks that need to be...
+ removeTracks_l(*tracksToRemove);
+
+ return mixerStatus;
+}
+
+void AudioFlinger::OffloadThread::flushOutput_l()
+{
+ mFlushPending = true;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
+{
+ ALOGVV("waitingAsyncCallback_l mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ if (mUseAsyncWrite && ((mWriteAckSequence & 1) || (mDrainSequence & 1))) {
+ return true;
+ }
+ return false;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::shouldStandby_l()
+{
+ bool TrackPaused = false;
+
+ // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
+ // after a timeout and we will enter standby then.
+ if (mTracks.size() > 0) {
+ TrackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ }
+
+ return !mStandby && !TrackPaused;
+}
+
+
+bool AudioFlinger::OffloadThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
+void AudioFlinger::OffloadThread::flushHw_l()
+{
+ mOutput->stream->flush(mOutput->stream);
+ // Flush anything still waiting in the mixbuffer
+ mCurrentWriteLength = 0;
+ mBytesRemaining = 0;
+ mPausedWriteLength = 0;
+ mPausedBytesRemaining = 0;
+ if (mUseAsyncWrite) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
}
// ----------------------------------------------------------------------------
@@ -3434,6 +4232,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix()
}
sleepTime = 0;
writeFrames = mNormalFrameCount;
+ mCurrentWriteLength = mixBufferSize;
standbyTime = systemTime() + standbyDelay;
}
@@ -3457,12 +4256,13 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
}
}
-void AudioFlinger::DuplicatingThread::threadLoop_write()
+ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
{
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);
}
- mBytesWritten += mixBufferSize;
+ mStandby = false;
+ return (ssize_t)mixBufferSize;
}
void AudioFlinger::DuplicatingThread::threadLoop_standby()
@@ -3493,7 +4293,8 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
mSampleRate,
mFormat,
mChannelMask,
- frameCount);
+ frameCount,
+ IPCThreadState::self()->getCallingUid());
if (outputTrack->cblk() != NULL) {
thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
@@ -3583,7 +4384,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex and mInputBytes set by readInputParameters()
+ // mRsmpInIndex and mBufferSize set by readInputParameters()
mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
@@ -3595,7 +4396,6 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
snprintf(mName, kNameLength, "AudioIn_%X", id);
readInputParameters();
-
}
@@ -3627,7 +4427,11 @@ bool AudioFlinger::RecordThread::threadLoop()
nsecs_t lastWarning = 0;
inputStandBy();
- acquireWakeLock();
+ {
+ Mutex::Autolock _l(mLock);
+ activeTrack = mActiveTrack;
+ acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1);
+ }
// used to verify we've read at least once before evaluating how many bytes were read
bool readOnce = false;
@@ -3640,6 +4444,12 @@ bool AudioFlinger::RecordThread::threadLoop()
{ // scope for mLock
Mutex::Autolock _l(mLock);
checkForNewParameters_l();
+ if (mActiveTrack != 0 && activeTrack != mActiveTrack) {
+ SortedVector<int> tmp;
+ tmp.add(mActiveTrack->uid());
+ updateWakeLockUids_l(tmp);
+ }
+ activeTrack = mActiveTrack;
if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
standby();
@@ -3652,11 +4462,14 @@ bool AudioFlinger::RecordThread::threadLoop()
// go to sleep
mWaitWorkCV.wait(mLock);
ALOGV("RecordThread: loop starting");
- acquireWakeLock_l();
+ acquireWakeLock_l(mActiveTrack != 0 ? mActiveTrack->uid() : -1);
continue;
}
if (mActiveTrack != 0) {
- if (mActiveTrack->mState == TrackBase::PAUSING) {
+ if (mActiveTrack->isTerminated()) {
+ removeTrack_l(mActiveTrack);
+ mActiveTrack.clear();
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
standby();
mActiveTrack.clear();
mStartStopCond.broadcast();
@@ -3675,11 +4488,9 @@ bool AudioFlinger::RecordThread::threadLoop()
mStartStopCond.broadcast();
}
mStandby = false;
- } else if (mActiveTrack->mState == TrackBase::TERMINATED) {
- removeTrack_l(mActiveTrack);
- mActiveTrack.clear();
}
}
+
lockEffectChains_l(effectChains);
}
@@ -3695,7 +4506,8 @@ bool AudioFlinger::RecordThread::threadLoop()
}
buffer.frameCount = mFrameCount;
- if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ status_t status = mActiveTrack->getNextBuffer(&buffer);
+ if (status == NO_ERROR) {
readOnce = true;
size_t framesOut = buffer.frameCount;
if (mResampler == NULL) {
@@ -3710,8 +4522,7 @@ bool AudioFlinger::RecordThread::threadLoop()
framesIn = framesOut;
mRsmpInIndex += framesIn;
framesOut -= framesIn;
- if (mChannelCount == mReqChannelCount ||
- mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ if (mChannelCount == mReqChannelCount) {
memcpy(dst, src, framesIn * mFrameSize);
} else {
if (mChannelCount == 1) {
@@ -3725,9 +4536,7 @@ bool AudioFlinger::RecordThread::threadLoop()
}
if (framesOut && mFrameCount == mRsmpInIndex) {
void *readInto;
- if (framesOut == mFrameCount &&
- (mChannelCount == mReqChannelCount ||
- mFormat != AUDIO_FORMAT_PCM_16_BIT)) {
+ if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
readInto = buffer.raw;
framesOut = 0;
} else {
@@ -3735,7 +4544,7 @@ bool AudioFlinger::RecordThread::threadLoop()
mRsmpInIndex = 0;
}
mBytesRead = mInput->stream->read(mInput->stream, readInto,
- mInputBytes);
+ mBufferSize);
if (mBytesRead <= 0) {
if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
{
@@ -3760,7 +4569,8 @@ bool AudioFlinger::RecordThread::threadLoop()
} else {
// resampling
- memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t));
+ // resampler accumulates, but we only have one source track
+ memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
// alter output frame count as if we were expecting stereo samples
if (mChannelCount == 1 && mReqChannelCount == 1) {
framesOut >>= 1;
@@ -3770,6 +4580,7 @@ bool AudioFlinger::RecordThread::threadLoop()
// 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) {
+ // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
// the resampler always outputs stereo samples:
// do post stereo to mono conversion
@@ -3778,6 +4589,7 @@ bool AudioFlinger::RecordThread::threadLoop()
} else {
ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
}
+ // now done with mRsmpOutBuffer
}
if (mFramestoDrop == 0) {
@@ -3826,6 +4638,10 @@ bool AudioFlinger::RecordThread::threadLoop()
{
Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ track->invalidate();
+ }
mActiveTrack.clear();
mStartStopCond.broadcast();
}
@@ -3856,7 +4672,8 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- IAudioFlinger::track_flags_t flags,
+ int uid,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status)
{
@@ -3865,9 +4682,59 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
lStatus = initCheck();
if (lStatus != NO_ERROR) {
- ALOGE("Audio driver not initialized.");
+ ALOGE("createRecordTrack_l() audio driver not initialized");
goto Exit;
}
+ // client expresses a preference for FAST, but we get the final say
+ if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (
+ // use case: callback handler and frame count is default or at least as large as HAL
+ (
+ (tid != -1) &&
+ ((frameCount == 0) ||
+ (frameCount >= mFrameCount))
+ ) &&
+ // FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format)
+ // mono or stereo
+ ( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
+ (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
+ // hardware sample rate
+ (sampleRate == mSampleRate) &&
+ // record thread has an associated fast recorder
+ hasFastRecorder()
+ // FIXME test that RecordThread for this fast track has a capable output HAL
+ // FIXME add a permission test also?
+ ) {
+ // if frameCount not specified, then it defaults to fast recorder (HAL) frame count
+ if (frameCount == 0) {
+ frameCount = mFrameCount * kFastTrackMultiplier;
+ }
+ ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
+ frameCount, mFrameCount);
+ } else {
+ ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+ "hasFastRecorder=%d tid=%d",
+ frameCount, mFrameCount, format,
+ audio_is_linear_pcm(format),
+ channelMask, sampleRate, mSampleRate, hasFastRecorder(), tid);
+ *flags &= ~IAudioFlinger::TRACK_FAST;
+ // For compatibility with AudioRecord calculation, buffer depth is forced
+ // to be at least 2 x the record thread frame count and cover audio hardware latency.
+ // This is probably too conservative, but legacy application code may depend on it.
+ // If you change this calculation, also review the start threshold which is related.
+ uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
+ size_t mNormalFrameCount = 2048; // FIXME
+ uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ size_t minFrameCount = mNormalFrameCount * minBufCount;
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
+ }
// FIXME use flags and tid similar to createTrack_l()
@@ -3875,10 +4742,12 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
Mutex::Autolock _l(mLock);
track = new RecordTrack(this, client, sampleRate,
- format, channelMask, frameCount, sessionId);
+ format, channelMask, frameCount, sessionId, uid);
if (track->getCblk() == 0) {
+ ALOGE("createRecordTrack_l() no control block");
lStatus = NO_MEMORY;
+ track.clear();
goto Exit;
}
mTracks.add(track);
@@ -3888,6 +4757,13 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR
mAudioFlinger->btNrecIsOff();
setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
+
+ 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);
+ }
}
lStatus = NO_ERROR;
@@ -3969,6 +4845,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
ALOGV("Record started OK");
return status;
}
+
startError:
AudioSystem::stopInput(mId);
clearSyncStartEvent();
@@ -4003,8 +4880,9 @@ void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event
}
}
-bool AudioFlinger::RecordThread::stop_l(RecordThread::RecordTrack* recordTrack) {
+bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
ALOGV("RecordThread::stop");
+ AutoMutex _l(mLock);
if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
return false;
}
@@ -4055,7 +4933,8 @@ status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
// destroyTrack_l() must be called with ThreadBase::mLock held
void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
{
- track->mState = TrackBase::TERMINATED;
+ track->terminate();
+ track->mState = TrackBase::STOPPED;
// active tracks are removed by threadLoop()
if (mActiveTrack != track) {
removeTrack_l(track);
@@ -4087,7 +4966,7 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a
if (mActiveTrack != 0) {
snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
result.append(buffer);
- snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
+ snprintf(buffer, SIZE, "Buffer size: %u bytes\n", mBufferSize);
result.append(buffer);
snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
result.append(buffer);
@@ -4140,7 +5019,7 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer*
int channelCount;
if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
if (mBytesRead <= 0) {
if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
ALOGE("RecordThread::getNextBuffer() Error reading audio input");
@@ -4196,8 +5075,12 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- reqFormat = (audio_format_t) value;
- reconfig = true;
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reqFormat = (audio_format_t) value;
+ reconfig = true;
+ }
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
reqChannelCount = popcount(value);
@@ -4289,16 +5172,13 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l()
String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
{
- char *s;
- String8 out_s8 = String8();
-
Mutex::Autolock _l(mLock);
if (initCheck() != NO_ERROR) {
- return out_s8;
+ return String8();
}
- s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
- out_s8 = String8(s);
+ char *s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
+ const String8 out_s8(s);
free(s);
return out_s8;
}
@@ -4310,7 +5190,7 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
switch (event) {
case AudioSystem::INPUT_OPENED:
case AudioSystem::INPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
+ desc.channelMask = mChannelMask;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
desc.frameCount = mFrameCount;
@@ -4336,12 +5216,14 @@ void AudioFlinger::RecordThread::readInputParameters()
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
+ mChannelCount = popcount(mChannelMask);
mFormat = mInput->stream->common.get_format(&mInput->stream->common);
+ if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ }
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
- mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
- mFrameCount = mInputBytes / mFrameSize;
- mNormalFrameCount = mFrameCount; // not used by record, but used by input effects
+ mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
+ mFrameCount = mBufferSize / mFrameSize;
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2)
@@ -4357,7 +5239,7 @@ void AudioFlinger::RecordThread::readInputParameters()
mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
- mRsmpOutBuffer = new int32_t[mFrameCount * 2];
+ mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
// optmization: if mono to mono, alter input frame count as if we were inputing
// stereo samples
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7de6872..207f1eb 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -28,7 +28,8 @@ public:
MIXER, // Thread class is MixerThread
DIRECT, // Thread class is DirectOutputThread
DUPLICATING, // Thread class is DuplicatingThread
- RECORD // Thread class is RecordThread
+ RECORD, // Thread class is RecordThread
+ OFFLOAD // Thread class is OffloadThread
};
ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
@@ -125,10 +126,9 @@ public:
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.
- size_t frameCount() const { return mNormalFrameCount; }
- // Return's the HAL's frame count i.e. fast mixer buffer size.
- size_t frameCountHAL() const { return mFrameCount; }
+ // and returns the [normal mix] buffer's frame count.
+ virtual size_t frameCount() const = 0;
+ size_t frameSize() const { return mFrameSize; }
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
@@ -184,6 +184,8 @@ public:
void lockEffectChains_l(Vector< sp<EffectChain> >& effectChains);
// unlock effect chains after process
void unlockEffectChains(const Vector< sp<EffectChain> >& effectChains);
+ // get a copy of mEffectChains vector
+ Vector< sp<EffectChain> > getEffectChains_l() const { return mEffectChains; };
// set audio mode to all effect chains
void setMode(audio_mode_t mode);
// get effect module with corresponding ID on specified audio session
@@ -235,10 +237,13 @@ protected:
effect_uuid_t mType; // effect type UUID
};
- void acquireWakeLock();
- void acquireWakeLock_l();
+ void acquireWakeLock(int uid = -1);
+ void acquireWakeLock_l(int uid = -1);
void releaseWakeLock();
void releaseWakeLock_l();
+ void updateWakeLockUids(const SortedVector<int> &uids);
+ void updateWakeLockUids_l(const SortedVector<int> &uids);
+ void getPowerManager_l();
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
int sessionId);
@@ -249,6 +254,8 @@ protected:
// check if some effects must be suspended when an effect chain is added
void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
+ String16 getWakeLockTag();
+
virtual void preExit() { }
friend class AudioFlinger; // for mEffectChains
@@ -259,11 +266,13 @@ protected:
Condition mWaitWorkCV;
const sp<AudioFlinger> mAudioFlinger;
+
+ // updated by PlaybackThread::readOutputParameters() or
+ // RecordThread::readInputParameters()
uint32_t mSampleRate;
size_t mFrameCount; // output HAL, direct output, record
- size_t mNormalFrameCount; // normal mixer and effects
audio_channel_mask_t mChannelMask;
- uint16_t mChannelCount;
+ uint32_t mChannelCount;
size_t mFrameSize;
audio_format_t mFormat;
@@ -290,6 +299,7 @@ protected:
Vector<String8> mNewParameters;
status_t mParamStatus;
+ // vector owns each ConfigEvent *, so must delete after removing
Vector<ConfigEvent *> mConfigEvents;
// These fields are written and read by thread itself without lock or barrier,
@@ -328,11 +338,19 @@ public:
enum mixer_state {
MIXER_IDLE, // no active tracks
MIXER_TRACKS_ENABLED, // at least one active track, but no track has any data ready
- MIXER_TRACKS_READY // at least one active track, and at least one track has data
+ MIXER_TRACKS_READY, // at least one active track, and at least one track has data
+ MIXER_DRAIN_TRACK, // drain currently playing track
+ MIXER_DRAIN_ALL, // fully drain the hardware
// standby mode does not have an enum value
// suspend by audio policy manager is orthogonal to mixer state
};
+ // retry count before removing active track in case of underrun on offloaded thread:
+ // we need to make sure that AudioTrack client has enough time to send large buffers
+//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
+ // for offloaded tracks
+ static const int8_t kMaxTrackRetriesOffload = 20;
+
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, audio_devices_t device, type_t type);
virtual ~PlaybackThread();
@@ -350,8 +368,10 @@ protected:
// Code snippets that were lifted up out of threadLoop()
virtual void threadLoop_mix() = 0;
virtual void threadLoop_sleepTime() = 0;
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
+ virtual void threadLoop_drain();
virtual void threadLoop_standby();
+ virtual void threadLoop_exit();
virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
// prepareTracks_l reads and writes mActiveTracks, and returns
@@ -359,6 +379,19 @@ protected:
// is responsible for clearing or destroying this Vector later on, when it
// 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;
+ void removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
+
+ void writeCallback();
+ void resetWriteBlocked(uint32_t sequence);
+ void drainCallback();
+ void resetDraining(uint32_t sequence);
+
+ static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
// ThreadBase virtuals
virtual void preExit();
@@ -391,6 +424,7 @@ public:
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status);
AudioStreamOut* getOutput() const;
@@ -429,11 +463,23 @@ public:
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
+
+ // called with AudioFlinger lock held
void invalidateTracks(audio_stream_type_t streamType);
+ virtual size_t frameCount() const { return mNormalFrameCount; }
+
+ // Return's the HAL's frame count i.e. fast mixer buffer size.
+ size_t frameCountHAL() const { return mFrameCount; }
+
+ status_t getTimestamp_l(AudioTimestamp& timestamp);
protected:
- int16_t* mMixBuffer;
+ // updated by readOutputParameters()
+ size_t mNormalFrameCount; // normal mixer and effects
+
+ int16_t* mMixBuffer; // frame size aligned mix buffer
+ int8_t* mAllocMixBuffer; // mixer buffer allocation address
// 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
@@ -453,6 +499,9 @@ private:
void setMasterMute_l(bool muted) { mMasterMute = muted; }
protected:
SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
+ SortedVector<int> mWakeLockUids;
+ int mActiveTracksGeneration;
+ wp<Track> mLatestActiveTrack; // latest track added to mActiveTracks
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
@@ -486,8 +535,9 @@ private:
PlaybackThread& operator = (const PlaybackThread&);
status_t addTrack_l(const sp<Track>& track);
- void destroyTrack_l(const sp<Track>& track);
+ bool destroyTrack_l(const sp<Track>& track);
void removeTrack_l(const sp<Track>& track);
+ void broadcast_l();
void readOutputParameters();
@@ -535,6 +585,27 @@ private:
// DUPLICATING only
uint32_t writeFrames;
+ size_t mBytesRemaining;
+ size_t mCurrentWriteLength;
+ bool mUseAsyncWrite;
+ // mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
+ // incremented each time a write(), a flush() or a standby() occurs.
+ // Bit 0 is set when a write blocks and indicates a callback is expected.
+ // Bit 0 is reset by the async callback thread calling resetWriteBlocked(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mWriteAckSequence;
+ // mDrainSequence contains current drain sequence on bits 31-1. The drain sequence is
+ // incremented each time a drain is requested or a flush() or standby() occurs.
+ // Bit 0 is set when the drain() command is called at the HAL and indicates a callback is
+ // expected.
+ // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mDrainSequence;
+ // A condition that must be evaluated by prepareTrack_l() has changed and we must not wait
+ // for async write callback in the thread loop before evaluating it
+ bool mSignalPending;
+ sp<AsyncCallbackThread> mCallbackThread;
+
private:
// The HAL output sink is treated as non-blocking, but current implementation is blocking
sp<NBAIO_Sink> mOutputSink;
@@ -558,7 +629,18 @@ public:
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
+ virtual void flushOutput_l();
+private:
+ // timestamp latch:
+ // D input is written by threadLoop_write while mutex is unlocked, and read while locked
+ // Q output is written while locked, and read while locked
+ struct {
+ AudioTimestamp mTimestamp;
+ uint32_t mUnpresentedFrames;
+ } mLatchD, mLatchQ;
+ bool mLatchDValid; // true means mLatchD is valid, and clock it into latch at next opportunity
+ bool mLatchQValid; // true means mLatchQ is valid
};
class MixerThread : public PlaybackThread {
@@ -584,7 +666,7 @@ protected:
virtual void cacheParameters_l();
// threadLoop snippets
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
virtual void threadLoop_standby();
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
@@ -641,17 +723,81 @@ protected:
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
-private:
// volumes last sent to audio HAL with stream->set_volume()
float mLeftVolFloat;
float mRightVolFloat;
+ DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device, ThreadBase::type_t type);
+ void processVolume_l(Track *track, bool lastTrack);
+
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
sp<Track> mActiveTrack;
public:
virtual bool hasFastMixer() const { return false; }
};
+class OffloadThread : public DirectOutputThread {
+public:
+
+ OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device);
+ virtual ~OffloadThread() {};
+
+protected:
+ // threadLoop snippets
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
+ virtual void threadLoop_exit();
+ virtual void flushOutput_l();
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
+private:
+ void flushHw_l();
+
+private:
+ bool mHwPaused;
+ bool mFlushPending;
+ size_t mPausedWriteLength; // length in bytes of write interrupted by pause
+ size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
+ wp<Track> mPreviousTrack; // used to detect track switch
+};
+
+class AsyncCallbackThread : public Thread {
+public:
+
+ AsyncCallbackThread(const wp<PlaybackThread>& playbackThread);
+
+ virtual ~AsyncCallbackThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ void exit();
+ void setWriteBlocked(uint32_t sequence);
+ void resetWriteBlocked();
+ void setDraining(uint32_t sequence);
+ void resetDraining();
+
+private:
+ const wp<PlaybackThread> mPlaybackThread;
+ // mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
+ // setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetWriteBlocked()
+ uint32_t mWriteAckSequence;
+ // mDrainSequence corresponds to the last drain sequence passed by the offload thread via
+ // setDraining(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetDraining()
+ uint32_t mDrainSequence;
+ Condition mWaitWorkCV;
+ Mutex mLock;
+};
+
class DuplicatingThread : public MixerThread {
public:
DuplicatingThread(const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
@@ -671,7 +817,7 @@ protected:
// threadLoop snippets
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
- virtual void threadLoop_write();
+ virtual ssize_t threadLoop_write();
virtual void threadLoop_standby();
virtual void cacheParameters_l();
@@ -734,7 +880,8 @@ public:
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
- IAudioFlinger::track_flags_t flags,
+ int uid,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status);
@@ -744,7 +891,7 @@ public:
// 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);
+ bool stop(RecordTrack* recordTrack);
void dump(int fd, const Vector<String16>& args);
AudioStreamIn* clearInput();
@@ -775,6 +922,9 @@ public:
static void syncStartEventCallback(const wp<SyncEvent>& event);
void handleSyncStartEvent(const sp<SyncEvent>& event);
+ virtual size_t frameCount() const { return mFrameCount; }
+ bool hasFastRecorder() const { return false; }
+
private:
void clearSyncStartEvent();
@@ -790,11 +940,14 @@ private:
// is used together with mStartStopCond to indicate start()/stop() progress
sp<RecordTrack> mActiveTrack;
Condition mStartStopCond;
+
+ // updated by RecordThread::readInputParameters()
AudioResampler *mResampler;
+ // interleaved stereo pairs of fixed-point signed Q19.12
int32_t *mRsmpOutBuffer;
- int16_t *mRsmpInBuffer;
+ int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount]
size_t mRsmpInIndex;
- size_t mInputBytes;
+ size_t mBufferSize; // stream buffer size for read()
const uint32_t mReqChannelCount;
const uint32_t mReqSampleRate;
ssize_t mBytesRead;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index fac7071..cd201d9 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -25,10 +25,10 @@ class TrackBase : public ExtendedAudioBufferProvider, public RefBase {
public:
enum track_state {
IDLE,
- TERMINATED,
FLUSHED,
STOPPED,
- // next 2 states are currently used for fast tracks only
+ // next 2 states are currently used for fast tracks
+ // and offloaded tracks only
STOPPING_1, // waiting for first underrun
STOPPING_2, // waiting for presentation complete
RESUMING,
@@ -45,6 +45,7 @@ public:
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
bool isOut);
virtual ~TrackBase();
@@ -54,6 +55,7 @@ public:
sp<IMemory> getCblk() const { return mCblkMemory; }
audio_track_cblk_t* cblk() const { return mCblk; }
int sessionId() const { return mSessionId; }
+ int uid() const { return mUid; }
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
protected:
@@ -74,7 +76,7 @@ protected:
audio_channel_mask_t channelMask() const { return mChannelMask; }
- uint32_t sampleRate() const; // FIXME inline after cblk sr moved
+ virtual uint32_t sampleRate() const { return mSampleRate; }
// Return a pointer to the start of a contiguous slice of the track buffer.
// Parameter 'offset' is the requested start position, expressed in
@@ -89,7 +91,7 @@ protected:
return (mState == STOPPED || mState == FLUSHED);
}
- // for fast tracks only
+ // for fast tracks and offloaded tracks only
bool isStopping() const {
return mState == STOPPING_1 || mState == STOPPING_2;
}
@@ -101,11 +103,12 @@ protected:
}
bool isTerminated() const {
- return mState == TERMINATED;
+ return mTerminated;
}
- bool step(); // mStepCount is an implicit input
- void reset();
+ void terminate() {
+ mTerminated = true;
+ }
bool isOut() const { return mIsOut; }
// true for Track and TimedTrack, false for RecordTrack,
@@ -117,29 +120,26 @@ protected:
audio_track_cblk_t* mCblk;
void* mBuffer; // start of track buffer, typically in shared memory
// except for OutputTrack when it is in local memory
- void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
- // is based on mChannelCount and 16-bit samples
- uint32_t mStepCount; // saves AudioBufferProvider::Buffer::frameCount as of
- // time of releaseBuffer() for later use by step()
// we don't really need a lock for these
track_state mState;
const uint32_t mSampleRate; // initial sample rate only; for tracks which
// support dynamic rates, the current value is in control block
const audio_format_t mFormat;
const audio_channel_mask_t mChannelMask;
- const uint8_t mChannelCount;
+ const uint32_t mChannelCount;
const size_t mFrameSize; // AudioFlinger's view of frame size in shared memory,
// where for AudioTrack (but not AudioRecord),
// 8-bit PCM samples are stored as 16-bit
const size_t mFrameCount;// size of track buffer given at createTrack() or
// openRecord(), and then adjusted as needed
- bool mStepServerFailed;
const int mSessionId;
+ int mUid;
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
ServerProxy* mServerProxy;
const int mId;
sp<NBAIO_Sink> mTeeSink;
sp<NBAIO_Source> mTeeSource;
+ bool mTerminated;
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5ac3129..af04ce7 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -19,8 +19,8 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <math.h>
-#include <cutils/compiler.h>
#include <utils/Log.h>
#include <private/media/AudioTrackShared.h>
@@ -68,14 +68,13 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int clientUid,
bool isOut)
: RefBase(),
mThread(thread),
mClient(client),
mCblk(NULL),
// mBuffer
- // mBufferEnd
- mStepCount(0),
mState(IDLE),
mSampleRate(sampleRate),
mFormat(format),
@@ -84,12 +83,24 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
mFrameSize(audio_is_linear_pcm(format) ?
mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
mFrameCount(frameCount),
- mStepServerFailed(false),
mSessionId(sessionId),
mIsOut(isOut),
mServerProxy(NULL),
- mId(android_atomic_inc(&nextTrackId))
+ mId(android_atomic_inc(&nextTrackId)),
+ mTerminated(false)
{
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
+ int newclientUid = IPCThreadState::self()->getCallingUid();
+ if (clientUid != -1 && clientUid != newclientUid) {
+ ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);
+ }
+ clientUid = newclientUid;
+ }
+ // clientUid contains the uid of the app that is responsible for this track, so we can blame
+ // battery usage on it.
+ mUid = clientUid;
+
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -98,7 +109,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
// ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
size_t size = sizeof(audio_track_cblk_t);
- size_t bufferSize = frameCount * mFrameSize;
+ size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
if (sharedBuffer == 0) {
size += bufferSize;
}
@@ -124,22 +135,15 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
new(mCblk) audio_track_cblk_t();
// clear all buffers
mCblk->frameCount_ = frameCount;
-// uncomment the following lines to quickly test 32-bit wraparound
-// mCblk->user = 0xffff0000;
-// mCblk->server = 0xffff0000;
-// mCblk->userBase = 0xffff0000;
-// mCblk->serverBase = 0xffff0000;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
memset(mBuffer, 0, bufferSize);
- // Force underrun condition to avoid false underrun callback until first data is
- // written to buffer (other flags are cleared)
- mCblk->flags = CBLK_UNDERRUN;
} else {
mBuffer = sharedBuffer->pointer();
+#if 0
+ mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
+#endif
}
- mBufferEnd = (uint8_t *)mBuffer + bufferSize;
- mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
#ifdef TEE_SINK
if (mTeeSinkTrackEnabled) {
@@ -199,51 +203,12 @@ void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buf
}
#endif
- buffer->raw = NULL;
- mStepCount = buffer->frameCount;
- // FIXME See note at getNextBuffer()
- (void) step(); // ignore return value of step()
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ buf.mRaw = buffer->raw;
buffer->frameCount = 0;
-}
-
-bool AudioFlinger::ThreadBase::TrackBase::step() {
- bool result = mServerProxy->step(mStepCount);
- if (!result) {
- ALOGV("stepServer failed acquiring cblk mutex");
- mStepServerFailed = true;
- }
- return result;
-}
-
-void AudioFlinger::ThreadBase::TrackBase::reset() {
- audio_track_cblk_t* cblk = this->cblk();
-
- cblk->user = 0;
- cblk->server = 0;
- cblk->userBase = 0;
- cblk->serverBase = 0;
- mStepServerFailed = false;
- ALOGV("TrackBase::reset");
-}
-
-uint32_t AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
- return mServerProxy->getSampleRate();
-}
-
-void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
- audio_track_cblk_t* cblk = this->cblk();
- int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase) * mFrameSize;
- int8_t *bufferEnd = bufferStart + frames * mFrameSize;
-
- // Check validity of returned pointer in case the track control block would have been corrupted.
- ALOG_ASSERT(!(bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd),
- "TrackBase::getBuffer buffer out of range:\n"
- " start: %p, end %p , mBuffer %p mBufferEnd %p\n"
- " server %u, serverBase %u, user %u, userBase %u, frameSize %u",
- bufferStart, bufferEnd, mBuffer, mBufferEnd,
- cblk->server, cblk->serverBase, cblk->user, cblk->userBase, mFrameSize);
-
- return bufferStart;
+ buffer->raw = NULL;
+ mServerProxy->releaseBuffer(&buf);
}
status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event)
@@ -327,6 +292,21 @@ status_t AudioFlinger::TrackHandle::setMediaTimeTransform(
xform, static_cast<TimedAudioTrack::TargetTimeline>(target));
}
+status_t AudioFlinger::TrackHandle::setParameters(const String8& keyValuePairs) {
+ return mTrack->setParameters(keyValuePairs);
+}
+
+status_t AudioFlinger::TrackHandle::getTimestamp(AudioTimestamp& timestamp)
+{
+ return mTrack->getTimestamp(timestamp);
+}
+
+
+void AudioFlinger::TrackHandle::signal()
+{
+ return mTrack->signal();
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -346,9 +326,10 @@ AudioFlinger::PlaybackThread::Track::Track(
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId, true /*isOut*/),
+ sessionId, uid, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -360,20 +341,29 @@ AudioFlinger::PlaybackThread::Track::Track(
mPresentationCompleteFrames(0),
mFlags(flags),
mFastIndex(-1),
- mUnderrunCount(0),
mCachedVolume(1.0),
- mIsInvalid(false)
+ mIsInvalid(false),
+ mAudioTrackServerProxy(NULL),
+ mResumeToStopping(false)
{
if (mCblk != NULL) {
+ if (sharedBuffer == 0) {
+ mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ } else {
+ mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ }
+ mServerProxy = mAudioTrackServerProxy;
// to avoid leaking a track name, do not allocate one unless there is an mCblk
mName = thread->getTrackName_l(channelMask, sessionId);
- mCblk->mName = mName;
if (mName < 0) {
ALOGE("no more track names available");
return;
}
// only allocate a fast track index if we were able to allocate a normal track name
if (flags & IAudioFlinger::TRACK_FAST) {
+ mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
int i = __builtin_ctz(thread->mFastTrackAvailMask);
ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
@@ -382,7 +372,6 @@ AudioFlinger::PlaybackThread::Track::Track(
// this means we are potentially denying other more important fast tracks from
// being created. It would be better to allocate the index dynamically.
mFastIndex = i;
- mCblk->mName = i;
// Read the initial underruns because this field is never cleared by the fast mixer
mObservedUnderruns = thread->getFastTrackUnderruns(i);
thread->mFastTrackAvailMask &= ~(1 << i);
@@ -395,6 +384,16 @@ AudioFlinger::PlaybackThread::Track::Track(
AudioFlinger::PlaybackThread::Track::~Track()
{
ALOGV("PlaybackThread::Track destructor");
+
+ // The destructor would clear mSharedBuffer,
+ // but it will not push the decremented reference count,
+ // leaving the client's IMemory dangling indefinitely.
+ // This prevents that leak.
+ if (mSharedBuffer != 0) {
+ mSharedBuffer.clear();
+ // flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ }
}
void AudioFlinger::PlaybackThread::Track::destroy()
@@ -411,33 +410,25 @@ void AudioFlinger::PlaybackThread::Track::destroy()
{ // scope for mLock
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- if (!isOutputTrack()) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
- AudioSystem::releaseOutput(thread->id());
- }
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->destroyTrack_l(this);
+ bool wasActive = playbackThread->destroyTrack_l(this);
+ if (!isOutputTrack() && !wasActive) {
+ AudioSystem::releaseOutput(thread->id());
+ }
}
}
}
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
- result.append(" Name Client Type Fmt Chn mask Session StpCnt fCount S F SRate "
- "L dB R dB Server User Main buf Aux Buf Flags Underruns\n");
+ result.append(" Name Client Type Fmt Chn mask Session fCount S F SRate "
+ "L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n");
}
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
- uint32_t vlr = mServerProxy->getVolumeLR();
+ uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
if (isFastTrack()) {
sprintf(buffer, " F %2d", mFastIndex);
} else {
@@ -445,40 +436,41 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
}
track_state state = mState;
char stateChar;
- switch (state) {
- case IDLE:
- stateChar = 'I';
- break;
- case TERMINATED:
+ if (isTerminated()) {
stateChar = 'T';
- break;
- case STOPPING_1:
- stateChar = 's';
- break;
- case STOPPING_2:
- stateChar = '5';
- break;
- case STOPPED:
- stateChar = 'S';
- break;
- case RESUMING:
- stateChar = 'R';
- break;
- case ACTIVE:
- stateChar = 'A';
- break;
- case PAUSING:
- stateChar = 'p';
- break;
- case PAUSED:
- stateChar = 'P';
- break;
- case FLUSHED:
- stateChar = 'F';
- break;
- default:
- stateChar = '?';
- break;
+ } else {
+ switch (state) {
+ case IDLE:
+ stateChar = 'I';
+ break;
+ case STOPPING_1:
+ stateChar = 's';
+ break;
+ case STOPPING_2:
+ stateChar = '5';
+ break;
+ case STOPPED:
+ stateChar = 'S';
+ break;
+ case RESUMING:
+ stateChar = 'R';
+ break;
+ case ACTIVE:
+ stateChar = 'A';
+ break;
+ case PAUSING:
+ stateChar = 'p';
+ break;
+ case PAUSED:
+ stateChar = 'P';
+ break;
+ case FLUSHED:
+ stateChar = 'F';
+ break;
+ default:
+ stateChar = '?';
+ break;
+ }
}
char nowInUnderrun;
switch (mObservedUnderruns.mBitFields.mMostRecent) {
@@ -495,77 +487,50 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
nowInUnderrun = '?';
break;
}
- snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %5u %5.2g %5.2g "
- "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
+ snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6u %1c %1d %5u %5.2g %5.2g "
+ "%08X %08X %08X 0x%03X %9u%c\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
mFormat,
mChannelMask,
mSessionId,
- mStepCount,
mFrameCount,
stateChar,
mFillingUpStatus,
- mServerProxy->getSampleRate(),
+ mAudioTrackServerProxy->getSampleRate(),
20.0 * log10((vlr & 0xFFFF) / 4096.0),
20.0 * log10((vlr >> 16) / 4096.0),
- mCblk->server,
- mCblk->user,
+ mCblk->mServer,
(int)mMainBuffer,
(int)mAuxBuffer,
- mCblk->flags,
- mUnderrunCount,
+ mCblk->mFlags,
+ mAudioTrackServerProxy->getUnderrunFrames(),
nowInUnderrun);
}
+uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
+ return mAudioTrackServerProxy->getSampleRate();
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
AudioBufferProvider::Buffer* buffer, int64_t pts)
{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesReady;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- // FIXME When called by fast mixer, this takes a mutex with tryLock().
- // Since the fast mixer is higher priority than client callback thread,
- // it does not result in priority inversion for client.
- // But a non-blocking solution would be preferable to avoid
- // fast mixer being unable to tryLock(), and
- // to avoid the extra context switches if the client wakes up,
- // discovers the mutex is locked, then has to wait for fast mixer to unlock.
- if (!step()) goto getNextBuffer_exit;
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
+ ServerProxy::Buffer buf;
+ size_t desiredFrames = buffer->frameCount;
+ buf.mFrameCount = desiredFrames;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
}
+ return status;
+}
- // FIXME Same as above
- framesReady = mServerProxy->framesReady();
-
- if (CC_LIKELY(framesReady)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
- bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
-
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
- }
+// releaseBuffer() is not overridden
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- ALOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
- return NOT_ENOUGH_DATA;
-}
+// ExtendedAudioBufferProvider interface
// Note that framesReady() takes a mutex on the control block using tryLock().
// This could result in priority inversion if framesReady() is called by the normal mixer,
@@ -576,7 +541,12 @@ getNextBuffer_exit:
// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
- return mServerProxy->framesReady();
+ return mAudioTrackServerProxy->framesReady();
+}
+
+size_t AudioFlinger::PlaybackThread::Track::framesReleased() const
+{
+ return mAudioTrackServerProxy->framesReleased();
}
// Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -586,9 +556,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
}
if (framesReady() >= mFrameCount ||
- (mCblk->flags & CBLK_FORCEREADY)) {
+ (mCblk->mFlags & CBLK_FORCEREADY)) {
mFillingUpStatus = FS_FILLED;
- android_atomic_and(~CBLK_FORCEREADY, &mCblk->flags);
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
return true;
}
return false;
@@ -603,36 +573,56 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
+ if (isOffloaded()) {
+ Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
+ Mutex::Autolock _lth(thread->mLock);
+ sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
+ if (thread->mAudioFlinger->isNonOffloadableGlobalEffectEnabled_l() ||
+ (ec != 0 && ec->isNonOffloadableEnabled())) {
+ invalidate();
+ return PERMISSION_DENIED;
+ }
+ }
+ Mutex::Autolock _lth(thread->mLock);
track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
+
if (state == PAUSED) {
- mState = TrackBase::RESUMING;
- ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ if (mResumeToStopping) {
+ // happened we need to resume to STOPPING_1
+ mState = TrackBase::STOPPING_1;
+ ALOGV("PAUSED => STOPPING_1 (%d) on thread %p", mName, this);
+ } else {
+ mState = TrackBase::RESUMING;
+ ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ }
} else {
mState = TrackBase::ACTIVE;
ALOGV("? => ACTIVE (%d) on thread %p", mName, this);
}
- if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
- thread->mLock.unlock();
- status = AudioSystem::startOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- if (status == NO_ERROR) {
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ status = playbackThread->addTrack_l(this);
+ if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
+ triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ // restore previous state if start was rejected by policy manager
+ if (status == PERMISSION_DENIED) {
+ mState = state;
}
-#endif
}
- if (status == NO_ERROR) {
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->addTrack_l(this);
+ // track was already in the active list, not a problem
+ if (status == ALREADY_EXISTS) {
+ status = NO_ERROR;
} else {
- mState = state;
- triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ // Acknowledge any pending flush(), so that subsequent new data isn't discarded.
+ // It is usually unsafe to access the server proxy from a binder thread.
+ // But in this case we know the mixer thread (whether normal mixer or fast mixer)
+ // isn't looking at this track yet: we still hold the normal mixer thread lock,
+ // and for fast tracks the track is not yet in the fast mixer thread's active set.
+ ServerProxy::Buffer buffer;
+ buffer.mFrameCount = 1;
+ (void) mAudioTrackServerProxy->obtainBuffer(&buffer, true /*ackFlush*/);
}
} else {
status = BAD_VALUE;
@@ -653,26 +643,18 @@ void AudioFlinger::PlaybackThread::Track::stop()
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
reset();
mState = STOPPED;
- } else if (!isFastTrack()) {
+ } else if (!isFastTrack() && !isOffloaded()) {
mState = STOPPED;
} else {
- // prepareTracks_l() will set state to STOPPING_2 after next underrun,
- // and then to STOPPED and reset() when presentation is complete
+ // For fast tracks prepareTracks_l() will set state to STOPPING_2
+ // presentation is complete
+ // For an offloaded track this starts a drain and state will
+ // move to STOPPING_2 when drain completes and then STOPPED
mState = STOPPING_1;
}
ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName,
playbackThread);
}
- if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
}
}
@@ -682,19 +664,27 @@ void AudioFlinger::PlaybackThread::Track::pause()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- if (mState == ACTIVE || mState == RESUMING) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ switch (mState) {
+ case STOPPING_1:
+ case STOPPING_2:
+ if (!isOffloaded()) {
+ /* nothing to do if track is not offloaded */
+ break;
+ }
+
+ // Offloaded track was draining, we need to carry on draining when resumed
+ mResumeToStopping = true;
+ // fall through...
+ case ACTIVE:
+ case RESUMING:
mState = PAUSING;
ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
- if (!isOutputTrack()) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
+ playbackThread->broadcast_l();
+ break;
+
+ default:
+ break;
}
}
}
@@ -705,21 +695,52 @@ void AudioFlinger::PlaybackThread::Track::flush()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
- mState != PAUSING && mState != IDLE && mState != FLUSHED) {
- return;
- }
- // No point remaining in PAUSED state after a flush => go to
- // FLUSHED state
- mState = FLUSHED;
- // do not reset the track if it is still in the process of being stopped or paused.
- // this will be done by prepareTracks_l() when the track is stopped.
- // prepareTracks_l() will see mState == FLUSHED, then
- // remove from active track list, reset(), and trigger presentation complete
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+
+ if (isOffloaded()) {
+ // If offloaded we allow flush during any state except terminated
+ // and keep the track active to avoid problems if user is seeking
+ // rapidly and underlying hardware has a significant delay handling
+ // a pause
+ if (isTerminated()) {
+ return;
+ }
+
+ ALOGV("flush: offload flush");
reset();
+
+ if (mState == STOPPING_1 || mState == STOPPING_2) {
+ ALOGV("flushed in STOPPING_1 or 2 state, change state to ACTIVE");
+ mState = ACTIVE;
+ }
+
+ if (mState == ACTIVE) {
+ ALOGV("flush called in active state, resetting buffer time out retry count");
+ mRetryCount = PlaybackThread::kMaxTrackRetriesOffload;
+ }
+
+ mResumeToStopping = false;
+ } else {
+ if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED &&
+ mState != PAUSED && mState != PAUSING && mState != IDLE && mState != FLUSHED) {
+ return;
+ }
+ // No point remaining in PAUSED state after a flush => go to
+ // FLUSHED state
+ mState = FLUSHED;
+ // do not reset the track if it is still in the process of being stopped or paused.
+ // this will be done by prepareTracks_l() when the track is stopped.
+ // prepareTracks_l() will see mState == FLUSHED, then
+ // remove from active track list, reset(), and trigger presentation complete
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
}
+ // Prevent flush being lost if the track is flushed and then resumed
+ // before mixer thread can run. This is important when offloading
+ // because the hardware buffer could hold a large amount of audio
+ playbackThread->flushOutput_l();
+ playbackThread->broadcast_l();
}
}
@@ -728,11 +749,9 @@ void AudioFlinger::PlaybackThread::Track::reset()
// Do not reset twice to avoid discarding data written just after a flush and before
// the audioflinger thread detects the track is stopped.
if (!mResetDone) {
- TrackBase::reset();
// Force underrun condition to avoid false underrun callback until first data is
// written to buffer
- android_atomic_and(~CBLK_FORCEREADY, &mCblk->flags);
- android_atomic_or(CBLK_UNDERRUN, &mCblk->flags);
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
mFillingUpStatus = FS_FILLING;
mResetDone = true;
if (mState == FLUSHED) {
@@ -741,6 +760,51 @@ void AudioFlinger::PlaybackThread::Track::reset()
}
}
+status_t AudioFlinger::PlaybackThread::Track::setParameters(const String8& keyValuePairs)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ ALOGE("thread is dead");
+ return FAILED_TRANSACTION;
+ } else if ((thread->type() == ThreadBase::DIRECT) ||
+ (thread->type() == ThreadBase::OFFLOAD)) {
+ return thread->setParameters(keyValuePairs);
+ } else {
+ return PERMISSION_DENIED;
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
+{
+ // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
+ if (isFastTrack()) {
+ return INVALID_OPERATION;
+ }
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return INVALID_OPERATION;
+ }
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (!isOffloaded()) {
+ if (!playbackThread->mLatchQValid) {
+ return INVALID_OPERATION;
+ }
+ uint32_t unpresentedFrames =
+ ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
+ playbackThread->mSampleRate;
+ uint32_t framesWritten = mAudioTrackServerProxy->framesReleased();
+ if (framesWritten < unpresentedFrames) {
+ return INVALID_OPERATION;
+ }
+ timestamp.mPosition = framesWritten - unpresentedFrames;
+ timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
+ return NO_ERROR;
+ }
+
+ return playbackThread->getTimestamp_l(timestamp);
+}
+
status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
{
status_t status = DEAD_OBJECT;
@@ -766,7 +830,11 @@ status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
return INVALID_OPERATION;
}
srcThread->removeEffect_l(effect);
- playbackThread->addEffect_l(effect);
+ status = playbackThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ srcThread->addEffect_l(effect);
+ return INVALID_OPERATION;
+ }
// removeEffect_l() has stopped the effect if it was active so it must be restarted
if (effect->state() == EffectModule::ACTIVE ||
effect->state() == EffectModule::STOPPING) {
@@ -784,6 +852,7 @@ status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
dstChain->strategy(),
AUDIO_SESSION_OUTPUT_MIX,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
status = playbackThread->attachAuxEffect(this, EffectId);
}
@@ -802,15 +871,23 @@ bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWrit
// a track is considered presented when the total number of frames written to audio HAL
// corresponds to the number of frames written when presentationComplete() is called for the
// first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time.
+ // For an offloaded track the HAL+h/w delay is variable so a HAL drain() is used
+ // to detect when all frames have been played. In this case framesWritten isn't
+ // useful because it doesn't always reflect whether there is data in the h/w
+ // buffers, particularly if a track has been paused and resumed during draining
+ ALOGV("presentationComplete() mPresentationCompleteFrames %d framesWritten %d",
+ mPresentationCompleteFrames, framesWritten);
if (mPresentationCompleteFrames == 0) {
mPresentationCompleteFrames = framesWritten + audioHalFrames;
ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d",
mPresentationCompleteFrames, audioHalFrames);
}
- if (framesWritten >= mPresentationCompleteFrames) {
+
+ if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) {
ALOGV("presentationComplete() session %d complete: framesWritten %d",
mSessionId, framesWritten);
triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ mAudioTrackServerProxy->setStreamEndDone();
return true;
}
return false;
@@ -833,7 +910,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
{
// called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
- uint32_t vlr = mServerProxy->getVolumeLR();
+ uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
uint32_t vl = vlr & 0xFFFF;
uint32_t vr = vlr >> 16;
// track volumes come from shared memory, so can't be trusted and must be clamped
@@ -856,7 +933,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event)
{
- if (mState == TERMINATED || mState == PAUSED ||
+ if (isTerminated() || mState == PAUSED ||
((framesReady() == 0) && ((mSharedBuffer != 0) ||
(mState == STOPPED)))) {
ALOGW("Track::setSyncEvent() in invalid state %d on session %d %s mode, framesReady %d ",
@@ -870,12 +947,25 @@ status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>&
void AudioFlinger::PlaybackThread::Track::invalidate()
{
- // FIXME should use proxy
- android_atomic_or(CBLK_INVALID, &mCblk->flags);
- mCblk->cv.signal();
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
mIsInvalid = true;
}
+void AudioFlinger::PlaybackThread::Track::signal()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+}
+
// ----------------------------------------------------------------------------
sp<AudioFlinger::PlaybackThread::TimedTrack>
@@ -888,13 +978,14 @@ AudioFlinger::PlaybackThread::TimedTrack::create(
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId) {
+ int sessionId,
+ int uid) {
if (!client->reserveTimedTrack())
return 0;
return new TimedTrack(
thread, client, streamType, sampleRate, format, channelMask, frameCount,
- sharedBuffer, sessionId);
+ sharedBuffer, sessionId, uid);
}
AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
@@ -906,9 +997,10 @@ AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId)
+ int sessionId,
+ int uid)
: Track(thread, client, streamType, sampleRate, format, channelMask,
- frameCount, sharedBuffer, sessionId, IAudioFlinger::TRACK_TIMED),
+ frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED),
mQueueHeadInFlight(false),
mTrimQueueHeadOnRelease(false),
mFramesPendingInQueue(0),
@@ -1185,10 +1277,12 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
}
}
+ uint32_t sr = sampleRate();
+
// adjust the head buffer's PTS to reflect the portion of the head buffer
// that has already been consumed
int64_t effectivePTS = headLocalPTS +
- ((head.position() / mFrameSize) * mLocalTimeFreq / sampleRate());
+ ((head.position() / mFrameSize) * mLocalTimeFreq / sr);
// Calculate the delta in samples between the head of the input buffer
// queue and the start of the next output buffer that will be written.
@@ -1220,7 +1314,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
// the current output position is within this threshold, then we will
// concatenate the next input samples to the previous output
const int64_t kSampleContinuityThreshold =
- (static_cast<int64_t>(sampleRate()) << 32) / 250;
+ (static_cast<int64_t>(sr) << 32) / 250;
// if this is the first buffer of audio that we're emitting from this track
// then it should be almost exactly on time.
@@ -1399,9 +1493,10 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount)
+ size_t frameCount,
+ int uid)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
- NULL, 0, IAudioFlinger::TRACK_DEFAULT),
+ NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT),
mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
@@ -1409,15 +1504,17 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
mOutBuffer.frameCount = 0;
playbackThread->mTracks.add(this);
ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
- "mCblk->frameCount_ %u, mChannelMask 0x%08x mBufferEnd %p",
+ "mCblk->frameCount_ %u, mChannelMask 0x%08x",
mCblk, mBuffer,
- mCblk->frameCount_, mChannelMask, mBufferEnd);
+ mCblk->frameCount_, mChannelMask);
// since client and server are in the same process,
// the buffer has the same virtual address on both sides
mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000));
mClientProxy->setSendLevel(0.0);
mClientProxy->setSampleRate(sampleRate);
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
+ true /*clientInServer*/);
} else {
ALOGW("Error creating output track on thread %p", playbackThread);
}
@@ -1477,7 +1574,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
} else {
- ALOGW ("OutputTrack::write() %p no more buffers in queue", this);
+ ALOGW("OutputTrack::write() %p no more buffers in queue", this);
}
}
}
@@ -1498,9 +1595,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
if (mOutBuffer.frameCount == 0) {
mOutBuffer.frameCount = pInBuffer->frameCount;
nsecs_t startTime = systemTime();
- if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)NO_MORE_BUFFERS) {
- ALOGV ("OutputTrack::write() %p thread %p no more output buffers", this,
- mThread.unsafe_get());
+ status_t status = obtainBuffer(&mOutBuffer, waitTimeLeftMs);
+ if (status != NO_ERROR) {
+ ALOGV("OutputTrack::write() %p thread %p no more output buffers; status %d", this,
+ mThread.unsafe_get(), status);
outputBufferFull = true;
break;
}
@@ -1515,7 +1613,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
pInBuffer->frameCount;
memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
- mClientProxy->stepUser(outFrames);
+ Proxy::Buffer buf;
+ buf.mFrameCount = outFrames;
+ buf.mRaw = NULL;
+ mClientProxy->releaseBuffer(&buf);
pInBuffer->frameCount -= outFrames;
pInBuffer->i16 += outFrames * channelCount;
mOutBuffer.frameCount -= outFrames;
@@ -1559,8 +1660,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
// If no more buffers are pending, fill output track buffer to make sure it is started
// by output mixer.
if (frames == 0 && mBufferQueue.size() == 0) {
- if (mCblk->user < mFrameCount) {
- frames = mFrameCount - mCblk->user;
+ // FIXME borken, replace by getting framesReady() from proxy
+ size_t user = 0; // was mCblk->user
+ if (user < mFrameCount) {
+ frames = mFrameCount - user;
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[frames * channelCount];
pInBuffer->frameCount = frames;
@@ -1578,46 +1681,17 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
{
- audio_track_cblk_t* cblk = mCblk;
- uint32_t framesReq = buffer->frameCount;
-
- ALOGVV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
- buffer->frameCount = 0;
-
- size_t framesAvail;
- {
- Mutex::Autolock _l(cblk->lock);
-
- // read the server count again
- while (!(framesAvail = mClientProxy->framesAvailable_l())) {
- if (CC_UNLIKELY(!mActive)) {
- ALOGV("Not active and NO_MORE_BUFFERS");
- return NO_MORE_BUFFERS;
- }
- status_t result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- if (result != NO_ERROR) {
- return NO_MORE_BUFFERS;
- }
- }
- }
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
-
- uint32_t u = cblk->user;
- uint32_t bufferEnd = cblk->userBase + mFrameCount;
-
- if (framesReq > bufferEnd - u) {
- framesReq = bufferEnd - u;
- }
-
- buffer->frameCount = framesReq;
- buffer->raw = mClientProxy->buffer(u);
- return NO_ERROR;
+ ClientProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ struct timespec timeout;
+ timeout.tv_sec = waitTimeMs / 1000;
+ timeout.tv_nsec = (int) (waitTimeMs % 1000) * 1000000;
+ status_t status = mClientProxy->obtainBuffer(&buf, &timeout);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ return status;
}
-
void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
{
size_t size = mBufferQueue.size();
@@ -1682,12 +1756,18 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
- int sessionId)
+ int sessionId,
+ int uid)
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
mOverflow(false)
{
- ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
+ ALOGV("RecordTrack constructor");
+ if (mCblk != NULL) {
+ mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ mServerProxy = mAudioRecordServerProxy;
+ }
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1699,42 +1779,16 @@ AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
int64_t pts)
{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesAvail;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- if (!step()) {
- goto getNextBuffer_exit;
- }
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
- }
-
- // FIXME lock is not actually held, so overrun is possible
- framesAvail = mServerProxy->framesAvailableIn_l();
-
- if (CC_LIKELY(framesAvail)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
-
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ // FIXME also wake futex so that overrun is noticed more quickly
+ (void) android_atomic_or(CBLK_OVERRUN, &mCblk->mFlags);
}
-
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
+ return status;
}
status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
@@ -1754,16 +1808,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
- 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, &mCblk->flags);
- }
- recordThread->mLock.unlock();
- if (doStop) {
+ if (recordThread->stop(this)) {
AudioSystem::stopInput(recordThread->id());
}
}
@@ -1787,23 +1832,31 @@ void AudioFlinger::RecordThread::RecordTrack::destroy()
}
}
+void AudioFlinger::RecordThread::RecordTrack::invalidate()
+{
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+}
+
/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
{
- result.append(" Clien Fmt Chn mask Session Step S Serv User FrameCount\n");
+ result.append("Client Fmt Chn mask Session S Server fCount\n");
}
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %08x %08x %05d\n",
+ snprintf(buffer, size, "%6u %3u %08X %7u %1d %08X %6u\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mFormat,
mChannelMask,
mSessionId,
- mStepCount,
mState,
- mCblk->server,
- mCblk->user,
+ mCblk->mServer,
mFrameCount);
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 83d9ccd..51ba698 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -8,30 +8,34 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
CameraService.cpp \
- CameraClient.cpp \
- Camera2Client.cpp \
- ProCamera2Client.cpp \
- Camera2ClientBase.cpp \
- CameraDeviceBase.cpp \
- Camera2Device.cpp \
- Camera3Device.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 \
- camera2/ProFrameProcessor.cpp \
- camera2/ZslProcessor3.cpp \
- camera3/Camera3Stream.cpp \
- camera3/Camera3IOStreamBase.cpp \
- camera3/Camera3InputStream.cpp \
- camera3/Camera3OutputStream.cpp \
- camera3/Camera3ZslStream.cpp \
+ CameraDeviceFactory.cpp \
+ common/Camera2ClientBase.cpp \
+ common/CameraDeviceBase.cpp \
+ common/FrameProcessorBase.cpp \
+ api1/CameraClient.cpp \
+ api1/Camera2Client.cpp \
+ api1/client2/Parameters.cpp \
+ api1/client2/FrameProcessor.cpp \
+ api1/client2/StreamingProcessor.cpp \
+ api1/client2/JpegProcessor.cpp \
+ api1/client2/CallbackProcessor.cpp \
+ api1/client2/ZslProcessor.cpp \
+ api1/client2/BurstCapture.cpp \
+ api1/client2/JpegCompressor.cpp \
+ api1/client2/CaptureSequencer.cpp \
+ api1/client2/ZslProcessor3.cpp \
+ api2/CameraDeviceClient.cpp \
+ api_pro/ProCamera2Client.cpp \
+ device2/Camera2Device.cpp \
+ device3/Camera3Device.cpp \
+ device3/Camera3Stream.cpp \
+ device3/Camera3IOStreamBase.cpp \
+ device3/Camera3InputStream.cpp \
+ device3/Camera3OutputStream.cpp \
+ device3/Camera3ZslStream.cpp \
+ device3/StatusTracker.cpp \
gui/RingBufferConsumer.cpp \
+ utils/CameraTraces.cpp \
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp
new file mode 100644
index 0000000..7fdf304
--- /dev/null
+++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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 "CameraDeviceFactory"
+#include <utils/Log.h>
+
+#include "CameraService.h"
+#include "CameraDeviceFactory.h"
+#include "common/CameraDeviceBase.h"
+#include "device2/Camera2Device.h"
+#include "device3/Camera3Device.h"
+
+namespace android {
+
+wp<CameraService> CameraDeviceFactory::sService;
+
+sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) {
+
+ sp<CameraService> svc = sService.promote();
+ if (svc == 0) {
+ ALOGE("%s: No service registered", __FUNCTION__);
+ return NULL;
+ }
+
+ int deviceVersion = svc->getDeviceVersion(cameraId, /*facing*/NULL);
+
+ sp<CameraDeviceBase> device;
+
+ switch (deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ device = new Camera2Device(cameraId);
+ break;
+ case CAMERA_DEVICE_API_VERSION_3_0:
+ device = new Camera3Device(cameraId);
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unknown HAL device version %d",
+ __FUNCTION__, cameraId, deviceVersion);
+ device = NULL;
+ break;
+ }
+
+ ALOGV_IF(device != 0, "Created a new camera device for version %d",
+ deviceVersion);
+
+ return device;
+}
+
+void CameraDeviceFactory::registerService(wp<CameraService> service) {
+ ALOGV("%s: Registered service %p", __FUNCTION__,
+ service.promote().get());
+
+ sService = service;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/CameraDeviceFactory.h b/services/camera/libcameraservice/CameraDeviceFactory.h
new file mode 100644
index 0000000..236dc56
--- /dev/null
+++ b/services/camera/libcameraservice/CameraDeviceFactory.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 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_CAMERADEVICEFACTORY_H
+#define ANDROID_SERVERS_CAMERA_CAMERADEVICEFACTORY_H
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class CameraDeviceBase;
+class CameraService;
+
+/**
+ * Create the right instance of Camera2Device or Camera3Device
+ * automatically based on the device version.
+ */
+class CameraDeviceFactory : public virtual RefBase {
+ public:
+ static void registerService(wp<CameraService> service);
+
+ // Prerequisite: Call registerService.
+ static sp<CameraDeviceBase> createDevice(int cameraId);
+ private:
+ CameraDeviceFactory(wp<CameraService> service);
+
+ static wp<CameraService> sService;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 757a781..eeedfc9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -38,9 +38,12 @@
#include <utils/String16.h>
#include "CameraService.h"
-#include "CameraClient.h"
-#include "Camera2Client.h"
-#include "ProCamera2Client.h"
+#include "api1/CameraClient.h"
+#include "api1/Camera2Client.h"
+#include "api_pro/ProCamera2Client.h"
+#include "api2/CameraDeviceClient.h"
+#include "utils/CameraTraces.h"
+#include "CameraDeviceFactory.h"
namespace android {
@@ -126,6 +129,8 @@ void CameraService::onFirstRef()
CAMERA_MODULE_API_VERSION_2_1) {
mModule->set_callbacks(this);
}
+
+ CameraDeviceFactory::registerService(this);
}
}
@@ -164,7 +169,7 @@ void CameraService::onDeviceStatusChanged(int cameraId,
Mutex::Autolock al(mServiceLock);
/* Find all clients that we need to disconnect */
- sp<Client> client = mClient[cameraId].promote();
+ sp<BasicClient> client = mClient[cameraId].promote();
if (client.get() != NULL) {
clientsToDisconnect.push_back(client);
}
@@ -207,7 +212,7 @@ int32_t CameraService::getNumberOfCameras() {
status_t CameraService::getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) {
if (!mModule) {
- return NO_INIT;
+ return -ENODEV;
}
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
@@ -221,6 +226,49 @@ status_t CameraService::getCameraInfo(int cameraId,
return rc;
}
+status_t CameraService::getCameraCharacteristics(int cameraId,
+ CameraMetadata* cameraInfo) {
+ if (!cameraInfo) {
+ ALOGE("%s: cameraInfo is NULL", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (!mModule) {
+ ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0) {
+ // TODO: Remove this check once HAL1 shim is in place.
+ ALOGE("%s: Only HAL module version V2 or higher supports static metadata", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+ ALOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
+ return BAD_VALUE;
+ }
+
+ int facing;
+ if (getDeviceVersion(cameraId, &facing) == CAMERA_DEVICE_API_VERSION_1_0) {
+ // TODO: Remove this check once HAL1 shim is in place.
+ ALOGE("%s: HAL1 doesn't support static metadata yet", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1) {
+ // Disable HAL2.x support for camera2 API for now.
+ ALOGW("%s: HAL2.x doesn't support getCameraCharacteristics for now", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ struct camera_info info;
+ status_t ret = mModule->get_camera_info(cameraId, &info);
+ *cameraInfo = info.static_camera_characteristics;
+
+ return ret;
+}
+
int CameraService::getDeviceVersion(int cameraId, int* facing) {
struct camera_info info;
if (mModule->get_camera_info(cameraId, &info) != OK) {
@@ -258,7 +306,7 @@ bool CameraService::isValidCameraId(int cameraId) {
return false;
}
-bool CameraService::validateConnect(int cameraId,
+status_t CameraService::validateConnect(int cameraId,
/*inout*/
int& clientUid) const {
@@ -271,19 +319,19 @@ bool CameraService::validateConnect(int cameraId,
if (callingPid != getpid()) {
ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)",
callingPid);
- return false;
+ return PERMISSION_DENIED;
}
}
if (!mModule) {
ALOGE("Camera HAL module not loaded");
- return false;
+ return -ENODEV;
}
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
callingPid, cameraId);
- return false;
+ return -ENODEV;
}
char value[PROPERTY_VALUE_MAX];
@@ -291,36 +339,36 @@ bool CameraService::validateConnect(int cameraId,
if (strcmp(value, "1") == 0) {
// Camera is disabled by DevicePolicyManager.
ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
- return false;
+ return -EACCES;
}
ICameraServiceListener::Status currentStatus = getStatus(cameraId);
if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
ALOGI("Camera is not plugged in,"
" connect X (pid %d) rejected", callingPid);
- return false;
+ return -ENODEV;
} else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
ALOGI("Camera is enumerating,"
" connect X (pid %d) rejected", callingPid);
- return false;
+ return -EBUSY;
}
// Else don't check for STATUS_NOT_AVAILABLE.
// -- It's done implicitly in canConnectUnsafe /w the mBusy array
- return true;
+ return OK;
}
bool CameraService::canConnectUnsafe(int cameraId,
const String16& clientPackageName,
const sp<IBinder>& remoteCallback,
- sp<Client> &client) {
+ sp<BasicClient> &client) {
String8 clientName8(clientPackageName);
int callingPid = getCallingPid();
if (mClient[cameraId] != 0) {
client = mClient[cameraId].promote();
if (client != 0) {
- if (remoteCallback == client->getRemoteCallback()->asBinder()) {
+ if (remoteCallback == client->getRemote()) {
LOG1("CameraService::connect X (pid %d) (the same client)",
callingPid);
return true;
@@ -354,11 +402,13 @@ bool CameraService::canConnectUnsafe(int cameraId,
return true;
}
-sp<ICamera> CameraService::connect(
+status_t CameraService::connect(
const sp<ICameraClient>& cameraClient,
int cameraId,
const String16& clientPackageName,
- int clientUid) {
+ int clientUid,
+ /*out*/
+ sp<ICamera>& device) {
String8 clientName8(clientPackageName);
int callingPid = getCallingPid();
@@ -366,20 +416,23 @@ sp<ICamera> CameraService::connect(
LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
clientName8.string(), cameraId);
- if (!validateConnect(cameraId, /*inout*/clientUid)) {
- return NULL;
+ status_t status = validateConnect(cameraId, /*inout*/clientUid);
+ if (status != OK) {
+ return status;
}
- sp<Client> client;
+ sp<Client> client;
{
Mutex::Autolock lock(mServiceLock);
+ sp<BasicClient> clientTmp;
if (!canConnectUnsafe(cameraId, clientPackageName,
cameraClient->asBinder(),
- /*out*/client)) {
- return NULL;
+ /*out*/clientTmp)) {
+ return -EBUSY;
} else if (client.get() != NULL) {
- return client;
+ device = static_cast<Client*>(clientTmp.get());
+ return OK;
}
int facing = -1;
@@ -409,18 +462,18 @@ sp<ICamera> CameraService::connect(
break;
case -1:
ALOGE("Invalid camera id %d", cameraId);
- return NULL;
+ return BAD_VALUE;
default:
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
- return NULL;
+ return INVALID_OPERATION;
}
- if (!connectFinishUnsafe(client, client->asBinder())) {
+ status_t status = connectFinishUnsafe(client, client->getRemote());
+ if (status != OK) {
// this is probably not recoverable.. maybe the client can try again
// OK: we can only get here if we were originally in PRESENT state
updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
-
- return NULL;
+ return status;
}
mClient[cameraId] = client;
@@ -430,45 +483,49 @@ sp<ICamera> CameraService::connect(
// important: release the mutex here so the client can call back
// into the service from its destructor (can be at the end of the call)
- return client;
+ device = client;
+ return OK;
}
-bool CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
- const sp<IBinder>& clientBinder) {
- if (client->initialize(mModule) != OK) {
- return false;
+status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
+ const sp<IBinder>& remoteCallback) {
+ status_t status = client->initialize(mModule);
+ if (status != OK) {
+ return status;
}
- clientBinder->linkToDeath(this);
+ remoteCallback->linkToDeath(this);
- return true;
+ return OK;
}
-sp<IProCameraUser> CameraService::connect(
+status_t CameraService::connectPro(
const sp<IProCameraCallbacks>& cameraCb,
int cameraId,
const String16& clientPackageName,
- int clientUid)
+ int clientUid,
+ /*out*/
+ sp<IProCameraUser>& device)
{
String8 clientName8(clientPackageName);
int callingPid = getCallingPid();
LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid,
clientName8.string(), cameraId);
-
- if (!validateConnect(cameraId, /*inout*/clientUid)) {
- return NULL;
+ status_t status = validateConnect(cameraId, /*inout*/clientUid);
+ if (status != OK) {
+ return status;
}
sp<ProClient> client;
{
Mutex::Autolock lock(mServiceLock);
{
- sp<Client> client;
+ sp<BasicClient> client;
if (!canConnectUnsafe(cameraId, clientPackageName,
cameraCb->asBinder(),
/*out*/client)) {
- return NULL;
+ return -EBUSY;
}
}
@@ -479,23 +536,25 @@ sp<IProCameraUser> CameraService::connect(
case CAMERA_DEVICE_API_VERSION_1_0:
ALOGE("Camera id %d uses HALv1, doesn't support ProCamera",
cameraId);
- return NULL;
+ return -EOPNOTSUPP;
break;
case CAMERA_DEVICE_API_VERSION_2_0:
case CAMERA_DEVICE_API_VERSION_2_1:
+ case CAMERA_DEVICE_API_VERSION_3_0:
client = new ProCamera2Client(this, cameraCb, String16(),
cameraId, facing, callingPid, USE_CALLING_UID, getpid());
break;
case -1:
ALOGE("Invalid camera id %d", cameraId);
- return NULL;
+ return BAD_VALUE;
default:
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
- return NULL;
+ return INVALID_OPERATION;
}
- if (!connectFinishUnsafe(client, client->asBinder())) {
- return NULL;
+ status_t status = connectFinishUnsafe(client, client->getRemote());
+ if (status != OK) {
+ return status;
}
mProClientList[cameraId].push(client);
@@ -505,10 +564,93 @@ sp<IProCameraUser> CameraService::connect(
}
// important: release the mutex here so the client can call back
// into the service from its destructor (can be at the end of the call)
+ device = client;
+ return OK;
+}
- return client;
+status_t CameraService::connectDevice(
+ const sp<ICameraDeviceCallbacks>& cameraCb,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<ICameraDeviceUser>& device)
+{
+
+ String8 clientName8(clientPackageName);
+ int callingPid = getCallingPid();
+
+ LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid,
+ clientName8.string(), cameraId);
+
+ status_t status = validateConnect(cameraId, /*inout*/clientUid);
+ if (status != OK) {
+ return status;
+ }
+
+ sp<CameraDeviceClient> client;
+ {
+ Mutex::Autolock lock(mServiceLock);
+ {
+ sp<BasicClient> client;
+ if (!canConnectUnsafe(cameraId, clientPackageName,
+ cameraCb->asBinder(),
+ /*out*/client)) {
+ return -EBUSY;
+ }
+ }
+
+ int facing = -1;
+ int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+ // If there are other non-exclusive users of the camera,
+ // this will tear them down before we can reuse the camera
+ if (isValidCameraId(cameraId)) {
+ // transition from PRESENT -> NOT_AVAILABLE
+ updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
+ cameraId);
+ }
+
+ switch(deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_1_0:
+ ALOGW("Camera using old HAL version: %d", deviceVersion);
+ return -EOPNOTSUPP;
+ // TODO: don't allow 2.0 Only allow 2.1 and higher
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ case CAMERA_DEVICE_API_VERSION_3_0:
+ client = new CameraDeviceClient(this, cameraCb, String16(),
+ cameraId, facing, callingPid, USE_CALLING_UID, getpid());
+ break;
+ case -1:
+ ALOGE("Invalid camera id %d", cameraId);
+ return BAD_VALUE;
+ default:
+ ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+ return INVALID_OPERATION;
+ }
+
+ status_t status = connectFinishUnsafe(client, client->getRemote());
+ if (status != OK) {
+ // this is probably not recoverable.. maybe the client can try again
+ // OK: we can only get here if we were originally in PRESENT state
+ updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
+ return status;
+ }
+
+ LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId,
+ getpid());
+
+ mClient[cameraId] = client;
+ }
+ // important: release the mutex here so the client can call back
+ // into the service from its destructor (can be at the end of the call)
+
+ device = client;
+ return OK;
}
+
status_t CameraService::addListener(
const sp<ICameraServiceListener>& listener) {
ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -566,14 +708,14 @@ void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
Mutex::Autolock lock(mServiceLock);
int outIndex;
- sp<Client> client = findClientUnsafe(remoteBinder, outIndex);
+ sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex);
if (client != 0) {
// Found our camera, clear and leave.
LOG1("removeClient: clear camera %d", outIndex);
mClient[outIndex].clear();
- client->unlinkToDeath(this);
+ client->getRemote()->unlinkToDeath(this);
} else {
sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
@@ -620,9 +762,9 @@ sp<CameraService::ProClient> CameraService::findProClientUnsafe(
return clientPro;
}
-sp<CameraService::Client> CameraService::findClientUnsafe(
+sp<CameraService::BasicClient> CameraService::findClientUnsafe(
const wp<IBinder>& cameraClient, int& outIndex) {
- sp<Client> client;
+ sp<BasicClient> client;
for (int i = 0; i < mNumberOfCameras; i++) {
@@ -640,7 +782,7 @@ sp<CameraService::Client> CameraService::findClientUnsafe(
continue;
}
- if (cameraClient == client->getRemoteCallback()->asBinder()) {
+ if (cameraClient == client->getRemote()) {
// Found our camera
outIndex = i;
return client;
@@ -651,7 +793,7 @@ sp<CameraService::Client> CameraService::findClientUnsafe(
return NULL;
}
-CameraService::Client* CameraService::getClientByIdUnsafe(int cameraId) {
+CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) {
if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
return mClient[cameraId].unsafe_get();
}
@@ -906,7 +1048,9 @@ Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should
// be acquired for this to be safe
CameraService::Client* CameraService::Client::getClientFromCookie(void* user) {
- Client* client = gCameraService->getClientByIdUnsafe((int) user);
+ BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int) user);
+ // OK: only CameraClient calls this, and they already cast anyway.
+ Client* client = static_cast<Client*>(basicClient);
// This could happen if the Client is in the process of shutting down (the
// last strong reference is gone, but the destructor hasn't finished
@@ -1058,7 +1202,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
}
}
- sp<Client> client = mClient[i].promote();
+ sp<BasicClient> client = mClient[i].promote();
if (client == 0) {
result = String8::format(" Device is closed, no client instance\n");
write(fd, result.string(), result.size());
@@ -1076,6 +1220,10 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
if (locked) mServiceLock.unlock();
+ // Dump camera traces if there were any
+ write(fd, "\n", 1);
+ camera3::CameraTraces::dump(fd, args);
+
// change logging level
int n = args.size();
for (int i = 0; i + 1 < n; i++) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 710f164..ad6a582 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -29,6 +29,8 @@
#include <camera/ICameraClient.h>
#include <camera/IProCameraUser.h>
#include <camera/IProCameraCallbacks.h>
+#include <camera/camera2/ICameraDeviceUser.h>
+#include <camera/camera2/ICameraDeviceCallbacks.h>
#include <camera/ICameraServiceListener.h>
@@ -69,11 +71,26 @@ public:
virtual int32_t getNumberOfCameras();
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
-
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
- const String16& clientPackageName, int clientUid);
- virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb,
- int cameraId, const String16& clientPackageName, int clientUid);
+ virtual status_t getCameraCharacteristics(int cameraId,
+ CameraMetadata* cameraInfo);
+
+ virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
+ const String16& clientPackageName, int clientUid,
+ /*out*/
+ sp<ICamera>& device);
+
+ virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb,
+ int cameraId, const String16& clientPackageName, int clientUid,
+ /*out*/
+ sp<IProCameraUser>& device);
+
+ virtual status_t connectDevice(
+ const sp<ICameraDeviceCallbacks>& cameraCb,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<ICameraDeviceUser>& device);
virtual status_t addListener(const sp<ICameraServiceListener>& listener);
virtual status_t removeListener(
@@ -99,13 +116,17 @@ public:
void playSound(sound_kind kind);
void releaseSound();
+ /////////////////////////////////////////////////////////////////////
+ // CameraDeviceFactory functionality
+ int getDeviceVersion(int cameraId, int* facing = NULL);
+
/////////////////////////////////////////////////////////////////////
// CameraClient functionality
// returns plain pointer of client. Note that mClientLock should be acquired to
// prevent the client from destruction. The result can be NULL.
- virtual Client* getClientByIdUnsafe(int cameraId);
+ virtual BasicClient* getClientByIdUnsafe(int cameraId);
virtual Mutex* getClientLockById(int cameraId);
class BasicClient : public virtual RefBase {
@@ -114,11 +135,17 @@ public:
virtual void disconnect() = 0;
+ // because we can't virtually inherit IInterface, which breaks
+ // virtual inheritance
+ virtual sp<IBinder> asBinderWrapper() = 0;
+
// Return the remote callback binder object (e.g. IProCameraCallbacks)
- wp<IBinder> getRemote() {
+ sp<IBinder> getRemote() {
return mRemoteBinder;
}
+ virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -147,7 +174,7 @@ public:
pid_t mServicePid; // immutable after constructor
// - The app-side Binder interface to receive callbacks from us
- wp<IBinder> mRemoteBinder; // immutable after constructor
+ sp<IBinder> mRemoteBinder; // immutable after constructor
// permissions management
status_t startCameraOps();
@@ -187,9 +214,10 @@ public:
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<IGraphicBufferProducer>& bufferProducer)=0;
+ virtual status_t setPreviewTarget(const sp<IGraphicBufferProducer>& bufferProducer)=0;
virtual void setPreviewCallbackFlag(int flag) = 0;
+ virtual status_t setPreviewCallbackTarget(
+ const sp<IGraphicBufferProducer>& callbackProducer) = 0;
virtual status_t startPreview() = 0;
virtual void stopPreview() = 0;
virtual bool previewEnabled() = 0;
@@ -221,6 +249,10 @@ public:
return mRemoteCallback;
}
+ virtual sp<IBinder> asBinderWrapper() {
+ return asBinder();
+ }
+
protected:
static Mutex* getClientLockFromCookie(void* user);
// convert client from cookie. Client lock should be acquired before getting Client.
@@ -285,7 +317,7 @@ private:
virtual void onFirstRef();
// Step 1. Check if we can connect, before we acquire the service lock.
- bool validateConnect(int cameraId,
+ status_t validateConnect(int cameraId,
/*inout*/
int& clientUid) const;
@@ -294,16 +326,17 @@ private:
const String16& clientPackageName,
const sp<IBinder>& remoteCallback,
/*out*/
- sp<Client> &client);
+ sp<BasicClient> &client);
// When connection is successful, initialize client and track its death
- bool connectFinishUnsafe(const sp<BasicClient>& client,
- const sp<IBinder>& clientBinder);
+ status_t connectFinishUnsafe(const sp<BasicClient>& client,
+ const sp<IBinder>& remoteCallback);
virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient);
Mutex mServiceLock;
- wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock
+ // either a Client or CameraDeviceClient
+ wp<BasicClient> mClient[MAX_CAMERAS]; // protected by mServiceLock
Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
int mNumberOfCameras;
@@ -311,7 +344,7 @@ private:
Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
// needs to be called with mServiceLock held
- sp<Client> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
+ sp<BasicClient> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
sp<ProClient> findProClientUnsafe(
const wp<IBinder>& cameraCallbacksRemote);
@@ -352,7 +385,6 @@ private:
virtual void binderDied(const wp<IBinder> &who);
// Helpers
- int getDeviceVersion(int cameraId, int* facing);
bool isValidCameraId(int cameraId);
};
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6942006..0b6ca5c 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "Camera2"
+#define LOG_TAG "Camera2Client"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
@@ -23,13 +23,15 @@
#include <cutils/properties.h>
#include <gui/Surface.h>
-#include "camera2/Parameters.h"
-#include "Camera2Client.h"
-#include "Camera2Device.h"
-#include "Camera3Device.h"
-#include "camera2/ZslProcessor.h"
-#include "camera2/ZslProcessor3.h"
+#include "api1/Camera2Client.h"
+
+#include "api1/client2/StreamingProcessor.h"
+#include "api1/client2/JpegProcessor.h"
+#include "api1/client2/CaptureSequencer.h"
+#include "api1/client2/CallbackProcessor.h"
+#include "api1/client2/ZslProcessor.h"
+#include "api1/client2/ZslProcessor3.h"
#define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__);
#define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__);
@@ -58,22 +60,6 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
mDeviceVersion(deviceVersion)
{
ATRACE_CALL();
- ALOGI("Camera %d: Opened", cameraId);
-
- switch (mDeviceVersion) {
- case CAMERA_DEVICE_API_VERSION_2_0:
- mDevice = new Camera2Device(cameraId);
- break;
- case CAMERA_DEVICE_API_VERSION_3_0:
- mDevice = new Camera3Device(cameraId);
- break;
- default:
- ALOGE("Camera %d: Unknown HAL device version %d",
- cameraId, mDeviceVersion);
- mDevice = NULL;
- break;
- }
-
SharedParameters::Lock l(mParameters);
l.mParameters.state = Parameters::DISCONNECTED;
@@ -90,13 +76,15 @@ status_t Camera2Client::initialize(camera_module_t *module)
return res;
}
- SharedParameters::Lock l(mParameters);
+ {
+ 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;
+ 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;
@@ -149,6 +137,7 @@ status_t Camera2Client::initialize(camera_module_t *module)
mCallbackProcessor->run(threadName.string());
if (gLogLevel >= 1) {
+ SharedParameters::Lock l(mParameters);
ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__,
mCameraId);
ALOGD("%s", l.mParameters.paramsFlattened.string());
@@ -297,6 +286,7 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
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_PASSIVE_UNFOCUSED)
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)
@@ -340,6 +330,10 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
result.appendFormat(" Video stabilization is %s\n",
p.videoStabilization ? "enabled" : "disabled");
+ result.appendFormat(" Selected still capture FPS range: %d - %d\n",
+ p.fastInfo.bestStillCaptureFpsRange[0],
+ p.fastInfo.bestStillCaptureFpsRange[1]);
+
result.append(" Current streams:\n");
result.appendFormat(" Preview stream ID: %d\n",
getPreviewStreamId());
@@ -362,6 +356,10 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
result.appendFormat(" meteringCropRegion\n");
haveQuirk = true;
}
+ if (p.quirks.partialResults) {
+ result.appendFormat(" usePartialResult\n");
+ haveQuirk = true;
+ }
if (!haveQuirk) {
result.appendFormat(" none\n");
}
@@ -505,25 +503,7 @@ status_t Camera2Client::unlock() {
return EBUSY;
}
-status_t Camera2Client::setPreviewDisplay(
- const sp<Surface>& surface) {
- ATRACE_CALL();
- ALOGV("%s: E", __FUNCTION__);
- Mutex::Autolock icl(mBinderSerializationLock);
- status_t res;
- if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
-
- sp<IBinder> binder;
- sp<ANativeWindow> window;
- if (surface != 0) {
- binder = surface->getIGraphicBufferProducer()->asBinder();
- window = surface;
- }
-
- return setPreviewWindowL(binder,window);
-}
-
-status_t Camera2Client::setPreviewTexture(
+status_t Camera2Client::setPreviewTarget(
const sp<IGraphicBufferProducer>& bufferProducer) {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
@@ -535,7 +515,10 @@ status_t Camera2Client::setPreviewTexture(
sp<ANativeWindow> window;
if (bufferProducer != 0) {
binder = bufferProducer->asBinder();
- window = new Surface(bufferProducer);
+ // Using controlledByApp flag to ensure that the buffer queue remains in
+ // async mode for the old camera API, where many applications depend
+ // on that behavior.
+ window = new Surface(bufferProducer, /*controlledByApp*/ true);
}
return setPreviewWindowL(binder, window);
}
@@ -632,6 +615,19 @@ void Camera2Client::setPreviewCallbackFlagL(Parameters &params, int flag) {
params.previewCallbackOneShot = true;
}
if (params.previewCallbackFlags != (uint32_t)flag) {
+
+ if (params.previewCallbackSurface && flag != CAMERA_FRAME_CALLBACK_FLAG_NOOP) {
+ // Disable any existing preview callback window when enabling
+ // preview callback flags
+ res = mCallbackProcessor->setCallbackWindow(NULL);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to clear preview callback surface:"
+ " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+ return;
+ }
+ params.previewCallbackSurface = false;
+ }
+
params.previewCallbackFlags = flag;
if (params.state == Parameters::PREVIEW) {
@@ -643,9 +639,61 @@ void Camera2Client::setPreviewCallbackFlagL(Parameters &params, int flag) {
}
}
}
+}
+status_t Camera2Client::setPreviewCallbackTarget(
+ const sp<IGraphicBufferProducer>& callbackProducer) {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+ Mutex::Autolock icl(mBinderSerializationLock);
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ sp<ANativeWindow> window;
+ if (callbackProducer != 0) {
+ window = new Surface(callbackProducer);
+ }
+
+ res = mCallbackProcessor->setCallbackWindow(window);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set preview callback surface: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ SharedParameters::Lock l(mParameters);
+
+ if (window != NULL) {
+ // Disable traditional callbacks when a valid callback target is given
+ l.mParameters.previewCallbackFlags = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
+ l.mParameters.previewCallbackOneShot = false;
+ l.mParameters.previewCallbackSurface = true;
+ } else {
+ // Disable callback target if given a NULL interface.
+ l.mParameters.previewCallbackSurface = false;
+ }
+
+ switch(l.mParameters.state) {
+ case Parameters::PREVIEW:
+ res = startPreviewL(l.mParameters, true);
+ break;
+ case Parameters::RECORD:
+ case Parameters::VIDEO_SNAPSHOT:
+ res = startRecordingL(l.mParameters, true);
+ break;
+ default:
+ break;
+ }
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to refresh request in state %s",
+ __FUNCTION__, mCameraId,
+ Parameters::getStateName(l.mParameters.state));
+ }
+
+ return OK;
}
+
status_t Camera2Client::startPreview() {
ATRACE_CALL();
ALOGV("%s: E", __FUNCTION__);
@@ -707,9 +755,11 @@ status_t Camera2Client::startPreviewL(Parameters &params, bool restart) {
return res;
}
- Vector<uint8_t> outputStreams;
- bool callbacksEnabled = params.previewCallbackFlags &
- CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
+ Vector<int32_t> outputStreams;
+ bool callbacksEnabled = (params.previewCallbackFlags &
+ CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ||
+ params.previewCallbackSurface;
+
if (callbacksEnabled) {
// Can't have recording stream hanging around when enabling callbacks,
// since it exceeds the max stream count on some devices.
@@ -816,6 +866,7 @@ void Camera2Client::stopPreviewL() {
// no break
case Parameters::RECORD:
case Parameters::PREVIEW:
+ syncWithDevice();
res = stopStream();
if (res != OK) {
ALOGE("%s: Camera %d: Can't stop streaming: %s (%d)",
@@ -960,7 +1011,7 @@ status_t Camera2Client::startRecordingL(Parameters &params, bool restart) {
return res;
}
- Vector<uint8_t> outputStreams;
+ Vector<int32_t> outputStreams;
outputStreams.push(getPreviewStreamId());
outputStreams.push(getRecordingStreamId());
@@ -1105,6 +1156,8 @@ status_t Camera2Client::autoFocus() {
l.mParameters.currentAfTriggerId = ++l.mParameters.afTriggerCounter;
triggerId = l.mParameters.currentAfTriggerId;
}
+ ATRACE_ASYNC_BEGIN(kAutofocusLabel, triggerId);
+
syncWithDevice();
mDevice->triggerAutofocus(triggerId);
@@ -1127,6 +1180,12 @@ status_t Camera2Client::cancelAutoFocus() {
l.mParameters.focusMode == Parameters::FOCUS_MODE_INFINITY) {
return OK;
}
+
+ // An active AF trigger is canceled
+ if (l.mParameters.afTriggerCounter == l.mParameters.currentAfTriggerId) {
+ ATRACE_ASYNC_END(kAutofocusLabel, l.mParameters.currentAfTriggerId);
+ }
+
triggerId = ++l.mParameters.afTriggerCounter;
// When using triggerAfWithAuto quirk, may need to reset focus mode to
@@ -1155,6 +1214,7 @@ status_t Camera2Client::takePicture(int msgType) {
status_t res;
if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+ int takePictureCounter;
{
SharedParameters::Lock l(mParameters);
switch (l.mParameters.state) {
@@ -1193,8 +1253,11 @@ status_t Camera2Client::takePicture(int msgType) {
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
+ takePictureCounter = ++l.mParameters.takePictureCounter;
}
+ ATRACE_ASYNC_BEGIN(kTakepictureLabel, takePictureCounter);
+
// Need HAL to have correct settings before (possibly) triggering precapture
syncWithDevice();
@@ -1422,7 +1485,24 @@ void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
bool afInMotion = false;
{
SharedParameters::Lock l(mParameters);
+ // Trace end of AF state
+ char tmp[32];
+ if (l.mParameters.afStateCounter > 0) {
+ camera_metadata_enum_snprint(
+ ANDROID_CONTROL_AF_STATE, l.mParameters.focusState, tmp, sizeof(tmp));
+ ATRACE_ASYNC_END(tmp, l.mParameters.afStateCounter);
+ }
+
+ // Update state
l.mParameters.focusState = newState;
+ l.mParameters.afStateCounter++;
+
+ // Trace start of AF state
+
+ camera_metadata_enum_snprint(
+ ANDROID_CONTROL_AF_STATE, l.mParameters.focusState, tmp, sizeof(tmp));
+ ATRACE_ASYNC_BEGIN(tmp, l.mParameters.afStateCounter);
+
switch (l.mParameters.focusMode) {
case Parameters::FOCUS_MODE_AUTO:
case Parameters::FOCUS_MODE_MACRO:
@@ -1444,6 +1524,7 @@ void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
case ANDROID_CONTROL_AF_STATE_INACTIVE:
case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
default:
// Unexpected in AUTO/MACRO mode
ALOGE("%s: Unexpected AF state transition in AUTO/MACRO mode: %d",
@@ -1486,6 +1567,7 @@ void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
afInMotion = true;
// no break
case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+ case ANDROID_CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
// Stop passive scan, inform upstream
if (l.mParameters.enableFocusMoveMessages) {
sendMovingMessage = true;
@@ -1514,6 +1596,7 @@ void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
}
}
if (sendCompletedMessage) {
+ ATRACE_ASYNC_END(kAutofocusLabel, triggerId);
SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
if (l.mRemoteCallback != 0) {
l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS,
@@ -1723,4 +1806,7 @@ status_t Camera2Client::updateProcessorStream(sp<ProcessorT> processor,
return res;
}
+const char* Camera2Client::kAutofocusLabel = "autofocus";
+const char* Camera2Client::kTakepictureLabel = "take_picture";
+
} // namespace android
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 8ab46b1..fe0bf74 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -17,19 +17,29 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_H
#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_H
-#include "CameraDeviceBase.h"
#include "CameraService.h"
-#include "camera2/Parameters.h"
-#include "camera2/FrameProcessor.h"
-#include "camera2/StreamingProcessor.h"
-#include "camera2/JpegProcessor.h"
-#include "camera2/ZslProcessorInterface.h"
-#include "camera2/CaptureSequencer.h"
-#include "camera2/CallbackProcessor.h"
-#include "Camera2ClientBase.h"
+#include "common/CameraDeviceBase.h"
+#include "common/Camera2ClientBase.h"
+#include "api1/client2/Parameters.h"
+#include "api1/client2/FrameProcessor.h"
+//#include "api1/client2/StreamingProcessor.h"
+//#include "api1/client2/JpegProcessor.h"
+//#include "api1/client2/ZslProcessorInterface.h"
+//#include "api1/client2/CaptureSequencer.h"
+//#include "api1/client2/CallbackProcessor.h"
namespace android {
+namespace camera2 {
+
+class StreamingProcessor;
+class JpegProcessor;
+class ZslProcessorInterface;
+class CaptureSequencer;
+class CallbackProcessor;
+
+}
+
class IMemory;
/**
* Interface between android.hardware.Camera API and Camera HAL device for versions
@@ -47,10 +57,12 @@ public:
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(
+ virtual status_t setPreviewTarget(
const sp<IGraphicBufferProducer>& bufferProducer);
virtual void setPreviewCallbackFlag(int flag);
+ virtual status_t setPreviewCallbackTarget(
+ const sp<IGraphicBufferProducer>& callbackProducer);
+
virtual status_t startPreview();
virtual void stopPreview();
virtual bool previewEnabled();
@@ -124,6 +136,10 @@ public:
static const int32_t kCaptureRequestIdStart = 30000000;
static const int32_t kCaptureRequestIdEnd = 40000000;
+ // Constant strings for ATRACE logging
+ static const char* kAutofocusLabel;
+ static const char* kTakepictureLabel;
+
private:
/** ICamera interface-related private members */
typedef camera2::Parameters Parameters;
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index e577fa3..bd6805d 100644
--- a/services/camera/libcameraservice/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -20,8 +20,8 @@
#include <cutils/properties.h>
#include <gui/Surface.h>
-#include "CameraClient.h"
-#include "CameraHardwareInterface.h"
+#include "api1/CameraClient.h"
+#include "device1/CameraHardwareInterface.h"
#include "CameraService.h"
namespace android {
@@ -308,26 +308,20 @@ status_t CameraClient::setPreviewWindow(const sp<IBinder>& binder,
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->getIGraphicBufferProducer()->asBinder() : 0);
- sp<ANativeWindow> window(surface);
- return setPreviewWindow(binder, window);
-}
-
-// set the SurfaceTextureClient that the preview will use
-status_t CameraClient::setPreviewTexture(
+// set the buffer consumer that the preview will use
+status_t CameraClient::setPreviewTarget(
const sp<IGraphicBufferProducer>& bufferProducer) {
- LOG1("setPreviewTexture(%p) (pid %d)", bufferProducer.get(),
+ LOG1("setPreviewTarget(%p) (pid %d)", bufferProducer.get(),
getCallingPid());
sp<IBinder> binder;
sp<ANativeWindow> window;
if (bufferProducer != 0) {
binder = bufferProducer->asBinder();
- window = new Surface(bufferProducer);
+ // Using controlledByApp flag to ensure that the buffer queue remains in
+ // async mode for the old camera API, where many applications depend
+ // on that behavior.
+ window = new Surface(bufferProducer, /*controlledByApp*/ true);
}
return setPreviewWindow(binder, window);
}
@@ -347,6 +341,13 @@ void CameraClient::setPreviewCallbackFlag(int callback_flag) {
}
}
+status_t CameraClient::setPreviewCallbackTarget(
+ const sp<IGraphicBufferProducer>& callbackProducer) {
+ (void)callbackProducer;
+ ALOGE("%s: Unimplemented!", __FUNCTION__);
+ return INVALID_OPERATION;
+}
+
// start preview mode
status_t CameraClient::startPreview() {
LOG1("startPreview (pid %d)", getCallingPid());
diff --git a/services/camera/libcameraservice/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 7f0cb29..4b89564 100644
--- a/services/camera/libcameraservice/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -37,9 +37,10 @@ public:
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<IGraphicBufferProducer>& bufferProducer);
+ virtual status_t setPreviewTarget(const sp<IGraphicBufferProducer>& bufferProducer);
virtual void setPreviewCallbackFlag(int flag);
+ virtual status_t setPreviewCallbackTarget(
+ const sp<IGraphicBufferProducer>& callbackProducer);
virtual status_t startPreview();
virtual void stopPreview();
virtual bool previewEnabled();
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.cpp b/services/camera/libcameraservice/api1/client2/BurstCapture.cpp
index 192d419..0bfdfd4 100644
--- a/services/camera/libcameraservice/camera2/BurstCapture.cpp
+++ b/services/camera/libcameraservice/api1/client2/BurstCapture.cpp
@@ -22,8 +22,8 @@
#include "BurstCapture.h"
-#include "../Camera2Client.h"
-#include "JpegCompressor.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/JpegCompressor.h"
namespace android {
namespace camera2 {
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/api1/client2/BurstCapture.h
index a2cc893..ea321fd 100644
--- a/services/camera/libcameraservice/camera2/BurstCapture.h
+++ b/services/camera/libcameraservice/api1/client2/BurstCapture.h
@@ -17,11 +17,12 @@
#ifndef ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
#define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
-#include "camera/CameraMetadata.h"
+#include <camera/CameraMetadata.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
#include <gui/CpuConsumer.h>
-#include "Camera2Device.h"
+
+#include "device2/Camera2Device.h"
namespace android {
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 522f49a..d2ac79c 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -20,11 +20,11 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-
-#include "CallbackProcessor.h"
#include <gui/Surface.h>
-#include "../CameraDeviceBase.h"
-#include "../Camera2Client.h"
+
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/CallbackProcessor.h"
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
@@ -37,6 +37,7 @@ CallbackProcessor::CallbackProcessor(sp<Camera2Client> client):
mDevice(client->getCameraDevice()),
mId(client->getCameraId()),
mCallbackAvailable(false),
+ mCallbackToApp(false),
mCallbackStreamId(NO_STREAM) {
}
@@ -53,6 +54,35 @@ void CallbackProcessor::onFrameAvailable() {
}
}
+status_t CallbackProcessor::setCallbackWindow(
+ sp<ANativeWindow> callbackWindow) {
+ ATRACE_CALL();
+ status_t res;
+
+ Mutex::Autolock l(mInputMutex);
+
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) return OK;
+ sp<CameraDeviceBase> device = client->getCameraDevice();
+
+ // If the window is changing, clear out stream if it already exists
+ if (mCallbackWindow != callbackWindow && mCallbackStreamId != NO_STREAM) {
+ res = device->deleteStream(mCallbackStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old stream "
+ "for callbacks: %s (%d)", __FUNCTION__,
+ client->getCameraId(), strerror(-res), res);
+ return res;
+ }
+ mCallbackStreamId = NO_STREAM;
+ mCallbackConsumer.clear();
+ }
+ mCallbackWindow = callbackWindow;
+ mCallbackToApp = (mCallbackWindow != NULL);
+
+ return OK;
+}
+
status_t CallbackProcessor::updateStream(const Parameters &params) {
ATRACE_CALL();
status_t res;
@@ -67,21 +97,24 @@ status_t CallbackProcessor::updateStream(const Parameters &params) {
// If possible, use the flexible YUV format
int32_t callbackFormat = params.previewFormat;
- if (params.fastInfo.useFlexibleYuv &&
+ if (mCallbackToApp) {
+ // TODO: etalvala: This should use the flexible YUV format as well, but
+ // need to reconcile HAL2/HAL3 requirements.
+ callbackFormat = HAL_PIXEL_FORMAT_YV12;
+ } else if(params.fastInfo.useFlexibleYuv &&
(params.previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
params.previewFormat == HAL_PIXEL_FORMAT_YV12) ) {
callbackFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
}
- if (mCallbackConsumer == 0) {
- // Create CPU buffer queue endpoint. Make it async to avoid disconnect
- // deadlocks.
- mCallbackConsumer = new CpuConsumer(kCallbackHeapCount,
- /*synchronized*/ false);
+ if (!mCallbackToApp && mCallbackConsumer == 0) {
+ // Create CPU buffer queue endpoint, since app hasn't given us one
+ // Make it async to avoid disconnect deadlocks
+ sp<BufferQueue> bq = new BufferQueue();
+ mCallbackConsumer = new CpuConsumer(bq, kCallbackHeapCount);
mCallbackConsumer->setFrameAvailableListener(this);
mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer"));
- mCallbackWindow = new Surface(
- mCallbackConsumer->getProducerInterface());
+ mCallbackWindow = new Surface(bq);
}
if (mCallbackStreamId != NO_STREAM) {
@@ -106,8 +139,8 @@ status_t CallbackProcessor::updateStream(const Parameters &params) {
res = device->deleteStream(mCallbackStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to delete old output stream "
- "for callbacks: %s (%d)", __FUNCTION__, mId,
- strerror(-res), res);
+ "for callbacks: %s (%d)", __FUNCTION__,
+ mId, strerror(-res), res);
return res;
}
mCallbackStreamId = NO_STREAM;
@@ -279,6 +312,16 @@ status_t CallbackProcessor::processNewCallback(sp<Camera2Client> &client) {
return OK;
}
+ if (imgBuffer.width != static_cast<uint32_t>(l.mParameters.previewWidth) ||
+ imgBuffer.height != static_cast<uint32_t>(l.mParameters.previewHeight)) {
+ ALOGW("%s: The preview size has changed to %d x %d from %d x %d, this buffer is"
+ " no longer valid, dropping",__FUNCTION__,
+ l.mParameters.previewWidth, l.mParameters.previewHeight,
+ imgBuffer.width, imgBuffer.height);
+ mCallbackConsumer->unlockBuffer(imgBuffer);
+ return OK;
+ }
+
previewFormat = l.mParameters.previewFormat;
useFlexibleYuv = l.mParameters.fastInfo.useFlexibleYuv &&
(previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h
index d851a84..613f5be 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.h
@@ -23,9 +23,8 @@
#include <utils/Mutex.h>
#include <utils/Condition.h>
#include <gui/CpuConsumer.h>
-#include "Parameters.h"
-#include "camera/CameraMetadata.h"
-#include "Camera2Heap.h"
+
+#include "api1/client2/Camera2Heap.h"
namespace android {
@@ -34,6 +33,8 @@ class CameraDeviceBase;
namespace camera2 {
+class Parameters;
+
/***
* Still image capture output image processing
*/
@@ -45,6 +46,8 @@ class CallbackProcessor:
void onFrameAvailable();
+ // Set to NULL to disable the direct-to-app callback window
+ status_t setCallbackWindow(sp<ANativeWindow> callbackWindow);
status_t updateStream(const Parameters &params);
status_t deleteStream();
int getStreamId() const;
@@ -64,6 +67,9 @@ class CallbackProcessor:
NO_STREAM = -1
};
+ // True if mCallbackWindow is a remote consumer, false if just the local
+ // mCallbackConsumer
+ bool mCallbackToApp;
int mCallbackStreamId;
static const size_t kCallbackHeapCount = 6;
sp<CpuConsumer> mCallbackConsumer;
diff --git a/services/camera/libcameraservice/camera2/Camera2Heap.h b/services/camera/libcameraservice/api1/client2/Camera2Heap.h
index 9c72d76..9c72d76 100644
--- a/services/camera/libcameraservice/camera2/Camera2Heap.h
+++ b/services/camera/libcameraservice/api1/client2/Camera2Heap.h
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index e5a011c..8a4ce4e 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -22,12 +22,11 @@
#include <utils/Trace.h>
#include <utils/Vector.h>
-#include "CaptureSequencer.h"
-#include "BurstCapture.h"
-#include "../Camera2Device.h"
-#include "../Camera2Client.h"
-#include "Parameters.h"
-#include "ZslProcessorInterface.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/CaptureSequencer.h"
+#include "api1/client2/BurstCapture.h"
+#include "api1/client2/Parameters.h"
+#include "api1/client2/ZslProcessorInterface.h"
namespace android {
namespace camera2 {
@@ -44,6 +43,7 @@ CaptureSequencer::CaptureSequencer(wp<Camera2Client> client):
mShutterNotified(false),
mClient(client),
mCaptureState(IDLE),
+ mStateTransitionCount(0),
mTriggerId(0),
mTimeoutCount(0),
mCaptureId(Camera2Client::kCaptureRequestIdStart),
@@ -104,12 +104,12 @@ void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) {
}
}
-void CaptureSequencer::onFrameAvailable(int32_t frameId,
+void CaptureSequencer::onFrameAvailable(int32_t requestId,
const CameraMetadata &frame) {
ALOGV("%s: Listener found new frame", __FUNCTION__);
ATRACE_CALL();
Mutex::Autolock l(mInputMutex);
- mNewFrameId = frameId;
+ mNewFrameId = requestId;
mNewFrame = frame;
if (!mNewFrameReceived) {
mNewFrameReceived = true;
@@ -199,8 +199,14 @@ bool CaptureSequencer::threadLoop() {
Mutex::Autolock l(mStateMutex);
if (currentState != mCaptureState) {
+ if (mCaptureState != IDLE) {
+ ATRACE_ASYNC_END(kStateNames[mCaptureState], mStateTransitionCount);
+ }
mCaptureState = currentState;
- ATRACE_INT("cam2_capt_state", mCaptureState);
+ mStateTransitionCount++;
+ if (mCaptureState != IDLE) {
+ ATRACE_ASYNC_BEGIN(kStateNames[mCaptureState], mStateTransitionCount);
+ }
ALOGV("Camera %d: New capture state %s",
client->getCameraId(), kStateNames[mCaptureState]);
mStateChanged.signal();
@@ -244,6 +250,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &c
mBusy = false;
}
+ int takePictureCounter = 0;
{
SharedParameters::Lock l(client->getParameters());
switch (l.mParameters.state) {
@@ -271,6 +278,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &c
Parameters::getStateName(l.mParameters.state));
res = INVALID_OPERATION;
}
+ takePictureCounter = l.mParameters.takePictureCounter;
}
sp<ZslProcessorInterface> processor = mZslProcessor.promote();
if (processor != 0) {
@@ -283,6 +291,8 @@ CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &c
* Fire the jpegCallback in Camera#takePicture(..., jpegCallback)
*/
if (mCaptureBuffer != 0 && res == OK) {
+ ATRACE_ASYNC_END(Camera2Client::kTakepictureLabel, takePictureCounter);
+
Camera2Client::SharedCameraCallbacks::Lock
l(client->mSharedCameraCallbacks);
ALOGV("%s: Sending still image to client", __FUNCTION__);
@@ -380,11 +390,23 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart(
sp<Camera2Client> &client) {
ATRACE_CALL();
+ bool isAeConverged = false;
// Get the onFrameAvailable callback when the requestID == mCaptureId
client->registerFrameListener(mCaptureId, mCaptureId + 1,
this);
+
+ {
+ Mutex::Autolock l(mInputMutex);
+ isAeConverged = (mAEState == ANDROID_CONTROL_AE_STATE_CONVERGED);
+ }
+
{
SharedParameters::Lock l(client->getParameters());
+ // Skip AE precapture when it is already converged and not in force flash mode.
+ if (l.mParameters.flashMode != Parameters::FLASH_MODE_ON && isAeConverged) {
+ return STANDARD_CAPTURE;
+ }
+
mTriggerId = l.mParameters.precaptureTriggerCounter++;
}
client->getCameraDevice()->triggerPrecaptureMetering(mTriggerId);
@@ -438,7 +460,8 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCapture(
status_t res;
ATRACE_CALL();
SharedParameters::Lock l(client->getParameters());
- Vector<uint8_t> outputStreams;
+ Vector<int32_t> outputStreams;
+ uint8_t captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
/**
* Set up output streams in the request
@@ -457,6 +480,7 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCapture(
if (l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
outputStreams.push(client->getRecordingStreamId());
+ captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT);
}
res = mCaptureRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
@@ -466,6 +490,10 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCapture(
&mCaptureId, 1);
}
if (res == OK) {
+ res = mCaptureRequest.update(ANDROID_CONTROL_CAPTURE_INTENT,
+ &captureIntent, 1);
+ }
+ if (res == OK) {
res = mCaptureRequest.sort();
}
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
index 76750aa..9fb4ee7 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h
@@ -62,7 +62,7 @@ class CaptureSequencer:
void notifyAutoExposure(uint8_t newState, int triggerId);
// Notifications from the frame processor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
// Notifications from the JPEG processor
void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer);
@@ -100,7 +100,7 @@ class CaptureSequencer:
* Internal to CaptureSequencer
*/
static const nsecs_t kWaitDuration = 100000000; // 100 ms
- static const int kMaxTimeoutsForPrecaptureStart = 2; // 200 ms
+ static const int kMaxTimeoutsForPrecaptureStart = 10; // 1 sec
static const int kMaxTimeoutsForPrecaptureEnd = 20; // 2 sec
static const int kMaxTimeoutsForCaptureEnd = 40; // 4 sec
@@ -125,6 +125,7 @@ class CaptureSequencer:
NUM_CAPTURE_STATES
} mCaptureState;
static const char* kStateNames[];
+ int mStateTransitionCount;
Mutex mStateMutex; // Guards mCaptureState
Condition mStateChanged;
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index d13d398..19acae4 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -21,18 +21,35 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "FrameProcessor.h"
-#include "../CameraDeviceBase.h"
-#include "../Camera2Client.h"
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/FrameProcessor.h"
namespace android {
namespace camera2 {
FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device,
- wp<Camera2Client> client) :
- ProFrameProcessor(device),
+ sp<Camera2Client> client) :
+ FrameProcessorBase(device),
mClient(client),
- mLastFrameNumberOfFaces(0) {
+ mLastFrameNumberOfFaces(0),
+ mLast3AFrameNumber(-1) {
+
+ sp<CameraDeviceBase> d = device.promote();
+ mSynthesize3ANotify = !(d->willNotify3A());
+
+ {
+ SharedParameters::Lock l(client->getParameters());
+ mUsePartialQuirk = l.mParameters.quirks.partialResults;
+
+ // Initialize starting 3A state
+ m3aState.afTriggerId = l.mParameters.afTriggerCounter;
+ m3aState.aeTriggerId = l.mParameters.precaptureTriggerCounter;
+ // Check if lens is fixed-focus
+ if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
+ m3aState.afMode = ANDROID_CONTROL_AF_MODE_OFF;
+ }
+ }
}
FrameProcessor::~FrameProcessor() {
@@ -46,15 +63,25 @@ bool FrameProcessor::processSingleFrame(CameraMetadata &frame,
return false;
}
- if (processFaceDetect(frame, client) != OK) {
- return false;
+ bool partialResult = false;
+ if (mUsePartialQuirk) {
+ camera_metadata_entry_t entry;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count > 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ partialResult = true;
+ }
}
- if (!ProFrameProcessor::processSingleFrame(frame, device)) {
+ if (!partialResult && processFaceDetect(frame, client) != OK) {
return false;
}
- return true;
+ if (mSynthesize3ANotify) {
+ process3aState(frame, client);
+ }
+
+ return FrameProcessorBase::processSingleFrame(frame, device);
}
status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame,
@@ -185,6 +212,121 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame,
return OK;
}
+status_t FrameProcessor::process3aState(const CameraMetadata &frame,
+ const sp<Camera2Client> &client) {
+
+ ATRACE_CALL();
+ camera_metadata_ro_entry_t entry;
+ int cameraId = client->getCameraId();
+
+ entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+ int32_t frameNumber = entry.data.i32[0];
+
+ // Don't send 3A notifications for the same frame number twice
+ if (frameNumber <= mLast3AFrameNumber) {
+ ALOGV("%s: Already sent 3A for frame number %d, skipping",
+ __FUNCTION__, frameNumber);
+ return OK;
+ }
+
+ mLast3AFrameNumber = frameNumber;
+
+ // Get 3A states from result metadata
+ bool gotAllStates = true;
+
+ AlgState new3aState;
+
+ // TODO: Also use AE mode, AE trigger ID
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_MODE,
+ &new3aState.afMode, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_MODE,
+ &new3aState.awbMode, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AE_STATE,
+ &new3aState.aeState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_STATE,
+ &new3aState.afState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_STATE,
+ &new3aState.awbState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &new3aState.afTriggerId, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &new3aState.aeTriggerId, frameNumber, cameraId);
+
+ if (!gotAllStates) return BAD_VALUE;
+
+ if (new3aState.aeState != m3aState.aeState) {
+ ALOGV("%s: Camera %d: AE state %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.aeState, new3aState.aeState);
+ client->notifyAutoExposure(new3aState.aeState, new3aState.aeTriggerId);
+ }
+
+ if (new3aState.afState != m3aState.afState ||
+ new3aState.afMode != m3aState.afMode ||
+ new3aState.afTriggerId != m3aState.afTriggerId) {
+ ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.afState, new3aState.afState,
+ m3aState.afMode, new3aState.afMode,
+ m3aState.afTriggerId, new3aState.afTriggerId);
+ client->notifyAutoFocus(new3aState.afState, new3aState.afTriggerId);
+ }
+ if (new3aState.awbState != m3aState.awbState ||
+ new3aState.awbMode != m3aState.awbMode) {
+ ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.awbState, new3aState.awbState,
+ m3aState.awbMode, new3aState.awbMode);
+ client->notifyAutoWhitebalance(new3aState.awbState,
+ new3aState.aeTriggerId);
+ }
+
+ m3aState = new3aState;
+
+ return OK;
+}
+
+template<typename Src, typename T>
+bool FrameProcessor::get3aResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber, int cameraId) {
+ camera_metadata_ro_entry_t entry;
+ if (value == NULL) {
+ ALOGE("%s: Camera %d: Value to write to is NULL",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: No %s provided by HAL for frame %d!",
+ __FUNCTION__, cameraId,
+ get_camera_metadata_tag_name(tag), frameNumber);
+ return false;
+ } else {
+ switch(sizeof(Src)){
+ case sizeof(uint8_t):
+ *value = static_cast<T>(entry.data.u8[0]);
+ break;
+ case sizeof(int32_t):
+ *value = static_cast<T>(entry.data.i32[0]);
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unsupported source",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+ }
+ return true;
+}
+
+
void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client,
const camera_frame_metadata &metadata) {
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
new file mode 100644
index 0000000..856ad32
--- /dev/null
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -0,0 +1,111 @@
+/*
+ * 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 <camera/CameraMetadata.h>
+
+#include "common/FrameProcessorBase.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 FrameProcessorBase {
+ public:
+ FrameProcessor(wp<CameraDeviceBase> device, sp<Camera2Client> client);
+ ~FrameProcessor();
+
+ private:
+ wp<Camera2Client> mClient;
+
+ bool mSynthesize3ANotify;
+
+ int mLastFrameNumberOfFaces;
+
+ void processNewFrames(const sp<Camera2Client> &client);
+
+ virtual bool processSingleFrame(CameraMetadata &frame,
+ const sp<CameraDeviceBase> &device);
+
+ status_t processFaceDetect(const CameraMetadata &frame,
+ const sp<Camera2Client> &client);
+
+ // Send 3A state change notifications to client based on frame metadata
+ status_t process3aState(const CameraMetadata &frame,
+ const sp<Camera2Client> &client);
+
+ // Helper for process3aState
+ template<typename Src, typename T>
+ bool get3aResult(const CameraMetadata& result, int32_t tag, T* value,
+ int32_t frameNumber, int cameraId);
+
+
+ struct AlgState {
+ // TODO: also track AE mode
+ camera_metadata_enum_android_control_af_mode afMode;
+ camera_metadata_enum_android_control_awb_mode awbMode;
+
+ camera_metadata_enum_android_control_ae_state aeState;
+ camera_metadata_enum_android_control_af_state afState;
+ camera_metadata_enum_android_control_awb_state awbState;
+
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ // These defaults need to match those in Parameters.cpp
+ AlgState() :
+ afMode(ANDROID_CONTROL_AF_MODE_AUTO),
+ awbMode(ANDROID_CONTROL_AWB_MODE_AUTO),
+ aeState(ANDROID_CONTROL_AE_STATE_INACTIVE),
+ afState(ANDROID_CONTROL_AF_STATE_INACTIVE),
+ awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE),
+ afTriggerId(0),
+ aeTriggerId(0) {
+ }
+ } m3aState;
+
+ // Whether the partial result quirk is enabled for this device
+ bool mUsePartialQuirk;
+
+ // Track most recent frame number for which 3A notifications were sent for.
+ // Used to filter against sending 3A notifications for the same frame
+ // several times.
+ int32_t mLast3AFrameNumber;
+
+ // Emit FaceDetection event to java if faces changed
+ void callbackFaceDetection(sp<Camera2Client> client,
+ const camera_frame_metadata &metadata);
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.cpp b/services/camera/libcameraservice/api1/client2/JpegCompressor.cpp
index c9af71e..2f0c67d 100644
--- a/services/camera/libcameraservice/camera2/JpegCompressor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegCompressor.cpp
@@ -210,7 +210,8 @@ boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr /*cinfo*/) {
return true;
}
-void JpegCompressor::jpegTermDestination(j_compress_ptr /*cinfo*/) {
+void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
+ (void) cinfo; // TODO: clean up
ALOGV("%s", __FUNCTION__);
ALOGV("%s: Done writing JPEG data. %d bytes left in buffer",
__FUNCTION__, cinfo->dest->free_in_buffer);
diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.h b/services/camera/libcameraservice/api1/client2/JpegCompressor.h
index 945b1de..945b1de 100644
--- a/services/camera/libcameraservice/camera2/JpegCompressor.h
+++ b/services/camera/libcameraservice/api1/client2/JpegCompressor.h
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index f0a13ca..77d5c8a 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -24,12 +24,13 @@
#include <binder/MemoryHeapBase.h>
#include <utils/Log.h>
#include <utils/Trace.h>
-
-#include "JpegProcessor.h"
#include <gui/Surface.h>
-#include "../CameraDeviceBase.h"
-#include "../Camera2Client.h"
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/Camera2Heap.h"
+#include "api1/client2/CaptureSequencer.h"
+#include "api1/client2/JpegProcessor.h"
namespace android {
namespace camera2 {
@@ -82,11 +83,11 @@ status_t JpegProcessor::updateStream(const Parameters &params) {
if (mCaptureConsumer == 0) {
// Create CPU buffer queue endpoint
- mCaptureConsumer = new CpuConsumer(1);
+ sp<BufferQueue> bq = new BufferQueue();
+ mCaptureConsumer = new CpuConsumer(bq, 1);
mCaptureConsumer->setFrameAvailableListener(this);
mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer"));
- mCaptureWindow = new Surface(
- mCaptureConsumer->getProducerInterface());
+ mCaptureWindow = new Surface(bq);
// Create memory for API consumption
mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0,
"Camera2Client::CaptureHeap");
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h
index a38611c..b2c05df 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h
@@ -23,7 +23,7 @@
#include <utils/Mutex.h>
#include <utils/Condition.h>
#include <gui/CpuConsumer.h>
-#include "Parameters.h"
+
#include "camera/CameraMetadata.h"
namespace android {
@@ -35,6 +35,7 @@ class MemoryHeapBase;
namespace camera2 {
class CaptureSequencer;
+class Parameters;
/***
* Still image capture output image processing
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index a248b76..08af566 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -58,13 +58,13 @@ status_t Parameters::initialize(const CameraMetadata *info) {
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;
+ const Size MAX_PREVIEW_SIZE = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+ res = getFilteredPreviewSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes);
+ if (res != OK) return res;
// TODO: Pick more intelligently
- previewWidth = availableProcessedSizes.data.i32[0];
- previewHeight = availableProcessedSizes.data.i32[1];
+ previewWidth = availablePreviewSizes[0].width;
+ previewHeight = availablePreviewSizes[0].height;
videoWidth = previewWidth;
videoHeight = previewHeight;
@@ -75,12 +75,13 @@ status_t Parameters::initialize(const CameraMetadata *info) {
previewWidth, previewHeight));
{
String8 supportedPreviewSizes;
- for (size_t i=0; i < availableProcessedSizes.count; i += 2) {
+ for (size_t i = 0; i < availablePreviewSizes.size(); i++) {
if (i != 0) supportedPreviewSizes += ",";
supportedPreviewSizes += String8::format("%dx%d",
- availableProcessedSizes.data.i32[i],
- availableProcessedSizes.data.i32[i+1]);
+ availablePreviewSizes[i].width,
+ availablePreviewSizes[i].height);
}
+ ALOGV("Supported preview sizes are: %s", supportedPreviewSizes.string());
params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
supportedPreviewSizes);
params.set(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES,
@@ -182,7 +183,7 @@ status_t Parameters::initialize(const CameraMetadata *info) {
// still have to do something sane for them
// NOTE: Not scaled like FPS range values are.
- previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
+ int previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
previewFps);
@@ -248,9 +249,17 @@ status_t Parameters::initialize(const CameraMetadata *info) {
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];
+ // Pick the largest thumbnail size that matches still image aspect ratio.
+ ALOG_ASSERT(pictureWidth > 0 && pictureHeight > 0,
+ "Invalid picture size, %d x %d", pictureWidth, pictureHeight);
+ float picAspectRatio = static_cast<float>(pictureWidth) / pictureHeight;
+ Size thumbnailSize =
+ getMaxSizeForRatio(
+ picAspectRatio,
+ &availableJpegThumbnailSizes.data.i32[0],
+ availableJpegThumbnailSizes.count);
+ jpegThumbSize[0] = thumbnailSize.width;
+ jpegThumbSize[1] = thumbnailSize.height;
params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
jpegThumbSize[0]);
@@ -292,8 +301,11 @@ status_t Parameters::initialize(const CameraMetadata *info) {
CameraParameters::WHITE_BALANCE_AUTO);
camera_metadata_ro_entry_t availableWhiteBalanceModes =
- staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
- {
+ staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES, 0, 0, false);
+ if (!availableWhiteBalanceModes.count) {
+ params.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE,
+ CameraParameters::WHITE_BALANCE_AUTO);
+ } else {
String8 supportedWhiteBalance;
bool addComma = false;
for (size_t i=0; i < availableWhiteBalanceModes.count; i++) {
@@ -353,9 +365,11 @@ status_t Parameters::initialize(const CameraMetadata *info) {
CameraParameters::EFFECT_NONE);
camera_metadata_ro_entry_t availableEffects =
- staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS);
- if (!availableEffects.count) return NO_INIT;
- {
+ staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS, 0, 0, false);
+ if (!availableEffects.count) {
+ params.set(CameraParameters::KEY_SUPPORTED_EFFECTS,
+ CameraParameters::EFFECT_NONE);
+ } else {
String8 supportedEffects;
bool addComma = false;
for (size_t i=0; i < availableEffects.count; i++) {
@@ -413,9 +427,11 @@ status_t Parameters::initialize(const CameraMetadata *info) {
CameraParameters::ANTIBANDING_AUTO);
camera_metadata_ro_entry_t availableAntibandingModes =
- staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES);
- if (!availableAntibandingModes.count) return NO_INIT;
- {
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, 0, 0, false);
+ if (!availableAntibandingModes.count) {
+ params.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING,
+ CameraParameters::ANTIBANDING_OFF);
+ } else {
String8 supportedAntibanding;
bool addComma = false;
for (size_t i=0; i < availableAntibandingModes.count; i++) {
@@ -455,9 +471,10 @@ status_t Parameters::initialize(const CameraMetadata *info) {
CameraParameters::SCENE_MODE_AUTO);
camera_metadata_ro_entry_t availableSceneModes =
- staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
- if (!availableSceneModes.count) return NO_INIT;
- {
+ staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, 0, 0, false);
+ if (!availableSceneModes.count) {
+ params.remove(CameraParameters::KEY_SCENE_MODE);
+ } else {
String8 supportedSceneModes(CameraParameters::SCENE_MODE_AUTO);
bool addComma = true;
bool noSceneModes = false;
@@ -548,15 +565,17 @@ status_t Parameters::initialize(const CameraMetadata *info) {
}
}
+ bool isFlashAvailable = false;
camera_metadata_ro_entry_t flashAvailable =
- staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 1, 1);
- if (!flashAvailable.count) return NO_INIT;
+ staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 0, 1, false);
+ if (flashAvailable.count) {
+ isFlashAvailable = flashAvailable.data.u8[0];
+ }
camera_metadata_ro_entry_t availableAeModes =
- staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES);
- if (!availableAeModes.count) return NO_INIT;
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES, 0, 0, false);
- if (flashAvailable.data.u8[0]) {
+ if (isFlashAvailable) {
flashMode = Parameters::FLASH_MODE_OFF;
params.set(CameraParameters::KEY_FLASH_MODE,
CameraParameters::FLASH_MODE_OFF);
@@ -585,14 +604,12 @@ status_t Parameters::initialize(const CameraMetadata *info) {
}
camera_metadata_ro_entry_t minFocusDistance =
- staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 1, 1);
- if (!minFocusDistance.count) return NO_INIT;
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0, 1, false);
camera_metadata_ro_entry_t availableAfModes =
- staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES);
- if (!availableAfModes.count) return NO_INIT;
+ staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES, 0, 0, false);
- if (minFocusDistance.data.f[0] == 0) {
+ if (!minFocusDistance.count || minFocusDistance.data.f[0] == 0) {
// Fixed-focus lens
focusMode = Parameters::FOCUS_MODE_FIXED;
params.set(CameraParameters::KEY_FOCUS_MODE,
@@ -662,7 +679,7 @@ status_t Parameters::initialize(const CameraMetadata *info) {
focusingAreas.add(Parameters::Area(0,0,0,0,0));
camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
if (!availableFocalLengths.count) return NO_INIT;
float minFocalLength = availableFocalLengths.data.f[0];
@@ -768,8 +785,8 @@ status_t Parameters::initialize(const CameraMetadata *info) {
CameraParameters::FALSE);
camera_metadata_ro_entry_t availableVideoStabilizationModes =
- staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
- if (!availableVideoStabilizationModes.count) return NO_INIT;
+ staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, 0, 0,
+ false);
if (availableVideoStabilizationModes.count > 1) {
params.set(CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED,
@@ -787,29 +804,25 @@ status_t Parameters::initialize(const CameraMetadata *info) {
enableFocusMoveMessages = false;
afTriggerCounter = 1;
+ afStateCounter = 0;
currentAfTriggerId = -1;
afInMotion = false;
precaptureTriggerCounter = 1;
+ takePictureCounter = 0;
+
previewCallbackFlags = 0;
previewCallbackOneShot = false;
+ previewCallbackSurface = false;
- camera_metadata_ro_entry_t supportedHardwareLevel =
- staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
- if (!supportedHardwareLevel.count || (supportedHardwareLevel.data.u8[0] ==
- ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)) {
- ALOGI("Camera %d: ZSL mode disabled for limited mode HALs", cameraId);
+ 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 {
- 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;
- }
+ zslMode = true;
}
lightFx = LIGHTFX_NONE;
@@ -828,14 +841,50 @@ String8 Parameters::get() const {
status_t Parameters::buildFastInfo() {
camera_metadata_ro_entry_t activeArraySize =
- staticInfo(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, 2, 2);
+ staticInfo(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, 2, 4);
if (!activeArraySize.count) return NO_INIT;
- int32_t arrayWidth = activeArraySize.data.i32[0];
- int32_t arrayHeight = activeArraySize.data.i32[1];
+ int32_t arrayWidth;
+ int32_t arrayHeight;
+ if (activeArraySize.count == 2) {
+ ALOGW("%s: Camera %d: activeArraySize is missing xmin/ymin!",
+ __FUNCTION__, cameraId);
+ arrayWidth = activeArraySize.data.i32[0];
+ arrayHeight = activeArraySize.data.i32[1];
+ } else if (activeArraySize.count == 4) {
+ arrayWidth = activeArraySize.data.i32[2];
+ arrayHeight = activeArraySize.data.i32[3];
+ } else return NO_INIT;
+
+ // We'll set the target FPS range for still captures to be as wide
+ // as possible to give the HAL maximum latitude for exposure selection
+ camera_metadata_ro_entry_t availableFpsRanges =
+ staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
+ if (availableFpsRanges.count < 2 || availableFpsRanges.count % 2 != 0) {
+ return NO_INIT;
+ }
+
+ int32_t bestStillCaptureFpsRange[2] = {
+ availableFpsRanges.data.i32[0], availableFpsRanges.data.i32[1]
+ };
+ int32_t curRange =
+ bestStillCaptureFpsRange[1] - bestStillCaptureFpsRange[0];
+ for (size_t i = 2; i < availableFpsRanges.count; i += 2) {
+ int32_t nextRange =
+ availableFpsRanges.data.i32[i + 1] -
+ availableFpsRanges.data.i32[i];
+ if ( (nextRange > curRange) || // Maximize size of FPS range first
+ (nextRange == curRange && // Then minimize low-end FPS
+ bestStillCaptureFpsRange[0] > availableFpsRanges.data.i32[i])) {
+
+ bestStillCaptureFpsRange[0] = availableFpsRanges.data.i32[i];
+ bestStillCaptureFpsRange[1] = availableFpsRanges.data.i32[i + 1];
+ curRange = nextRange;
+ }
+ }
camera_metadata_ro_entry_t availableFaceDetectModes =
- staticInfo(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
- if (!availableFaceDetectModes.count) return NO_INIT;
+ staticInfo(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, 0, 0,
+ false);
uint8_t bestFaceDetectMode =
ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
@@ -862,19 +911,21 @@ status_t Parameters::buildFastInfo() {
}
}
+ int32_t maxFaces = 0;
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];
+ staticInfo(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, 0, 1, false);
+ if (maxFacesDetected.count) {
+ maxFaces = maxFacesDetected.data.i32[0];
+ }
camera_metadata_ro_entry_t availableSceneModes =
- staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
+ staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES, 0, 0, false);
camera_metadata_ro_entry_t sceneModeOverrides =
- staticInfo(ANDROID_CONTROL_SCENE_MODE_OVERRIDES);
+ staticInfo(ANDROID_CONTROL_SCENE_MODE_OVERRIDES, 0, 0, false);
camera_metadata_ro_entry_t minFocusDistance =
- staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE);
- bool fixedLens = (minFocusDistance.data.f[0] == 0);
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0, 0, false);
+ bool fixedLens = minFocusDistance.count == 0 ||
+ minFocusDistance.data.f[0] == 0;
camera_metadata_ro_entry_t availableFocalLengths =
staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
@@ -950,6 +1001,8 @@ status_t Parameters::buildFastInfo() {
fastInfo.arrayWidth = arrayWidth;
fastInfo.arrayHeight = arrayHeight;
+ fastInfo.bestStillCaptureFpsRange[0] = bestStillCaptureFpsRange[0];
+ fastInfo.bestStillCaptureFpsRange[1] = bestStillCaptureFpsRange[1];
fastInfo.bestFaceDetectMode = bestFaceDetectMode;
fastInfo.maxFaces = maxFaces;
@@ -993,6 +1046,11 @@ status_t Parameters::buildQuirks() {
ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion"
" enabled", cameraId);
+ entry = info->find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ quirks.partialResults = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.partialResults, "Camera %d: Quirk usePartialResult"
+ " enabled", cameraId);
+
return OK;
}
@@ -1052,15 +1110,13 @@ status_t Parameters::set(const String8& paramString) {
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] ==
+ for (i = 0; i < availablePreviewSizes.size(); i++) {
+ if ((availablePreviewSizes[i].width ==
validatedParams.previewWidth) &&
- (availablePreviewSizes.data.i32[i+1] ==
+ (availablePreviewSizes[i].height ==
validatedParams.previewHeight)) break;
}
- if (i == availablePreviewSizes.count) {
+ if (i == availablePreviewSizes.size()) {
ALOGE("%s: Requested preview size %d x %d is not supported",
__FUNCTION__, validatedParams.previewWidth,
validatedParams.previewHeight);
@@ -1077,13 +1133,22 @@ status_t Parameters::set(const String8& paramString) {
// PREVIEW_FPS_RANGE
bool fpsRangeChanged = false;
+ int32_t lastSetFpsRange[2];
+
+ params.getPreviewFpsRange(&lastSetFpsRange[0], &lastSetFpsRange[1]);
+ lastSetFpsRange[0] /= kFpsToApiScale;
+ lastSetFpsRange[1] /= kFpsToApiScale;
+
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]) {
+ // Compare the FPS range value from the last set() to the current set()
+ // to determine if the client has changed it
+ if (validatedParams.previewFpsRange[0] != lastSetFpsRange[0] ||
+ validatedParams.previewFpsRange[1] != lastSetFpsRange[1]) {
+
fpsRangeChanged = true;
camera_metadata_ro_entry_t availablePreviewFpsRanges =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
@@ -1101,10 +1166,6 @@ status_t Parameters::set(const String8& paramString) {
validatedParams.previewFpsRange[1]);
return BAD_VALUE;
}
- validatedParams.previewFps =
- fpsFromRange(validatedParams.previewFpsRange[0],
- validatedParams.previewFpsRange[1]);
- newParams.setPreviewFrameRate(validatedParams.previewFps);
}
// PREVIEW_FORMAT
@@ -1139,12 +1200,14 @@ status_t Parameters::set(const String8& paramString) {
}
}
- // 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.
+ // 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. To detect whether the application has changed the value of
+ // previewFps, compare against their last-set preview FPS.
if (!fpsRangeChanged) {
- validatedParams.previewFps = newParams.getPreviewFrameRate();
- if (validatedParams.previewFps != previewFps || recordingHintChanged) {
+ int previewFps = newParams.getPreviewFrameRate();
+ int lastSetPreviewFps = params.getPreviewFrameRate();
+ if (previewFps != lastSetPreviewFps || recordingHintChanged) {
camera_metadata_ro_entry_t availableFrameRates =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
/**
@@ -1157,8 +1220,8 @@ status_t Parameters::set(const String8& paramString) {
* Either way, in case of multiple ranges, break the tie by
* selecting the smaller range.
*/
- int targetFps = validatedParams.previewFps;
- // all ranges which have targetFps
+
+ // all ranges which have previewFps
Vector<Range> candidateRanges;
for (i = 0; i < availableFrameRates.count; i+=2) {
Range r = {
@@ -1166,13 +1229,13 @@ status_t Parameters::set(const String8& paramString) {
availableFrameRates.data.i32[i+1]
};
- if (r.min <= targetFps && targetFps <= r.max) {
+ if (r.min <= previewFps && previewFps <= r.max) {
candidateRanges.push(r);
}
}
if (candidateRanges.isEmpty()) {
ALOGE("%s: Requested preview frame rate %d is not supported",
- __FUNCTION__, validatedParams.previewFps);
+ __FUNCTION__, previewFps);
return BAD_VALUE;
}
// most applicable range with targetFps
@@ -1211,11 +1274,6 @@ status_t Parameters::set(const String8& paramString) {
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
@@ -1465,7 +1523,7 @@ status_t Parameters::set(const String8& paramString) {
}
if (validatedParams.wbMode != wbMode) {
camera_metadata_ro_entry_t availableWbModes =
- staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
+ staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES, 0, 0, false);
for (i = 0; i < availableWbModes.count; i++) {
if (validatedParams.wbMode == availableWbModes.data.u8[i]) break;
}
@@ -1496,8 +1554,9 @@ status_t Parameters::set(const String8& paramString) {
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) {
+ staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0, 0,
+ false);
+ if (minFocusDistance.count && minFocusDistance.data.f[0] == 0) {
ALOGE("%s: Requested focus mode \"%s\" is not available: "
"fixed focus lens",
__FUNCTION__,
@@ -1597,15 +1656,13 @@ status_t Parameters::set(const String8& paramString) {
__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] ==
+ for (i = 0; i < availablePreviewSizes.size(); i++) {
+ if ((availablePreviewSizes[i].width ==
validatedParams.videoWidth) &&
- (availableVideoSizes.data.i32[i+1] ==
+ (availablePreviewSizes[i].height ==
validatedParams.videoHeight)) break;
}
- if (i == availableVideoSizes.count) {
+ if (i == availablePreviewSizes.size()) {
ALOGE("%s: Requested video size %d x %d is not supported",
__FUNCTION__, validatedParams.videoWidth,
validatedParams.videoHeight);
@@ -1617,7 +1674,8 @@ status_t Parameters::set(const String8& paramString) {
validatedParams.videoStabilization = boolFromString(
newParams.get(CameraParameters::KEY_VIDEO_STABILIZATION) );
camera_metadata_ro_entry_t availableVideoStabilizationModes =
- staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES);
+ staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, 0, 0,
+ false);
if (validatedParams.videoStabilization &&
availableVideoStabilizationModes.count == 1) {
ALOGE("%s: Video stabilization not supported", __FUNCTION__);
@@ -1690,8 +1748,15 @@ status_t Parameters::updateRequest(CameraMetadata *request) const {
&metadataMode, 1);
if (res != OK) return res;
- res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
- previewFpsRange, 2);
+ camera_metadata_entry_t intent =
+ request->find(ANDROID_CONTROL_CAPTURE_INTENT);
+ if (intent.data.u8[0] == ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE) {
+ res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ fastInfo.bestStillCaptureFpsRange, 2);
+ } else {
+ res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ previewFpsRange, 2);
+ }
if (res != OK) return res;
uint8_t reqWbLock = autoWhiteBalanceLock ?
@@ -2425,6 +2490,64 @@ int Parameters::normalizedYToArray(int y) const {
return cropYToArray(normalizedYToCrop(y));
}
+status_t Parameters::getFilteredPreviewSizes(Size limit, Vector<Size> *sizes) {
+ if (info == NULL) {
+ ALOGE("%s: Static metadata is not initialized", __FUNCTION__);
+ return NO_INIT;
+ }
+ if (sizes == NULL) {
+ ALOGE("%s: Input size is null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
+ camera_metadata_ro_entry_t availableProcessedSizes =
+ staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
+ if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
+
+ Size previewSize;
+ for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
+ previewSize.width = availableProcessedSizes.data.i32[i];
+ previewSize.height = availableProcessedSizes.data.i32[i+1];
+ // Need skip the preview sizes that are too large.
+ if (previewSize.width <= limit.width &&
+ previewSize.height <= limit.height) {
+ sizes->push(previewSize);
+ }
+ }
+ if (sizes->isEmpty()) {
+ ALOGE("generated preview size list is empty!!");
+ return BAD_VALUE;
+ }
+ return OK;
+}
+
+Parameters::Size Parameters::getMaxSizeForRatio(
+ float ratio, const int32_t* sizeArray, size_t count) {
+ ALOG_ASSERT(sizeArray != NULL, "size array shouldn't be NULL");
+ ALOG_ASSERT(count >= 2 && count % 2 == 0, "count must be a positive even number");
+
+ Size maxSize = {0, 0};
+ for (size_t i = 0; i < count; i += 2) {
+ if (sizeArray[i] > 0 && sizeArray[i+1] > 0) {
+ float curRatio = static_cast<float>(sizeArray[i]) / sizeArray[i+1];
+ if (fabs(curRatio - ratio) < ASPECT_RATIO_TOLERANCE && maxSize.width < sizeArray[i]) {
+ maxSize.width = sizeArray[i];
+ maxSize.height = sizeArray[i+1];
+ }
+ }
+ }
+
+ if (maxSize.width == 0 || maxSize.height == 0) {
+ maxSize.width = sizeArray[0];
+ maxSize.height = sizeArray[1];
+ ALOGW("Unable to find the size to match the given aspect ratio %f."
+ "Fall back to %d x %d", ratio, maxSize.width, maxSize.height);
+ }
+
+ return maxSize;
+}
+
Parameters::CropRegion Parameters::calculateCropRegion(
Parameters::CropRegion::Outputs outputs) const {
@@ -2544,10 +2667,6 @@ status_t Parameters::calculatePictureFovs(float *horizFov, float *vertFov)
staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2);
if (!sensorSize.count) return NO_INIT;
- camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
- if (!availableFocalLengths.count) return NO_INIT;
-
float arrayAspect = static_cast<float>(fastInfo.arrayWidth) /
fastInfo.arrayHeight;
float stillAspect = static_cast<float>(pictureWidth) / pictureHeight;
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index be05b54..32dbd42 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -46,7 +46,6 @@ struct Parameters {
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
@@ -105,6 +104,11 @@ struct Parameters {
};
Vector<Area> focusingAreas;
+ struct Size {
+ int32_t width;
+ int32_t height;
+ };
+
int32_t exposureCompensation;
bool autoExposureLock;
bool autoWhiteBalanceLock;
@@ -135,13 +139,17 @@ struct Parameters {
bool enableFocusMoveMessages;
int afTriggerCounter;
+ int afStateCounter;
int currentAfTriggerId;
bool afInMotion;
int precaptureTriggerCounter;
+ int takePictureCounter;
+
uint32_t previewCallbackFlags;
bool previewCallbackOneShot;
+ bool previewCallbackSurface;
bool zslMode;
@@ -158,6 +166,11 @@ struct Parameters {
// Number of zoom steps to simulate
static const unsigned int NUM_ZOOM_STEPS = 100;
+ // Max preview size allowed
+ static const unsigned int MAX_PREVIEW_WIDTH = 1920;
+ static const unsigned int MAX_PREVIEW_HEIGHT = 1080;
+ // Aspect ratio tolerance
+ static const float ASPECT_RATIO_TOLERANCE = 0.001;
// Full static camera info, object owned by someone else, such as
// Camera2Device.
@@ -170,6 +183,7 @@ struct Parameters {
struct DeviceInfo {
int32_t arrayWidth;
int32_t arrayHeight;
+ int32_t bestStillCaptureFpsRange[2];
uint8_t bestFaceDetectMode;
int32_t maxFaces;
struct OverrideModes {
@@ -193,6 +207,7 @@ struct Parameters {
bool triggerAfWithAuto;
bool useZslFormat;
bool meteringCropRegion;
+ bool partialResults;
} quirks;
/**
@@ -316,6 +331,12 @@ private:
int cropYToNormalized(int y) const;
int normalizedXToCrop(int x) const;
int normalizedYToCrop(int y) const;
+
+ Vector<Size> availablePreviewSizes;
+ // Get size list (that are no larger than limit) from static metadata.
+ status_t getFilteredPreviewSizes(Size limit, Vector<Size> *sizes);
+ // Get max size (from the size array) that matches the given aspect ratio.
+ Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
};
// This class encapsulates the Parameters class so that it can only be accessed
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index f7a6be7..6076dae 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -30,10 +30,10 @@
#include <gui/Surface.h>
#include <media/hardware/MetadataBufferType.h>
-#include "StreamingProcessor.h"
-#include "Camera2Heap.h"
-#include "../Camera2Client.h"
-#include "../CameraDeviceBase.h"
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/StreamingProcessor.h"
+#include "api1/client2/Camera2Heap.h"
namespace android {
namespace camera2 {
@@ -319,14 +319,13 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters &params) {
// 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(
+ sp<BufferQueue> bq = new BufferQueue();
+ mRecordingConsumer = new BufferItemConsumer(bq,
GRALLOC_USAGE_HW_VIDEO_ENCODER,
- mRecordingHeapCount + 1,
- true);
+ mRecordingHeapCount + 1);
mRecordingConsumer->setFrameAvailableListener(this);
mRecordingConsumer->setName(String8("Camera2-RecordingConsumer"));
- mRecordingWindow = new Surface(
- mRecordingConsumer->getProducerInterface());
+ mRecordingWindow = new Surface(bq);
newConsumer = true;
// Allocate memory later, since we don't know buffer size until receipt
}
@@ -413,7 +412,7 @@ int StreamingProcessor::getRecordingStreamId() const {
}
status_t StreamingProcessor::startStream(StreamType type,
- const Vector<uint8_t> &outputStreams) {
+ const Vector<int32_t> &outputStreams) {
ATRACE_CALL();
status_t res;
@@ -617,7 +616,7 @@ status_t StreamingProcessor::processRecordingFrame() {
if (client == 0) {
// Discard frames during shutdown
BufferItemConsumer::BufferItem imgBuffer;
- res = mRecordingConsumer->acquireBuffer(&imgBuffer);
+ res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0);
if (res != OK) {
if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
ALOGE("%s: Camera %d: Can't acquire recording buffer: %s (%d)",
@@ -635,7 +634,7 @@ status_t StreamingProcessor::processRecordingFrame() {
SharedParameters::Lock l(client->getParameters());
Mutex::Autolock m(mMutex);
BufferItemConsumer::BufferItem imgBuffer;
- res = mRecordingConsumer->acquireBuffer(&imgBuffer);
+ res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0);
if (res != OK) {
if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
ALOGE("%s: Camera %d: Can't acquire recording buffer: %s (%d)",
@@ -831,8 +830,8 @@ void StreamingProcessor::releaseAllRecordingFramesLocked() {
mRecordingHeapFree = mRecordingHeapCount;
}
-bool StreamingProcessor::isStreamActive(const Vector<uint8_t> &streams,
- uint8_t recordingStreamId) {
+bool StreamingProcessor::isStreamActive(const Vector<int32_t> &streams,
+ int32_t recordingStreamId) {
for (size_t i = 0; i < streams.size(); i++) {
if (streams[i] == recordingStreamId) {
return true;
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
index 3ec2df7..833bb8f 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
@@ -21,7 +21,6 @@
#include <utils/String16.h>
#include <gui/BufferItemConsumer.h>
-#include "Parameters.h"
#include "camera/CameraMetadata.h"
namespace android {
@@ -32,6 +31,7 @@ class IMemory;
namespace camera2 {
+class Parameters;
class Camera2Heap;
/**
@@ -64,7 +64,7 @@ class StreamingProcessor:
RECORD
};
status_t startStream(StreamType type,
- const Vector<uint8_t> &outputStreams);
+ const Vector<int32_t> &outputStreams);
// Toggle between paused and unpaused. Stream must be started first.
status_t togglePauseStream(bool pause);
@@ -97,7 +97,7 @@ class StreamingProcessor:
StreamType mActiveRequest;
bool mPaused;
- Vector<uint8_t> mActiveStreamIds;
+ Vector<int32_t> mActiveStreamIds;
// Preview-related members
int32_t mPreviewRequestId;
@@ -132,8 +132,8 @@ class StreamingProcessor:
void releaseAllRecordingFramesLocked();
// Determine if the specified stream is currently in use
- static bool isStreamActive(const Vector<uint8_t> &streams,
- uint8_t recordingStreamId);
+ static bool isStreamActive(const Vector<int32_t> &streams,
+ int32_t recordingStreamId);
};
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 94059cd..4207ba9 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -27,12 +27,12 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-
-#include "ZslProcessor.h"
#include <gui/Surface.h>
-#include "../CameraDeviceBase.h"
-#include "../Camera2Client.h"
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/CaptureSequencer.h"
+#include "api1/client2/ZslProcessor.h"
namespace android {
namespace camera2 {
@@ -71,7 +71,7 @@ void ZslProcessor::onFrameAvailable() {
}
}
-void ZslProcessor::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor::onFrameAvailable(int32_t /*requestId*/,
const CameraMetadata &frame) {
Mutex::Autolock l(mInputMutex);
camera_metadata_ro_entry_t entry;
@@ -128,14 +128,13 @@ status_t ZslProcessor::updateStream(const Parameters &params) {
if (mZslConsumer == 0) {
// Create CPU buffer queue endpoint
- mZslConsumer = new BufferItemConsumer(
+ sp<BufferQueue> bq = new BufferQueue();
+ mZslConsumer = new BufferItemConsumer(bq,
GRALLOC_USAGE_HW_CAMERA_ZSL,
- kZslBufferDepth,
- true);
+ kZslBufferDepth);
mZslConsumer->setFrameAvailableListener(this);
mZslConsumer->setName(String8("Camera2Client::ZslConsumer"));
- mZslWindow = new Surface(
- mZslConsumer->getProducerInterface());
+ mZslWindow = new Surface(bq);
}
if (mZslStreamId != NO_STREAM) {
@@ -301,12 +300,12 @@ status_t ZslProcessor::pushToReprocess(int32_t requestId) {
uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
res = request.update(ANDROID_REQUEST_TYPE,
&requestType, 1);
- uint8_t inputStreams[1] =
- { static_cast<uint8_t>(mZslReprocessStreamId) };
+ int32_t inputStreams[1] =
+ { mZslReprocessStreamId };
if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
inputStreams, 1);
- uint8_t outputStreams[1] =
- { static_cast<uint8_t>(client->getCaptureStreamId()) };
+ int32_t outputStreams[1] =
+ { client->getCaptureStreamId() };
if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
outputStreams, 1);
res = request.update(ANDROID_REQUEST_ID,
@@ -426,7 +425,7 @@ status_t ZslProcessor::processNewZslBuffer() {
}
ALOGVV("Trying to get next buffer");
BufferItemConsumer::BufferItem item;
- res = zslConsumer->acquireBuffer(&item);
+ res = zslConsumer->acquireBuffer(&item, 0);
if (res != OK) {
if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) {
ALOGE("%s: Camera %d: Error receiving ZSL image buffer: "
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
index 27b597e..6d3cb85 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h
@@ -23,12 +23,11 @@
#include <utils/Mutex.h>
#include <utils/Condition.h>
#include <gui/BufferItemConsumer.h>
-#include "Parameters.h"
-#include "FrameProcessor.h"
-#include "camera/CameraMetadata.h"
-#include "Camera2Heap.h"
-#include "../CameraDeviceBase.h"
-#include "ZslProcessorInterface.h"
+#include <camera/CameraMetadata.h>
+
+#include "common/CameraDeviceBase.h"
+#include "api1/client2/ZslProcessorInterface.h"
+#include "api1/client2/FrameProcessor.h"
namespace android {
@@ -37,6 +36,7 @@ class Camera2Client;
namespace camera2 {
class CaptureSequencer;
+class Parameters;
/***
* ZSL queue processing
@@ -54,7 +54,7 @@ class ZslProcessor:
// From mZslConsumer
virtual void onFrameAvailable();
// From FrameProcessor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
virtual void onBufferReleased(buffer_handle_t *handle);
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 40c77df..776ebe2 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -27,13 +27,13 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-
-#include "ZslProcessor3.h"
#include <gui/Surface.h>
-#include "../CameraDeviceBase.h"
-#include "../Camera3Device.h"
-#include "../Camera2Client.h"
+#include "common/CameraDeviceBase.h"
+#include "api1/Camera2Client.h"
+#include "api1/client2/CaptureSequencer.h"
+#include "api1/client2/ZslProcessor3.h"
+#include "device3/Camera3Device.h"
namespace android {
namespace camera2 {
@@ -61,7 +61,7 @@ ZslProcessor3::~ZslProcessor3() {
deleteStream();
}
-void ZslProcessor3::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor3::onFrameAvailable(int32_t /*requestId*/,
const CameraMetadata &frame) {
Mutex::Autolock l(mInputMutex);
camera_metadata_ro_entry_t entry;
@@ -247,13 +247,13 @@ status_t ZslProcessor3::pushToReprocess(int32_t requestId) {
uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
res = request.update(ANDROID_REQUEST_TYPE,
&requestType, 1);
- uint8_t inputStreams[1] =
- { static_cast<uint8_t>(mZslStreamId) };
+ int32_t inputStreams[1] =
+ { mZslStreamId };
if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
inputStreams, 1);
// TODO: Shouldn't we also update the latest preview frame?
- uint8_t outputStreams[1] =
- { static_cast<uint8_t>(client->getCaptureStreamId()) };
+ int32_t outputStreams[1] =
+ { client->getCaptureStreamId() };
if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
outputStreams, 1);
res = request.update(ANDROID_REQUEST_ID,
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index cb98b99..d2f8322 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -23,13 +23,11 @@
#include <utils/Mutex.h>
#include <utils/Condition.h>
#include <gui/BufferItemConsumer.h>
-#include "Parameters.h"
-#include "FrameProcessor.h"
-#include "camera/CameraMetadata.h"
-#include "Camera2Heap.h"
-#include "../CameraDeviceBase.h"
-#include "ZslProcessorInterface.h"
-#include "../camera3/Camera3ZslStream.h"
+#include <camera/CameraMetadata.h>
+
+#include "api1/client2/FrameProcessor.h"
+#include "api1/client2/ZslProcessorInterface.h"
+#include "device3/Camera3ZslStream.h"
namespace android {
@@ -38,6 +36,7 @@ class Camera2Client;
namespace camera2 {
class CaptureSequencer;
+class Parameters;
/***
* ZSL queue processing
@@ -52,7 +51,7 @@ class ZslProcessor3 :
~ZslProcessor3();
// From FrameProcessor
- virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+ virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
/**
****************************************
diff --git a/services/camera/libcameraservice/camera2/ZslProcessorInterface.h b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h
index 183c0c2..183c0c2 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessorInterface.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
new file mode 100644
index 0000000..1cdf8dc
--- /dev/null
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2013 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 "CameraDeviceClient"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+// #define LOG_NDEBUG 0
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <gui/Surface.h>
+#include <camera/camera2/CaptureRequest.h>
+
+#include "common/CameraDeviceBase.h"
+#include "api2/CameraDeviceClient.h"
+
+
+
+namespace android {
+using namespace camera2;
+
+CameraDeviceClientBase::CameraDeviceClientBase(
+ const sp<CameraService>& cameraService,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ uid_t clientUid,
+ int servicePid) :
+ BasicClient(cameraService, remoteCallback->asBinder(), clientPackageName,
+ cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ mRemoteCallback(remoteCallback) {
+}
+
+// Interface used by CameraService
+
+CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ uid_t clientUid,
+ int servicePid) :
+ Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
+ cameraId, cameraFacing, clientPid, clientUid, servicePid),
+ mRequestIdCounter(0) {
+
+ ATRACE_CALL();
+ ALOGI("CameraDeviceClient %d: Opened", cameraId);
+}
+
+status_t CameraDeviceClient::initialize(camera_module_t *module)
+{
+ ATRACE_CALL();
+ status_t res;
+
+ res = Camera2ClientBase::initialize(module);
+ if (res != OK) {
+ return res;
+ }
+
+ String8 threadName;
+ mFrameProcessor = new FrameProcessorBase(mDevice);
+ threadName = String8::format("CDU-%d-FrameProc", mCameraId);
+ mFrameProcessor->run(threadName.string());
+
+ mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+ FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this,
+ /*quirkSendPartials*/true);
+
+ return OK;
+}
+
+CameraDeviceClient::~CameraDeviceClient() {
+}
+
+status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request,
+ bool streaming) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res;
+
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ if (request == 0) {
+ ALOGE("%s: Camera %d: Sent null request. Rejecting request.",
+ __FUNCTION__, mCameraId);
+ return BAD_VALUE;
+ }
+
+ CameraMetadata metadata(request->mMetadata);
+
+ if (metadata.isEmpty()) {
+ ALOGE("%s: Camera %d: Sent empty metadata packet. Rejecting request.",
+ __FUNCTION__, mCameraId);
+ return BAD_VALUE;
+ } else if (request->mSurfaceList.size() == 0) {
+ ALOGE("%s: Camera %d: Requests must have at least one surface target. "
+ "Rejecting request.", __FUNCTION__, mCameraId);
+ return BAD_VALUE;
+ }
+
+ if (!enforceRequestPermissions(metadata)) {
+ // Callee logs
+ return PERMISSION_DENIED;
+ }
+
+ /**
+ * Write in the output stream IDs which we calculate from
+ * the capture request's list of surface targets
+ */
+ Vector<int32_t> outputStreamIds;
+ outputStreamIds.setCapacity(request->mSurfaceList.size());
+ for (size_t i = 0; i < request->mSurfaceList.size(); ++i) {
+ sp<Surface> surface = request->mSurfaceList[i];
+
+ if (surface == 0) continue;
+
+ sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer();
+ int idx = mStreamMap.indexOfKey(gbp->asBinder());
+
+ // Trying to submit request with surface that wasn't created
+ if (idx == NAME_NOT_FOUND) {
+ ALOGE("%s: Camera %d: Tried to submit a request with a surface that"
+ " we have not called createStream on",
+ __FUNCTION__, mCameraId);
+ return BAD_VALUE;
+ }
+
+ int streamId = mStreamMap.valueAt(idx);
+ outputStreamIds.push_back(streamId);
+ ALOGV("%s: Camera %d: Appending output stream %d to request",
+ __FUNCTION__, mCameraId, streamId);
+ }
+
+ metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
+ outputStreamIds.size());
+
+ int32_t requestId = mRequestIdCounter++;
+ metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
+ ALOGV("%s: Camera %d: Submitting request with ID %d",
+ __FUNCTION__, mCameraId, requestId);
+
+ if (streaming) {
+ res = mDevice->setStreamingRequest(metadata);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Got error %d after trying to set streaming "
+ "request", __FUNCTION__, mCameraId, res);
+ } else {
+ mStreamingRequestList.push_back(requestId);
+ }
+ } else {
+ res = mDevice->capture(metadata);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Got error %d after trying to set capture",
+ __FUNCTION__, mCameraId, res);
+ }
+ }
+
+ ALOGV("%s: Camera %d: End of function", __FUNCTION__, mCameraId);
+ if (res == OK) {
+ return requestId;
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::cancelRequest(int requestId) {
+ ATRACE_CALL();
+ ALOGV("%s, requestId = %d", __FUNCTION__, requestId);
+
+ status_t res;
+
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ Vector<int>::iterator it, end;
+ for (it = mStreamingRequestList.begin(), end = mStreamingRequestList.end();
+ it != end; ++it) {
+ if (*it == requestId) {
+ break;
+ }
+ }
+
+ if (it == end) {
+ ALOGE("%s: Camera%d: Did not find request id %d in list of streaming "
+ "requests", __FUNCTION__, mCameraId, requestId);
+ return BAD_VALUE;
+ }
+
+ res = mDevice->clearStreamingRequest();
+
+ if (res == OK) {
+ ALOGV("%s: Camera %d: Successfully cleared streaming request",
+ __FUNCTION__, mCameraId);
+ mStreamingRequestList.erase(it);
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::deleteStream(int streamId) {
+ ATRACE_CALL();
+ ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ // Guard against trying to delete non-created streams
+ ssize_t index = NAME_NOT_FOUND;
+ for (size_t i = 0; i < mStreamMap.size(); ++i) {
+ if (streamId == mStreamMap.valueAt(i)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == NAME_NOT_FOUND) {
+ ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream "
+ "created yet", __FUNCTION__, mCameraId, streamId);
+ return BAD_VALUE;
+ }
+
+ // Also returns BAD_VALUE if stream ID was not valid
+ res = mDevice->deleteStream(streamId);
+
+ if (res == BAD_VALUE) {
+ ALOGE("%s: Camera %d: Unexpected BAD_VALUE when deleting stream, but we"
+ " already checked and the stream ID (%d) should be valid.",
+ __FUNCTION__, mCameraId, streamId);
+ } else if (res == OK) {
+ mStreamMap.removeItemsAt(index);
+
+ ALOGV("%s: Camera %d: Successfully deleted stream ID (%d)",
+ __FUNCTION__, mCameraId, streamId);
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::createStream(int width, int height, int format,
+ const sp<IGraphicBufferProducer>& bufferProducer)
+{
+ ATRACE_CALL();
+ ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ // Don't create multiple streams for the same target surface
+ {
+ ssize_t index = mStreamMap.indexOfKey(bufferProducer->asBinder());
+ if (index != NAME_NOT_FOUND) {
+ ALOGW("%s: Camera %d: Buffer producer already has a stream for it "
+ "(ID %d)",
+ __FUNCTION__, mCameraId, index);
+ return ALREADY_EXISTS;
+ }
+ }
+
+ // HACK b/10949105
+ // Query consumer usage bits to set async operation mode for
+ // GLConsumer using controlledByApp parameter.
+ bool useAsync = false;
+ int32_t consumerUsage;
+ if ((res = bufferProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+ &consumerUsage)) != OK) {
+ ALOGE("%s: Camera %d: Failed to query consumer usage", __FUNCTION__,
+ mCameraId);
+ return res;
+ }
+ if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
+ ALOGW("%s: Camera %d: Forcing asynchronous mode for stream",
+ __FUNCTION__, mCameraId);
+ useAsync = true;
+ }
+
+ sp<IBinder> binder;
+ sp<ANativeWindow> anw;
+ if (bufferProducer != 0) {
+ binder = bufferProducer->asBinder();
+ anw = new Surface(bufferProducer, useAsync);
+ }
+
+ // TODO: remove w,h,f since we are ignoring them
+
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
+ ALOGE("%s: Camera %d: Failed to query Surface width", __FUNCTION__,
+ mCameraId);
+ return res;
+ }
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
+ ALOGE("%s: Camera %d: Failed to query Surface height", __FUNCTION__,
+ mCameraId);
+ return res;
+ }
+ if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &format)) != OK) {
+ ALOGE("%s: Camera %d: Failed to query Surface format", __FUNCTION__,
+ mCameraId);
+ return res;
+ }
+
+ // FIXME: remove this override since the default format should be
+ // IMPLEMENTATION_DEFINED. b/9487482
+ if (format >= HAL_PIXEL_FORMAT_RGBA_8888 &&
+ format <= HAL_PIXEL_FORMAT_BGRA_8888) {
+ ALOGW("%s: Camera %d: Overriding format 0x%x to IMPLEMENTATION_DEFINED",
+ __FUNCTION__, mCameraId, format);
+ format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ }
+
+ // TODO: add startConfigure/stopConfigure call to CameraDeviceBase
+ // this will make it so Camera3Device doesn't call configure_streams
+ // after each call, but only once we are done with all.
+
+ int streamId = -1;
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ // JPEG buffers need to be sized for maximum possible compressed size
+ CameraMetadata staticInfo = mDevice->info();
+ camera_metadata_entry_t entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Can't find maximum JPEG size in "
+ "static metadata!", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+ int32_t maxJpegSize = entry.data.i32[0];
+ res = mDevice->createStream(anw, width, height, format, maxJpegSize,
+ &streamId);
+ } else {
+ // All other streams are a known size
+ res = mDevice->createStream(anw, width, height, format, /*size*/0,
+ &streamId);
+ }
+
+ if (res == OK) {
+ mStreamMap.add(bufferProducer->asBinder(), streamId);
+
+ ALOGV("%s: Camera %d: Successfully created a new stream ID %d",
+ __FUNCTION__, mCameraId, streamId);
+
+ /**
+ * Set the stream transform flags to automatically
+ * rotate the camera stream for preview use cases.
+ */
+ int32_t transform = 0;
+ res = getRotationTransformLocked(&transform);
+
+ if (res != OK) {
+ // Error logged by getRotationTransformLocked.
+ return res;
+ }
+
+ res = mDevice->setStreamTransform(streamId, transform);
+ if (res != OK) {
+ ALOGE("%s: Failed to set stream transform (stream id %d)",
+ __FUNCTION__, streamId);
+ return res;
+ }
+
+ return streamId;
+ }
+
+ return res;
+}
+
+// Create a request object from a template.
+status_t CameraDeviceClient::createDefaultRequest(int templateId,
+ /*out*/
+ CameraMetadata* request)
+{
+ ATRACE_CALL();
+ ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId);
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ CameraMetadata metadata;
+ if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK &&
+ request != NULL) {
+
+ request->swap(metadata);
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::getCameraInfo(/*out*/CameraMetadata* info)
+{
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res = OK;
+
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ if (info != NULL) {
+ *info = mDevice->info(); // static camera metadata
+ // TODO: merge with device-specific camera metadata
+ }
+
+ return res;
+}
+
+status_t CameraDeviceClient::waitUntilIdle()
+{
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res = OK;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ // FIXME: Also need check repeating burst.
+ if (!mStreamingRequestList.isEmpty()) {
+ ALOGE("%s: Camera %d: Try to waitUntilIdle when there are active streaming requests",
+ __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+ res = mDevice->waitUntilDrained();
+ ALOGV("%s Done", __FUNCTION__);
+
+ return res;
+}
+
+status_t CameraDeviceClient::flush() {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res = OK;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ return mDevice->flush();
+}
+
+status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
+ String8 result;
+ result.appendFormat("CameraDeviceClient[%d] (%p) PID: %d, dump:\n",
+ mCameraId,
+ getRemoteCallback()->asBinder().get(),
+ mClientPid);
+ result.append(" State: ");
+
+ // TODO: print dynamic/request section from most recent requests
+ mFrameProcessor->dump(fd, args);
+
+ return dumpDevice(fd, args);
+}
+
+
+void CameraDeviceClient::notifyError() {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+ if (remoteCb != 0) {
+ remoteCb->onDeviceError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE);
+ }
+}
+
+void CameraDeviceClient::notifyIdle() {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+ if (remoteCb != 0) {
+ remoteCb->onDeviceIdle();
+ }
+}
+
+void CameraDeviceClient::notifyShutter(int requestId,
+ nsecs_t timestamp) {
+ // Thread safe. Don't bother locking.
+ sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+ if (remoteCb != 0) {
+ remoteCb->onCaptureStarted(requestId, timestamp);
+ }
+}
+
+// TODO: refactor the code below this with IProCameraUser.
+// it's 100% copy-pasted, so lets not change it right now to make it easier.
+
+void CameraDeviceClient::detachDevice() {
+ if (mDevice == 0) return;
+
+ ALOGV("Camera %d: Stopping processors", mCameraId);
+
+ mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+ FRAME_PROCESSOR_LISTENER_MAX_ID,
+ /*listener*/this);
+ mFrameProcessor->requestExit();
+ ALOGV("Camera %d: Waiting for threads", mCameraId);
+ mFrameProcessor->join();
+ ALOGV("Camera %d: Disconnecting device", mCameraId);
+
+ // WORKAROUND: HAL refuses to disconnect while there's streams in flight
+ {
+ mDevice->clearStreamingRequest();
+
+ status_t code;
+ if ((code = mDevice->waitUntilDrained()) != OK) {
+ ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__,
+ code);
+ }
+ }
+
+ Camera2ClientBase::detachDevice();
+}
+
+/** Device-related methods */
+void CameraDeviceClient::onFrameAvailable(int32_t requestId,
+ const CameraMetadata& frame) {
+ ATRACE_CALL();
+ ALOGV("%s", __FUNCTION__);
+
+ // Thread-safe. No lock necessary.
+ sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
+ if (remoteCb != NULL) {
+ ALOGV("%s: frame = %p ", __FUNCTION__, &frame);
+ remoteCb->onResultReceived(requestId, frame);
+ }
+}
+
+// TODO: move to Camera2ClientBase
+bool CameraDeviceClient::enforceRequestPermissions(CameraMetadata& metadata) {
+
+ const int pid = IPCThreadState::self()->getCallingPid();
+ const int selfPid = getpid();
+ camera_metadata_entry_t entry;
+
+ /**
+ * Mixin default important security values
+ * - android.led.transmit = defaulted ON
+ */
+ CameraMetadata staticInfo = mDevice->info();
+ entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS);
+ for(size_t i = 0; i < entry.count; ++i) {
+ uint8_t led = entry.data.u8[i];
+
+ switch(led) {
+ case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: {
+ uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON;
+ if (!metadata.exists(ANDROID_LED_TRANSMIT)) {
+ metadata.update(ANDROID_LED_TRANSMIT,
+ &transmitDefault, 1);
+ }
+ break;
+ }
+ }
+ }
+
+ // We can do anything!
+ if (pid == selfPid) {
+ return true;
+ }
+
+ /**
+ * Permission check special fields in the request
+ * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT
+ */
+ entry = metadata.find(ANDROID_LED_TRANSMIT);
+ if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) {
+ String16 permissionString =
+ String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED");
+ if (!checkCallingPermission(permissionString)) {
+ const int uid = IPCThreadState::self()->getCallingUid();
+ ALOGE("Permission Denial: "
+ "can't disable transmit LED pid=%d, uid=%d", pid, uid);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) {
+ ALOGV("%s: begin", __FUNCTION__);
+
+ if (transform == NULL) {
+ ALOGW("%s: null transform", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ *transform = 0;
+
+ const CameraMetadata& staticInfo = mDevice->info();
+ camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_SENSOR_ORIENTATION);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: Can't find android.sensor.orientation in "
+ "static metadata!", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
+
+ int32_t& flags = *transform;
+
+ int orientation = entry.data.i32[0];
+ switch (orientation) {
+ case 0:
+ flags = 0;
+ break;
+ case 90:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
+ break;
+ case 180:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
+ break;
+ case 270:
+ flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
+ break;
+ default:
+ ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
+ __FUNCTION__, orientation);
+ return INVALID_OPERATION;
+ }
+
+ /**
+ * This magic flag makes surfaceflinger un-rotate the buffers
+ * to counter the extra global device UI rotation whenever the user
+ * physically rotates the device.
+ *
+ * By doing this, the camera buffer always ends up aligned
+ * with the physical camera for a "see through" effect.
+ *
+ * In essence, the buffer only gets rotated during preview use-cases.
+ * The user is still responsible to re-create streams of the proper
+ * aspect ratio, or the preview will end up looking non-uniformly
+ * stretched.
+ */
+ flags |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+
+ ALOGV("%s: final transform = 0x%x", __FUNCTION__, flags);
+
+ return OK;
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
new file mode 100644
index 0000000..b9c16aa
--- /dev/null
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 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_PHOTOGRAPHY_CAMERADEVICECLIENT_H
+#define ANDROID_SERVERS_CAMERA_PHOTOGRAPHY_CAMERADEVICECLIENT_H
+
+#include <camera/camera2/ICameraDeviceUser.h>
+#include <camera/camera2/ICameraDeviceCallbacks.h>
+
+#include "CameraService.h"
+#include "common/FrameProcessorBase.h"
+#include "common/Camera2ClientBase.h"
+
+namespace android {
+
+struct CameraDeviceClientBase :
+ public CameraService::BasicClient, public BnCameraDeviceUser
+{
+ typedef ICameraDeviceCallbacks TCamCallbacks;
+
+ const sp<ICameraDeviceCallbacks>& getRemoteCallback() {
+ return mRemoteCallback;
+ }
+
+protected:
+ CameraDeviceClientBase(const sp<CameraService>& cameraService,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ uid_t clientUid,
+ int servicePid);
+
+ sp<ICameraDeviceCallbacks> mRemoteCallback;
+};
+
+/**
+ * Implements the binder ICameraDeviceUser API,
+ * meant for HAL3-public implementation of
+ * android.hardware.photography.CameraDevice
+ */
+class CameraDeviceClient :
+ public Camera2ClientBase<CameraDeviceClientBase>,
+ public camera2::FrameProcessorBase::FilteredListener
+{
+public:
+ /**
+ * ICameraDeviceUser interface (see ICameraDeviceUser for details)
+ */
+
+ // Note that the callee gets a copy of the metadata.
+ virtual int submitRequest(sp<CaptureRequest> request,
+ bool streaming = false);
+ virtual status_t cancelRequest(int requestId);
+
+ // Returns -EBUSY if device is not idle
+ virtual status_t deleteStream(int streamId);
+
+ virtual status_t createStream(
+ int width,
+ int height,
+ int format,
+ const sp<IGraphicBufferProducer>& bufferProducer);
+
+ // Create a request object from a template.
+ virtual status_t createDefaultRequest(int templateId,
+ /*out*/
+ CameraMetadata* request);
+
+ // Get the static metadata for the camera
+ // -- Caller owns the newly allocated metadata
+ virtual status_t getCameraInfo(/*out*/CameraMetadata* info);
+
+ // Wait until all the submitted requests have finished processing
+ virtual status_t waitUntilIdle();
+
+ // Flush all active and pending requests as fast as possible
+ virtual status_t flush();
+
+ /**
+ * Interface used by CameraService
+ */
+
+ CameraDeviceClient(const sp<CameraService>& cameraService,
+ const sp<ICameraDeviceCallbacks>& remoteCallback,
+ const String16& clientPackageName,
+ int cameraId,
+ int cameraFacing,
+ int clientPid,
+ uid_t clientUid,
+ int servicePid);
+ virtual ~CameraDeviceClient();
+
+ virtual status_t initialize(camera_module_t *module);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ /**
+ * Device listener interface
+ */
+
+ virtual void notifyIdle();
+ virtual void notifyError();
+ virtual void notifyShutter(int requestId, nsecs_t timestamp);
+
+ /**
+ * Interface used by independent components of CameraDeviceClient.
+ */
+protected:
+ /** FilteredListener implementation **/
+ virtual void onFrameAvailable(int32_t requestId,
+ const CameraMetadata& frame);
+ virtual void detachDevice();
+
+ // Calculate the ANativeWindow transform from android.sensor.orientation
+ status_t getRotationTransformLocked(/*out*/int32_t* transform);
+
+private:
+ /** ICameraDeviceUser interface-related private members */
+
+ /** Preview callback related members */
+ sp<camera2::FrameProcessorBase> mFrameProcessor;
+ static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
+ static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+
+ /** Utility members */
+ bool enforceRequestPermissions(CameraMetadata& metadata);
+
+ // IGraphicsBufferProducer binder -> Stream ID
+ KeyedVector<sp<IBinder>, int> mStreamMap;
+
+ // Stream ID
+ Vector<int> mStreamingRequestList;
+
+ int32_t mRequestIdCounter;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index 251fdab..1a7a7a7 100644
--- a/services/camera/libcameraservice/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -24,10 +24,9 @@
#include <cutils/properties.h>
#include <gui/Surface.h>
#include <gui/Surface.h>
-#include "camera2/Parameters.h"
-#include "ProCamera2Client.h"
-#include "camera2/ProFrameProcessor.h"
-#include "CameraDeviceBase.h"
+
+#include "api_pro/ProCamera2Client.h"
+#include "common/CameraDeviceBase.h"
namespace android {
using namespace camera2;
@@ -62,7 +61,7 @@ status_t ProCamera2Client::initialize(camera_module_t *module)
}
String8 threadName;
- mFrameProcessor = new ProFrameProcessor(mDevice);
+ mFrameProcessor = new FrameProcessorBase(mDevice);
threadName = String8::format("PC2-%d-FrameProc", mCameraId);
mFrameProcessor->run(threadName.string());
@@ -218,6 +217,7 @@ status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
}
status_t ProCamera2Client::cancelRequest(int requestId) {
+ (void)requestId;
ATRACE_CALL();
ALOGV("%s", __FUNCTION__);
@@ -374,7 +374,7 @@ void ProCamera2Client::detachDevice() {
}
/** Device-related methods */
-void ProCamera2Client::onFrameAvailable(int32_t frameId,
+void ProCamera2Client::onFrameAvailable(int32_t requestId,
const CameraMetadata& frame) {
ATRACE_CALL();
ALOGV("%s", __FUNCTION__);
@@ -386,7 +386,7 @@ void ProCamera2Client::onFrameAvailable(int32_t frameId,
CameraMetadata tmp(frame);
camera_metadata_t* meta = tmp.release();
ALOGV("%s: meta = %p ", __FUNCTION__, meta);
- mRemoteCallback->onResultReceived(frameId, meta);
+ mRemoteCallback->onResultReceived(requestId, meta);
tmp.acquire(meta);
}
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h
index faee9f9..8a0f547 100644
--- a/services/camera/libcameraservice/ProCamera2Client.h
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.h
@@ -17,10 +17,10 @@
#ifndef ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
-#include "Camera2Device.h"
#include "CameraService.h"
-#include "camera2/ProFrameProcessor.h"
-#include "Camera2ClientBase.h"
+#include "common/FrameProcessorBase.h"
+#include "common/Camera2ClientBase.h"
+#include "device2/Camera2Device.h"
namespace android {
@@ -31,7 +31,7 @@ class IMemory;
*/
class ProCamera2Client :
public Camera2ClientBase<CameraService::ProClient>,
- public camera2::ProFrameProcessor::FilteredListener
+ public camera2::FrameProcessorBase::FilteredListener
{
public:
/**
@@ -97,7 +97,7 @@ public:
protected:
/** FilteredListener implementation **/
- virtual void onFrameAvailable(int32_t frameId,
+ virtual void onFrameAvailable(int32_t requestId,
const CameraMetadata& frame);
virtual void detachDevice();
@@ -105,7 +105,7 @@ private:
/** IProCameraUser interface-related private members */
/** Preview callback related members */
- sp<camera2::ProFrameProcessor> mFrameProcessor;
+ sp<camera2::FrameProcessorBase> mFrameProcessor;
static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h
deleted file mode 100644
index 27ed8f6..0000000
--- a/services/camera/libcameraservice/camera2/FrameProcessor.h
+++ /dev/null
@@ -1,66 +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_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 <camera/CameraMetadata.h>
-
-#include "ProFrameProcessor.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 ProFrameProcessor {
- public:
- FrameProcessor(wp<CameraDeviceBase> device, wp<Camera2Client> client);
- ~FrameProcessor();
-
- private:
- wp<Camera2Client> mClient;
- int mLastFrameNumberOfFaces;
-
- void processNewFrames(const sp<Camera2Client> &client);
-
- virtual bool processSingleFrame(CameraMetadata &frame,
- const sp<CameraDeviceBase> &device);
-
- status_t processFaceDetect(const CameraMetadata &frame,
- const sp<Camera2Client> &client);
-
- // Emit FaceDetection event to java if faces changed
- void callbackFaceDetection(sp<Camera2Client> client,
- const camera_frame_metadata &metadata);
-};
-
-
-}; //namespace camera2
-}; //namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 0623b89..2d1253f 100644
--- a/services/camera/libcameraservice/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -24,11 +24,12 @@
#include <cutils/properties.h>
#include <gui/Surface.h>
#include <gui/Surface.h>
-#include "camera2/Parameters.h"
-#include "Camera2ClientBase.h"
-#include "camera2/ProFrameProcessor.h"
-#include "Camera2Device.h"
+#include "common/Camera2ClientBase.h"
+
+#include "api2/CameraDeviceClient.h"
+
+#include "CameraDeviceFactory.h"
namespace android {
using namespace camera2;
@@ -54,7 +55,9 @@ Camera2ClientBase<TClientBase>::Camera2ClientBase(
mSharedCameraCallbacks(remoteCallback)
{
ALOGI("Camera %d: Opened", cameraId);
- mDevice = new Camera2Device(cameraId);
+
+ mDevice = CameraDeviceFactory::createDevice(cameraId);
+ LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here.");
}
template <typename TClientBase>
@@ -92,7 +95,7 @@ status_t Camera2ClientBase<TClientBase>::initialize(camera_module_t *module) {
if (res != OK) {
ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
__FUNCTION__, TClientBase::mCameraId, strerror(-res), res);
- return NO_INIT;
+ return res;
}
res = mDevice->setNotifyCallback(this);
@@ -223,13 +226,18 @@ void Camera2ClientBase<TClientBase>::notifyError(int errorCode, int arg1,
}
template <typename TClientBase>
-void Camera2ClientBase<TClientBase>::notifyShutter(int frameNumber,
+void Camera2ClientBase<TClientBase>::notifyIdle() {
+ ALOGV("Camera device is now idle");
+}
+
+template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyShutter(int requestId,
nsecs_t timestamp) {
- (void)frameNumber;
+ (void)requestId;
(void)timestamp;
- ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
- frameNumber, timestamp);
+ ALOGV("%s: Shutter notification for request id %d at time %lld",
+ __FUNCTION__, requestId, timestamp);
}
template <typename TClientBase>
@@ -241,13 +249,6 @@ void Camera2ClientBase<TClientBase>::notifyAutoFocus(uint8_t newState,
ALOGV("%s: Autofocus state now %d, last trigger %d",
__FUNCTION__, newState, triggerId);
- typename SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
- if (l.mRemoteCallback != 0) {
- l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, 1, 0);
- }
- if (l.mRemoteCallback != 0) {
- l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, 1, 0);
- }
}
template <typename TClientBase>
@@ -325,5 +326,6 @@ void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() {
template class Camera2ClientBase<CameraService::ProClient>;
template class Camera2ClientBase<CameraService::Client>;
+template class Camera2ClientBase<CameraDeviceClientBase>;
} // namespace android
diff --git a/services/camera/libcameraservice/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 9001efb..61e44f0 100644
--- a/services/camera/libcameraservice/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -17,13 +17,14 @@
#ifndef ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
-#include "CameraDeviceBase.h"
-#include "CameraService.h"
+#include "common/CameraDeviceBase.h"
namespace android {
class IMemory;
+class CameraService;
+
template <typename TClientBase>
class Camera2ClientBase :
public TClientBase,
@@ -61,7 +62,8 @@ public:
*/
virtual void notifyError(int errorCode, int arg1, int arg2);
- virtual void notifyShutter(int frameNumber, nsecs_t timestamp);
+ virtual void notifyIdle();
+ virtual void notifyShutter(int requestId, 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,
@@ -101,6 +103,10 @@ public:
protected:
+ virtual sp<IBinder> asBinderWrapper() {
+ return IInterface::asBinder();
+ }
+
virtual status_t dumpDevice(int fd, const Vector<String16>& args);
/** Binder client interface-related private members */
diff --git a/services/camera/libcameraservice/CameraDeviceBase.cpp b/services/camera/libcameraservice/common/CameraDeviceBase.cpp
index 6c4e87f..6c4e87f 100644
--- a/services/camera/libcameraservice/CameraDeviceBase.cpp
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.cpp
diff --git a/services/camera/libcameraservice/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 8c457d9..e80abf1 100644
--- a/services/camera/libcameraservice/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -138,9 +138,18 @@ class CameraDeviceBase : public virtual RefBase {
*/
class NotificationListener {
public:
- // Refer to the Camera2 HAL definition for notification definitions
+ // The set of notifications is a merge of the notifications required for
+ // API1 and API2.
+
+ // Required for API 1 and 2
virtual void notifyError(int errorCode, int arg1, int arg2) = 0;
- virtual void notifyShutter(int frameNumber, nsecs_t timestamp) = 0;
+
+ // Required only for API2
+ virtual void notifyIdle() = 0;
+ virtual void notifyShutter(int requestId,
+ nsecs_t timestamp) = 0;
+
+ // Required only for API1
virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
virtual void notifyAutoWhitebalance(uint8_t newState,
@@ -156,14 +165,23 @@ class CameraDeviceBase : public virtual RefBase {
virtual status_t setNotifyCallback(NotificationListener *listener) = 0;
/**
+ * Whether the device supports calling notifyAutofocus, notifyAutoExposure,
+ * and notifyAutoWhitebalance; if this returns false, the client must
+ * synthesize these notifications from received frame metadata.
+ */
+ virtual bool willNotify3A() = 0;
+
+ /**
* Wait for a new frame to be produced, with timeout in nanoseconds.
* Returns TIMED_OUT when no frame produced within the specified duration
+ * May be called concurrently to most methods, except for getNextFrame
*/
virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
/**
* Get next metadata frame from the frame queue. Returns NULL if the queue
* is empty; caller takes ownership of the metadata buffer.
+ * May be called concurrently to most methods, except for waitForNextFrame
*/
virtual status_t getNextFrame(CameraMetadata *frame) = 0;
@@ -202,6 +220,13 @@ class CameraDeviceBase : public virtual RefBase {
*/
virtual status_t pushReprocessBuffer(int reprocessStreamId,
buffer_handle_t *buffer, wp<BufferReleasedListener> listener) = 0;
+
+ /**
+ * Flush all pending and in-flight requests. Blocks until flush is
+ * complete.
+ */
+ virtual status_t flush() = 0;
+
};
}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 4012fc5..f2064fb 100644
--- a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -14,39 +14,39 @@
* limitations under the License.
*/
-#define LOG_TAG "Camera2-ProFrameProcessor"
+#define LOG_TAG "Camera2-FrameProcessorBase"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "ProFrameProcessor.h"
-#include "../CameraDeviceBase.h"
+#include "common/FrameProcessorBase.h"
+#include "common/CameraDeviceBase.h"
namespace android {
namespace camera2 {
-ProFrameProcessor::ProFrameProcessor(wp<CameraDeviceBase> device) :
+FrameProcessorBase::FrameProcessorBase(wp<CameraDeviceBase> device) :
Thread(/*canCallJava*/false),
mDevice(device) {
}
-ProFrameProcessor::~ProFrameProcessor() {
+FrameProcessorBase::~FrameProcessorBase() {
ALOGV("%s: Exit", __FUNCTION__);
}
-status_t ProFrameProcessor::registerListener(int32_t minId,
- int32_t maxId, wp<FilteredListener> listener) {
+status_t FrameProcessorBase::registerListener(int32_t minId,
+ int32_t maxId, wp<FilteredListener> listener, bool quirkSendPartials) {
Mutex::Autolock l(mInputMutex);
ALOGV("%s: Registering listener for frame id range %d - %d",
__FUNCTION__, minId, maxId);
- RangeListener rListener = { minId, maxId, listener };
+ RangeListener rListener = { minId, maxId, listener, quirkSendPartials };
mRangeListeners.push_back(rListener);
return OK;
}
-status_t ProFrameProcessor::removeListener(int32_t minId,
+status_t FrameProcessorBase::removeListener(int32_t minId,
int32_t maxId,
wp<FilteredListener> listener) {
Mutex::Autolock l(mInputMutex);
@@ -63,13 +63,20 @@ status_t ProFrameProcessor::removeListener(int32_t minId,
return OK;
}
-void ProFrameProcessor::dump(int fd, const Vector<String16>& /*args*/) {
+void FrameProcessorBase::dump(int fd, const Vector<String16>& /*args*/) {
String8 result(" Latest received frame:\n");
write(fd, result.string(), result.size());
- mLastFrame.dump(fd, 2, 6);
+
+ CameraMetadata lastFrame;
+ {
+ // Don't race while dumping metadata
+ Mutex::Autolock al(mLastFrameMutex);
+ lastFrame = CameraMetadata(mLastFrame);
+ }
+ lastFrame.dump(fd, 2, 6);
}
-bool ProFrameProcessor::threadLoop() {
+bool FrameProcessorBase::threadLoop() {
status_t res;
sp<CameraDeviceBase> device;
@@ -82,14 +89,14 @@ bool ProFrameProcessor::threadLoop() {
if (res == OK) {
processNewFrames(device);
} else if (res != TIMED_OUT) {
- ALOGE("ProFrameProcessor: Error waiting for new "
+ ALOGE("FrameProcessorBase: Error waiting for new "
"frames: %s (%d)", strerror(-res), res);
}
return true;
}
-void ProFrameProcessor::processNewFrames(const sp<CameraDeviceBase> &device) {
+void FrameProcessorBase::processNewFrames(const sp<CameraDeviceBase> &device) {
status_t res;
ATRACE_CALL();
CameraMetadata frame;
@@ -113,6 +120,7 @@ void ProFrameProcessor::processNewFrames(const sp<CameraDeviceBase> &device) {
}
if (!frame.isEmpty()) {
+ Mutex::Autolock al(mLastFrameMutex);
mLastFrame.acquire(frame);
}
}
@@ -125,25 +133,35 @@ void ProFrameProcessor::processNewFrames(const sp<CameraDeviceBase> &device) {
return;
}
-bool ProFrameProcessor::processSingleFrame(CameraMetadata &frame,
+bool FrameProcessorBase::processSingleFrame(CameraMetadata &frame,
const sp<CameraDeviceBase> &device) {
ALOGV("%s: Camera %d: Process single frame (is empty? %d)",
__FUNCTION__, device->getId(), frame.isEmpty());
return processListeners(frame, device) == OK;
}
-status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
+status_t FrameProcessorBase::processListeners(const CameraMetadata &frame,
const sp<CameraDeviceBase> &device) {
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
+ // Quirks: Don't deliver partial results to listeners that don't want them
+ bool quirkIsPartial = false;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count != 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ ALOGV("%s: Camera %d: Not forwarding partial result to listeners",
+ __FUNCTION__, device->getId());
+ quirkIsPartial = true;
+ }
+
entry = frame.find(ANDROID_REQUEST_ID);
if (entry.count == 0) {
ALOGE("%s: Camera %d: Error reading frame id",
__FUNCTION__, device->getId());
return BAD_VALUE;
}
- int32_t frameId = entry.data.i32[0];
+ int32_t requestId = entry.data.i32[0];
List<sp<FilteredListener> > listeners;
{
@@ -151,8 +169,9 @@ status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
List<RangeListener>::iterator item = mRangeListeners.begin();
while (item != mRangeListeners.end()) {
- if (frameId >= item->minId &&
- frameId < item->maxId) {
+ if (requestId >= item->minId &&
+ requestId < item->maxId &&
+ (!quirkIsPartial || item->quirkSendPartials) ) {
sp<FilteredListener> listener = item->listener.promote();
if (listener == 0) {
item = mRangeListeners.erase(item);
@@ -167,7 +186,7 @@ status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
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);
+ (*item)->onFrameAvailable(requestId, frame);
}
return OK;
}
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index b82942c..89b608a 100644
--- a/services/camera/libcameraservice/camera2/ProFrameProcessor.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -33,20 +33,22 @@ namespace camera2 {
/* Output frame metadata processing thread. This thread waits for new
* frames from the device, and analyzes them as necessary.
*/
-class ProFrameProcessor: public Thread {
+class FrameProcessorBase: public Thread {
public:
- ProFrameProcessor(wp<CameraDeviceBase> device);
- virtual ~ProFrameProcessor();
+ FrameProcessorBase(wp<CameraDeviceBase> device);
+ virtual ~FrameProcessorBase();
struct FilteredListener: virtual public RefBase {
- virtual void onFrameAvailable(int32_t frameId,
+ virtual void onFrameAvailable(int32_t requestId,
const CameraMetadata &frame) = 0;
};
// Register a listener for a range of IDs [minId, maxId). Multiple listeners
- // can be listening to the same range
+ // can be listening to the same range.
+ // QUIRK: sendPartials controls whether partial results will be sent.
status_t registerListener(int32_t minId, int32_t maxId,
- wp<FilteredListener> listener);
+ wp<FilteredListener> listener,
+ bool quirkSendPartials = true);
status_t removeListener(int32_t minId, int32_t maxId,
wp<FilteredListener> listener);
@@ -58,11 +60,13 @@ class ProFrameProcessor: public Thread {
virtual bool threadLoop();
Mutex mInputMutex;
+ Mutex mLastFrameMutex;
struct RangeListener {
int32_t minId;
int32_t maxId;
wp<FilteredListener> listener;
+ bool quirkSendPartials;
};
List<RangeListener> mRangeListeners;
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 87b2807..87b2807 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index 77df152..2bc1a8a 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -445,6 +445,10 @@ status_t Camera2Device::setNotifyCallback(NotificationListener *listener) {
return res;
}
+bool Camera2Device::willNotify3A() {
+ return true;
+}
+
void Camera2Device::notificationCallback(int32_t msg_type,
int32_t ext1,
int32_t ext2,
@@ -460,8 +464,10 @@ void Camera2Device::notificationCallback(int32_t msg_type,
listener->notifyError(ext1, ext2, ext3);
break;
case CAMERA2_MSG_SHUTTER: {
- nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
- listener->notifyShutter(ext1, timestamp);
+ // TODO: Only needed for camera2 API, which is unsupported
+ // by HAL2 directly.
+ // nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
+ // listener->notifyShutter(requestId, timestamp);
break;
}
case CAMERA2_MSG_AUTOFOCUS:
@@ -563,6 +569,13 @@ status_t Camera2Device::pushReprocessBuffer(int reprocessStreamId,
return res;
}
+status_t Camera2Device::flush() {
+ ATRACE_CALL();
+
+ mRequestQueue.clear();
+ return waitUntilDrained();
+}
+
/**
* Camera2Device::MetadataQueue
*/
@@ -587,9 +600,7 @@ Camera2Device::MetadataQueue::MetadataQueue():
Camera2Device::MetadataQueue::~MetadataQueue() {
ATRACE_CALL();
- Mutex::Autolock l(mMutex);
- freeBuffers(mEntries.begin(), mEntries.end());
- freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
+ clear();
}
// Connect to camera2 HAL as consumer (input requests/reprocessing)
@@ -780,6 +791,23 @@ status_t Camera2Device::MetadataQueue::setStreamSlot(
return signalConsumerLocked();
}
+status_t Camera2Device::MetadataQueue::clear()
+{
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+
+ Mutex::Autolock l(mMutex);
+
+ // Clear streaming slot
+ freeBuffers(mStreamSlot.begin(), mStreamSlot.end());
+ mStreamSlotCount = 0;
+
+ // Clear request queue
+ freeBuffers(mEntries.begin(), mEntries.end());
+ mCount = 0;
+ return OK;
+}
+
status_t Camera2Device::MetadataQueue::dump(int fd,
const Vector<String16>& /*args*/) {
ATRACE_CALL();
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index 3034a1d..1f53c56 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -22,12 +22,16 @@
#include <utils/List.h>
#include <utils/Mutex.h>
-#include "CameraDeviceBase.h"
+#include "common/CameraDeviceBase.h"
namespace android {
/**
* CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_2_0
+ *
+ * TODO for camera2 API implementation:
+ * Does not produce notifyShutter / notifyIdle callbacks to NotificationListener
+ * Use waitUntilDrained for idle.
*/
class Camera2Device: public CameraDeviceBase {
public:
@@ -59,6 +63,7 @@ class Camera2Device: public CameraDeviceBase {
virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
virtual status_t waitUntilDrained();
virtual status_t setNotifyCallback(NotificationListener *listener);
+ virtual bool willNotify3A();
virtual status_t waitForNextFrame(nsecs_t timeout);
virtual status_t getNextFrame(CameraMetadata *frame);
virtual status_t triggerAutofocus(uint32_t id);
@@ -66,6 +71,8 @@ class Camera2Device: public CameraDeviceBase {
virtual status_t triggerPrecaptureMetering(uint32_t id);
virtual status_t pushReprocessBuffer(int reprocessStreamId,
buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
+ // Flush implemented as just a wait
+ virtual status_t flush();
private:
const int mId;
camera2_device_t *mHal2Device;
@@ -112,6 +119,9 @@ class Camera2Device: public CameraDeviceBase {
status_t setStreamSlot(camera_metadata_t *buf);
status_t setStreamSlot(const List<camera_metadata_t*> &bufs);
+ // Clear the request queue and the streaming slot
+ status_t clear();
+
status_t dump(int fd, const Vector<String16>& args);
private:
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index cc7802b..3dbc1b0 100644
--- a/services/camera/libcameraservice/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -40,9 +40,12 @@
#include <utils/Log.h>
#include <utils/Trace.h>
#include <utils/Timers.h>
-#include "Camera3Device.h"
-#include "camera3/Camera3OutputStream.h"
-#include "camera3/Camera3InputStream.h"
+
+#include "utils/CameraTraces.h"
+#include "device3/Camera3Device.h"
+#include "device3/Camera3OutputStream.h"
+#include "device3/Camera3InputStream.h"
+#include "device3/Camera3ZslStream.h"
using namespace android::camera3;
@@ -52,6 +55,7 @@ Camera3Device::Camera3Device(int id):
mId(id),
mHal3Device(NULL),
mStatus(STATUS_UNINITIALIZED),
+ mUsePartialResultQuirk(false),
mNextResultFrameNumber(0),
mNextShutterFrameNumber(0),
mListener(NULL)
@@ -80,6 +84,7 @@ int Camera3Device::getId() const {
status_t Camera3Device::initialize(camera_module_t *module)
{
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
@@ -128,7 +133,10 @@ status_t Camera3Device::initialize(camera_module_t *module)
/** Initialize device with callback functions */
+ ATRACE_BEGIN("camera3->initialize");
res = device->ops->initialize(device, this);
+ ATRACE_END();
+
if (res != OK) {
SET_ERR_L("Unable to initialize HAL device: %s (%d)",
strerror(-res), res);
@@ -140,7 +148,9 @@ status_t Camera3Device::initialize(camera_module_t *module)
mVendorTagOps.get_camera_vendor_section_name = NULL;
+ ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops");
device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps);
+ ATRACE_END();
if (mVendorTagOps.get_camera_vendor_section_name != NULL) {
res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps);
@@ -152,9 +162,20 @@ status_t Camera3Device::initialize(camera_module_t *module)
}
}
+ /** Start up status tracker thread */
+ mStatusTracker = new StatusTracker(this);
+ res = mStatusTracker->run(String8::format("C3Dev-%d-Status", mId).string());
+ if (res != OK) {
+ SET_ERR_L("Unable to start status tracking thread: %s (%d)",
+ strerror(-res), res);
+ device->common.close(&device->common);
+ mStatusTracker.clear();
+ return res;
+ }
+
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, device);
+ mRequestThread = new RequestThread(this, mStatusTracker, device);
res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -168,81 +189,139 @@ status_t Camera3Device::initialize(camera_module_t *module)
mDeviceInfo = info.static_camera_characteristics;
mHal3Device = device;
- mStatus = STATUS_IDLE;
+ mStatus = STATUS_UNCONFIGURED;
mNextStreamId = 0;
mNeedConfig = true;
+ mPauseStateNotify = false;
+
+ /** Check for quirks */
+
+ // Will the HAL be sending in early partial result metadata?
+ camera_metadata_entry partialResultsQuirk =
+ mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) {
+ mUsePartialResultQuirk = true;
+ }
return OK;
}
status_t Camera3Device::disconnect() {
ATRACE_CALL();
- Mutex::Autolock l(mLock);
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: E", __FUNCTION__);
status_t res = OK;
- if (mStatus == STATUS_UNINITIALIZED) return res;
- if (mStatus == STATUS_ACTIVE ||
- (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
- res = mRequestThread->clearRepeatingRequests();
- if (res != OK) {
- SET_ERR_L("Can't stop streaming");
- // Continue to close device even in case of error
- } else {
- res = waitUntilDrainedLocked();
+ {
+ Mutex::Autolock l(mLock);
+ if (mStatus == STATUS_UNINITIALIZED) return res;
+
+ if (mStatus == STATUS_ACTIVE ||
+ (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
+ res = mRequestThread->clearRepeatingRequests();
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain");
+ SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
+ } else {
+ res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Timeout waiting for HAL to drain");
+ // Continue to close device even in case of error
+ }
}
}
+
+ if (mStatus == STATUS_ERROR) {
+ CLOGE("Shutting down in an error state");
+ }
+
+ if (mStatusTracker != NULL) {
+ mStatusTracker->requestExit();
+ }
+
+ if (mRequestThread != NULL) {
+ mRequestThread->requestExit();
+ }
+
+ mOutputStreams.clear();
+ mInputStream.clear();
}
- assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR);
- if (mStatus == STATUS_ERROR) {
- CLOGE("Shutting down in an error state");
+ // Joining done without holding mLock, otherwise deadlocks may ensue
+ // as the threads try to access parent state
+ if (mRequestThread != NULL && mStatus != STATUS_ERROR) {
+ // HAL may be in a bad state, so waiting for request thread
+ // (which may be stuck in the HAL processCaptureRequest call)
+ // could be dangerous.
+ mRequestThread->join();
}
- if (mRequestThread != NULL) {
- mRequestThread->requestExit();
+ if (mStatusTracker != NULL) {
+ mStatusTracker->join();
}
- mOutputStreams.clear();
- mInputStream.clear();
+ {
+ Mutex::Autolock l(mLock);
- if (mRequestThread != NULL) {
- if (mStatus != STATUS_ERROR) {
- // HAL may be in a bad state, so waiting for request thread
- // (which may be stuck in the HAL processCaptureRequest call)
- // could be dangerous.
- mRequestThread->join();
- }
mRequestThread.clear();
- }
+ mStatusTracker.clear();
- if (mHal3Device != NULL) {
- mHal3Device->common.close(&mHal3Device->common);
- mHal3Device = NULL;
- }
+ if (mHal3Device != NULL) {
+ mHal3Device->common.close(&mHal3Device->common);
+ mHal3Device = NULL;
+ }
- mStatus = STATUS_UNINITIALIZED;
+ mStatus = STATUS_UNINITIALIZED;
+ }
ALOGV("%s: X", __FUNCTION__);
return res;
}
+// For dumping/debugging only -
+// try to acquire a lock a few times, eventually give up to proceed with
+// debug/dump operations
+bool Camera3Device::tryLockSpinRightRound(Mutex& lock) {
+ bool gotLock = false;
+ for (size_t i = 0; i < kDumpLockAttempts; ++i) {
+ if (lock.tryLock() == NO_ERROR) {
+ gotLock = true;
+ break;
+ } else {
+ usleep(kDumpSleepDuration);
+ }
+ }
+ return gotLock;
+}
+
status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
ATRACE_CALL();
(void)args;
+
+ // Try to lock, but continue in case of failure (to avoid blocking in
+ // deadlocks)
+ bool gotInterfaceLock = tryLockSpinRightRound(mInterfaceLock);
+ bool gotLock = tryLockSpinRightRound(mLock);
+
+ ALOGW_IF(!gotInterfaceLock,
+ "Camera %d: %s: Unable to lock interface lock, proceeding anyway",
+ mId, __FUNCTION__);
+ ALOGW_IF(!gotLock,
+ "Camera %d: %s: Unable to lock main lock, proceeding anyway",
+ mId, __FUNCTION__);
+
String8 lines;
const char *status =
mStatus == STATUS_ERROR ? "ERROR" :
mStatus == STATUS_UNINITIALIZED ? "UNINITIALIZED" :
- mStatus == STATUS_IDLE ? "IDLE" :
+ mStatus == STATUS_UNCONFIGURED ? "UNCONFIGURED" :
+ mStatus == STATUS_CONFIGURED ? "CONFIGURED" :
mStatus == STATUS_ACTIVE ? "ACTIVE" :
"Unknown";
+
lines.appendFormat(" Device status: %s\n", status);
if (mStatus == STATUS_ERROR) {
lines.appendFormat(" Error cause: %s\n", mErrorCause.string());
@@ -274,12 +353,23 @@ status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
}
write(fd, lines.string(), lines.size());
+ {
+ lines = String8(" Last request sent:\n");
+ write(fd, lines.string(), lines.size());
+
+ CameraMetadata lastRequest = getLatestRequestLocked();
+ lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
+ }
+
if (mHal3Device != NULL) {
lines = String8(" HAL device dump:\n");
write(fd, lines.string(), lines.size());
mHal3Device->ops->dump(mHal3Device, fd);
}
+ if (gotLock) mLock.unlock();
+ if (gotInterfaceLock) mInterfaceLock.unlock();
+
return OK;
}
@@ -296,6 +386,8 @@ const CameraMetadata& Camera3Device::info() const {
status_t Camera3Device::capture(CameraMetadata &request) {
ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
// TODO: take ownership of the request
@@ -307,7 +399,9 @@ status_t Camera3Device::capture(CameraMetadata &request) {
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ // May be lazily configuring streams, will check during setup
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -322,12 +416,23 @@ status_t Camera3Device::capture(CameraMetadata &request) {
return BAD_VALUE;
}
- return mRequestThread->queueRequest(newRequest);
+ res = mRequestThread->queueRequest(newRequest);
+ if (res == OK) {
+ waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
+ }
+ ALOGV("Camera %d: Capture request enqueued", mId);
+ }
+ return res;
}
status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
ATRACE_CALL();
+ status_t res;
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -337,7 +442,9 @@ status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ // May be lazily configuring streams, will check during setup
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -355,7 +462,16 @@ status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
RequestList newRepeatingRequests;
newRepeatingRequests.push_back(newRepeatingRequest);
- return mRequestThread->setRepeatingRequests(newRepeatingRequests);
+ res = mRequestThread->setRepeatingRequests(newRepeatingRequests);
+ if (res == OK) {
+ waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
+ }
+ ALOGV("Camera %d: Repeating request set", mId);
+ }
+ return res;
}
@@ -363,12 +479,16 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
const CameraMetadata &request) {
status_t res;
- if (mStatus == STATUS_IDLE) {
+ if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
res = configureStreamsLocked();
if (res != OK) {
SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
return NULL;
}
+ if (mStatus == STATUS_UNCONFIGURED) {
+ CLOGE("No streams configured");
+ return NULL;
+ }
}
sp<CaptureRequest> newRequest = createCaptureRequest(request);
@@ -377,6 +497,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
status_t Camera3Device::clearStreamingRequest() {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -386,7 +507,8 @@ status_t Camera3Device::clearStreamingRequest() {
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -394,12 +516,13 @@ status_t Camera3Device::clearStreamingRequest() {
SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
-
+ ALOGV("Camera %d: Clearing repeating request", mId);
return mRequestThread->clearRepeatingRequests();
}
status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
return mRequestThread->waitUntilRequestProcessed(requestId, timeout);
}
@@ -407,7 +530,10 @@ status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time
status_t Camera3Device::createInputStream(
uint32_t width, uint32_t height, int format, int *id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating new input stream %d: %d x %d, format %d",
+ mId, mNextStreamId, width, height, format);
status_t res;
bool wasActive = false;
@@ -419,26 +545,24 @@ status_t Camera3Device::createInputStream(
case STATUS_UNINITIALIZED:
ALOGE("%s: Device not initialized", __FUNCTION__);
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("%s: Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
if (mInputStream != 0) {
ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -447,6 +571,7 @@ status_t Camera3Device::createInputStream(
sp<Camera3InputStream> newStream = new Camera3InputStream(mNextStreamId,
width, height, format);
+ newStream->setStatusTracker(mStatusTracker);
mInputStream = newStream;
@@ -461,9 +586,10 @@ status_t Camera3Device::createInputStream(
__FUNCTION__, mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
+ ALOGV("Camera %d: Created input stream", mId);
return OK;
}
@@ -475,7 +601,10 @@ status_t Camera3Device::createZslStream(
int *id,
sp<Camera3ZslStream>* zslStream) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating ZSL stream %d: %d x %d, depth %d",
+ mId, mNextStreamId, width, height, depth);
status_t res;
bool wasActive = false;
@@ -487,26 +616,24 @@ status_t Camera3Device::createZslStream(
case STATUS_UNINITIALIZED:
ALOGE("%s: Device not initialized", __FUNCTION__);
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
- mStatus = STATUS_ERROR;
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
break;
default:
- ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+ SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
if (mInputStream != 0) {
ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -515,6 +642,7 @@ status_t Camera3Device::createZslStream(
sp<Camera3ZslStream> newStream = new Camera3ZslStream(mNextStreamId,
width, height, depth);
+ newStream->setStatusTracker(mStatusTracker);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
@@ -536,16 +664,20 @@ status_t Camera3Device::createZslStream(
__FUNCTION__, mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
+ ALOGV("Camera %d: Created ZSL stream", mId);
return OK;
}
status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, size_t size, int *id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, size %d",
+ mId, mNextStreamId, width, height, format, size);
status_t res;
bool wasActive = false;
@@ -557,16 +689,15 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
// OK
break;
case STATUS_ACTIVE:
ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
- mRequestThread->setPaused(true);
- res = waitUntilDrainedLocked();
+ res = internalPauseAndWaitLocked();
if (res != OK) {
- ALOGE("%s: Can't pause captures to reconfigure streams!",
- __FUNCTION__);
+ SET_ERR_L("Can't pause captures to reconfigure streams!");
return res;
}
wasActive = true;
@@ -575,7 +706,7 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
SET_ERR_L("Unexpected status: %d", mStatus);
return INVALID_OPERATION;
}
- assert(mStatus == STATUS_IDLE);
+ assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
if (format == HAL_PIXEL_FORMAT_BLOB) {
@@ -585,6 +716,7 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
newStream = new Camera3OutputStream(mNextStreamId, consumer,
width, height, format);
}
+ newStream->setStatusTracker(mStatusTracker);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
@@ -604,9 +736,9 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
mNextStreamId, strerror(-res), res);
return res;
}
- mRequestThread->setPaused(false);
+ internalResumeLocked();
}
-
+ ALOGV("Camera %d: Created new stream", mId);
return OK;
}
@@ -622,6 +754,7 @@ status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) {
status_t Camera3Device::getStreamInfo(int id,
uint32_t *width, uint32_t *height, uint32_t *format) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -631,7 +764,8 @@ status_t Camera3Device::getStreamInfo(int id,
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized!");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -656,6 +790,7 @@ status_t Camera3Device::getStreamInfo(int id,
status_t Camera3Device::setStreamTransform(int id,
int transform) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -665,7 +800,8 @@ status_t Camera3Device::setStreamTransform(int id,
case STATUS_UNINITIALIZED:
CLOGE("Device not initialized");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -686,6 +822,7 @@ status_t Camera3Device::setStreamTransform(int id,
status_t Camera3Device::deleteStream(int id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
status_t res;
@@ -693,7 +830,7 @@ status_t Camera3Device::deleteStream(int id) {
// CameraDevice semantics require device to already be idle before
// deleteStream is called, unlike for createStream.
- if (mStatus != STATUS_IDLE) {
+ if (mStatus == STATUS_ACTIVE) {
ALOGV("%s: Camera %d: Device not idle", __FUNCTION__, mId);
return -EBUSY;
}
@@ -736,7 +873,8 @@ status_t Camera3Device::deleteReprocessStream(int id) {
status_t Camera3Device::createDefaultRequest(int templateId,
CameraMetadata *request) {
ATRACE_CALL();
- ALOGV("%s: E", __FUNCTION__);
+ ALOGV("%s: for template %d", __FUNCTION__, templateId);
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
switch (mStatus) {
@@ -746,7 +884,8 @@ status_t Camera3Device::createDefaultRequest(int templateId,
case STATUS_UNINITIALIZED:
CLOGE("Device is not initialized!");
return INVALID_OPERATION;
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
+ case STATUS_CONFIGURED:
case STATUS_ACTIVE:
// OK
break;
@@ -756,8 +895,10 @@ status_t Camera3Device::createDefaultRequest(int templateId,
}
const camera_metadata_t *rawRequest;
+ ATRACE_BEGIN("camera3->construct_default_request_settings");
rawRequest = mHal3Device->ops->construct_default_request_settings(
mHal3Device, templateId);
+ ATRACE_END();
if (rawRequest == NULL) {
SET_ERR_L("HAL is unable to construct default settings for template %d",
templateId);
@@ -770,61 +911,88 @@ status_t Camera3Device::createDefaultRequest(int templateId,
status_t Camera3Device::waitUntilDrained() {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- return waitUntilDrainedLocked();
-}
-
-status_t Camera3Device::waitUntilDrainedLocked() {
- ATRACE_CALL();
- status_t res;
-
switch (mStatus) {
case STATUS_UNINITIALIZED:
- case STATUS_IDLE:
+ case STATUS_UNCONFIGURED:
ALOGV("%s: Already idle", __FUNCTION__);
return OK;
+ case STATUS_CONFIGURED:
+ // To avoid race conditions, check with tracker to be sure
case STATUS_ERROR:
case STATUS_ACTIVE:
- // Need to shut down
+ // Need to verify shut down
break;
default:
SET_ERR_L("Unexpected status: %d",mStatus);
return INVALID_OPERATION;
}
- if (mRequestThread != NULL) {
- res = mRequestThread->waitUntilPaused(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't stop request thread in %f seconds!",
- kShutdownTimeout/1e9);
- return res;
- }
- }
- if (mInputStream != NULL) {
- res = mInputStream->waitUntilIdle(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't idle input stream %d in %f seconds!",
- mInputStream->getId(), kShutdownTimeout/1e9);
- return res;
- }
+ ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ return res;
+}
+
+// Pause to reconfigure
+status_t Camera3Device::internalPauseAndWaitLocked() {
+ mRequestThread->setPaused(true);
+ mPauseStateNotify = true;
+
+ ALOGV("%s: Camera %d: Internal wait until idle", __FUNCTION__, mId);
+ status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't idle device in %f seconds!",
+ kShutdownTimeout/1e9);
}
- for (size_t i = 0; i < mOutputStreams.size(); i++) {
- res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout);
- if (res != OK) {
- SET_ERR_L("Can't idle output stream %d in %f seconds!",
- mOutputStreams.keyAt(i), kShutdownTimeout/1e9);
- return res;
- }
+
+ return res;
+}
+
+// Resume after internalPauseAndWaitLocked
+status_t Camera3Device::internalResumeLocked() {
+ status_t res;
+
+ mRequestThread->setPaused(false);
+
+ res = waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+ if (res != OK) {
+ SET_ERR_L("Can't transition to active in %f seconds!",
+ kActiveTimeout/1e9);
}
+ mPauseStateNotify = false;
+ return OK;
+}
- if (mStatus != STATUS_ERROR) {
- mStatus = STATUS_IDLE;
+status_t Camera3Device::waitUntilStateThenRelock(bool active,
+ nsecs_t timeout) {
+ status_t res = OK;
+ if (active == (mStatus == STATUS_ACTIVE)) {
+ // Desired state already reached
+ return res;
}
- return OK;
+ bool stateSeen = false;
+ do {
+ mRecentStatusUpdates.clear();
+
+ res = mStatusChanged.waitRelative(mLock, timeout);
+ if (res != OK) break;
+
+ // Check state change history during wait
+ for (size_t i = 0; i < mRecentStatusUpdates.size(); i++) {
+ if (active == (mRecentStatusUpdates[i] == STATUS_ACTIVE) ) {
+ stateSeen = true;
+ break;
+ }
+ }
+ } while (!stateSeen);
+
+ return res;
}
+
status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {
ATRACE_CALL();
Mutex::Autolock l(mOutputLock);
@@ -837,8 +1005,11 @@ status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {
return OK;
}
+bool Camera3Device::willNotify3A() {
+ return false;
+}
+
status_t Camera3Device::waitForNextFrame(nsecs_t timeout) {
- ATRACE_CALL();
status_t res;
Mutex::Autolock l(mOutputLock);
@@ -872,6 +1043,7 @@ status_t Camera3Device::getNextFrame(CameraMetadata *frame) {
status_t Camera3Device::triggerAutofocus(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -892,6 +1064,7 @@ status_t Camera3Device::triggerAutofocus(uint32_t id) {
status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -912,6 +1085,7 @@ status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) {
ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
// Mix-in this trigger into the next request and only the next request.
@@ -939,6 +1113,51 @@ status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId,
return INVALID_OPERATION;
}
+status_t Camera3Device::flush() {
+ ATRACE_CALL();
+ ALOGV("%s: Camera %d: Flushing all requests", __FUNCTION__, mId);
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ mRequestThread->clear();
+ return mHal3Device->ops->flush(mHal3Device);
+}
+
+/**
+ * Methods called by subclasses
+ */
+
+void Camera3Device::notifyStatus(bool idle) {
+ {
+ // Need mLock to safely update state and synchronize to current
+ // state of methods in flight.
+ Mutex::Autolock l(mLock);
+ // We can get various system-idle notices from the status tracker
+ // while starting up. Only care about them if we've actually sent
+ // in some requests recently.
+ if (mStatus != STATUS_ACTIVE && mStatus != STATUS_CONFIGURED) {
+ return;
+ }
+ ALOGV("%s: Camera %d: Now %s", __FUNCTION__, mId,
+ idle ? "idle" : "active");
+ mStatus = idle ? STATUS_CONFIGURED : STATUS_ACTIVE;
+ mRecentStatusUpdates.add(mStatus);
+ mStatusChanged.signal();
+
+ // Skip notifying listener if we're doing some user-transparent
+ // state changes
+ if (mPauseStateNotify) return;
+ }
+ NotificationListener *listener;
+ {
+ Mutex::Autolock l(mOutputLock);
+ listener = mListener;
+ }
+ if (idle && listener != NULL) {
+ listener->notifyIdle();
+ }
+}
+
/**
* Camera3Device private methods
*/
@@ -955,7 +1174,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(
newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS);
if (inputStreams.count > 0) {
if (mInputStream == NULL ||
- mInputStream->getId() != inputStreams.data.u8[0]) {
+ mInputStream->getId() != inputStreams.data.i32[0]) {
CLOGE("Request references unknown input stream %d",
inputStreams.data.u8[0]);
return NULL;
@@ -984,7 +1203,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(
}
for (size_t i = 0; i < streams.count; i++) {
- int idx = mOutputStreams.indexOfKey(streams.data.u8[i]);
+ int idx = mOutputStreams.indexOfKey(streams.data.i32[i]);
if (idx == NAME_NOT_FOUND) {
CLOGE("Request references unknown stream %d",
streams.data.u8[i]);
@@ -1015,18 +1234,18 @@ status_t Camera3Device::configureStreamsLocked() {
ATRACE_CALL();
status_t res;
- if (mStatus != STATUS_IDLE) {
+ if (mStatus != STATUS_UNCONFIGURED && mStatus != STATUS_CONFIGURED) {
CLOGE("Not idle");
return INVALID_OPERATION;
}
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
- mStatus = STATUS_ACTIVE;
return OK;
}
// Start configuring the streams
+ ALOGV("%s: Camera %d: Starting stream configuration", __FUNCTION__, mId);
camera3_stream_configuration config;
@@ -1068,8 +1287,9 @@ status_t Camera3Device::configureStreamsLocked() {
// Do the HAL configuration; will potentially touch stream
// max_buffers, usage, priv fields.
-
+ ATRACE_BEGIN("camera3->configure_streams");
res = mHal3Device->ops->configure_streams(mHal3Device, &config);
+ ATRACE_END();
if (res != OK) {
SET_ERR_L("Unable to configure streams with HAL: %s (%d)",
@@ -1107,11 +1327,18 @@ status_t Camera3Device::configureStreamsLocked() {
// across configure_streams() calls
mRequestThread->configurationComplete();
- // Finish configuring the streams lazily on first reference
+ // Update device state
- mStatus = STATUS_ACTIVE;
mNeedConfig = false;
+ if (config.num_streams > 0) {
+ mStatus = STATUS_CONFIGURED;
+ } else {
+ mStatus = STATUS_UNCONFIGURED;
+ }
+
+ ALOGV("%s: Camera %d: Stream configuration complete", __FUNCTION__, mId);
+
return OK;
}
@@ -1147,6 +1374,10 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
// But only do error state transition steps for the first error
if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return;
+ // Save stack trace. View by dumping it later.
+ CameraTraces::saveTrace();
+ // TODO: consider adding errorCause and client pid/procname
+
mErrorCause = errorCause;
mRequestThread->setPaused(true);
@@ -1158,18 +1389,187 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
*/
status_t Camera3Device::registerInFlight(int32_t frameNumber,
- int32_t numBuffers) {
+ int32_t requestId, int32_t numBuffers) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
- res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers));
+ res = mInFlightMap.add(frameNumber, InFlightRequest(requestId, numBuffers));
if (res < 0) return res;
return OK;
}
/**
+ * QUIRK(partial results)
+ * Check if all 3A fields are ready, and send off a partial 3A-only result
+ * to the output frame queue
+ */
+bool Camera3Device::processPartial3AQuirk(
+ int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial) {
+
+ // Check if all 3A states are present
+ // The full list of fields is
+ // android.control.afMode
+ // android.control.awbMode
+ // android.control.aeState
+ // android.control.awbState
+ // android.control.afState
+ // android.control.afTriggerID
+ // android.control.aePrecaptureID
+ // TODO: Add android.control.aeMode
+
+ bool gotAllStates = true;
+
+ uint8_t afMode;
+ uint8_t awbMode;
+ uint8_t aeState;
+ uint8_t afState;
+ uint8_t awbState;
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber);
+
+ if (!gotAllStates) return false;
+
+ ALOGVV("%s: Camera %d: Frame %d, Request ID %d: AF mode %d, AWB mode %d, "
+ "AF state %d, AE state %d, AWB state %d, "
+ "AF trigger %d, AE precapture trigger %d",
+ __FUNCTION__, mId, frameNumber, requestId,
+ afMode, awbMode,
+ afState, aeState, awbState,
+ afTriggerId, aeTriggerId);
+
+ // Got all states, so construct a minimal result to send
+ // In addition to the above fields, this means adding in
+ // android.request.frameCount
+ // android.request.requestId
+ // android.quirks.partialResult
+
+ const size_t kMinimal3AResultEntries = 10;
+
+ Mutex::Autolock l(mOutputLock);
+
+ CameraMetadata& min3AResult =
+ *mResultQueue.insert(
+ mResultQueue.end(),
+ CameraMetadata(kMinimal3AResultEntries, /*dataCapacity*/ 0));
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_FRAME_COUNT,
+ &frameNumber, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_ID,
+ &requestId, frameNumber)) {
+ return false;
+ }
+
+ static const uint8_t partialResult = ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL;
+ if (!insert3AResult(min3AResult, ANDROID_QUIRKS_PARTIAL_RESULT,
+ &partialResult, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber)) {
+ return false;
+ }
+
+ mResultSignal.signal();
+
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber) {
+ (void) frameNumber;
+
+ camera_metadata_ro_entry_t entry;
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGVV("%s: Camera %d: Frame %d: No %s provided by HAL!", __FUNCTION__,
+ mId, frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+
+ if (sizeof(T) == sizeof(uint8_t)) {
+ *value = entry.data.u8[0];
+ } else if (sizeof(T) == sizeof(int32_t)) {
+ *value = entry.data.i32[0];
+ } else {
+ ALOGE("%s: Unexpected type", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag,
+ const T* value, int32_t frameNumber) {
+ if (result.update(tag, value, 1) != NO_ERROR) {
+ mResultQueue.erase(--mResultQueue.end(), mResultQueue.end());
+ SET_ERR("Frame %d: Failed to set %s in partial metadata",
+ frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+ return true;
+}
+
+/**
* Camera HAL device callback methods
*/
@@ -1184,6 +1584,8 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
frameNumber);
return;
}
+ bool partialResultQuirk = false;
+ CameraMetadata collectedQuirkResult;
// Get capture timestamp from list of in-flight requests, where it was added
// by the shutter notification for this frame. Then update the in-flight
@@ -1199,19 +1601,58 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
return;
}
InFlightRequest &request = mInFlightMap.editValueAt(idx);
+
+ // Check if this result carries only partial metadata
+ if (mUsePartialResultQuirk && result->result != NULL) {
+ camera_metadata_ro_entry_t partialResultEntry;
+ res = find_camera_metadata_ro_entry(result->result,
+ ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry);
+ if (res != NAME_NOT_FOUND &&
+ partialResultEntry.count > 0 &&
+ partialResultEntry.data.u8[0] ==
+ ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ // A partial result. Flag this as such, and collect this
+ // set of metadata into the in-flight entry.
+ partialResultQuirk = true;
+ request.partialResultQuirk.collectedResult.append(
+ result->result);
+ request.partialResultQuirk.collectedResult.erase(
+ ANDROID_QUIRKS_PARTIAL_RESULT);
+ // Fire off a 3A-only result if possible
+ if (!request.partialResultQuirk.haveSent3A) {
+ request.partialResultQuirk.haveSent3A =
+ processPartial3AQuirk(frameNumber,
+ request.requestId,
+ request.partialResultQuirk.collectedResult);
+ }
+ }
+ }
+
timestamp = request.captureTimestamp;
- if (timestamp == 0) {
+ /**
+ * One of the following must happen before it's legal to call process_capture_result,
+ * unless partial metadata is being provided:
+ * - CAMERA3_MSG_SHUTTER (expected during normal operation)
+ * - CAMERA3_MSG_ERROR (expected during flush)
+ */
+ if (request.requestStatus == OK && timestamp == 0 && !partialResultQuirk) {
SET_ERR("Called before shutter notify for frame %d",
frameNumber);
return;
}
- if (result->result != NULL) {
+ // Did we get the (final) result metadata for this capture?
+ if (result->result != NULL && !partialResultQuirk) {
if (request.haveResultMetadata) {
SET_ERR("Called multiple times with metadata for frame %d",
frameNumber);
return;
}
+ if (mUsePartialResultQuirk &&
+ !request.partialResultQuirk.collectedResult.isEmpty()) {
+ collectedQuirkResult.acquire(
+ request.partialResultQuirk.collectedResult);
+ }
request.haveResultMetadata = true;
}
@@ -1223,7 +1664,9 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
return;
}
+ // Check if everything has arrived for this result (buffers and metadata)
if (request.haveResultMetadata && request.numBuffersLeft == 0) {
+ ATRACE_ASYNC_END("frame capture", frameNumber);
mInFlightMap.removeItemsAt(idx, 1);
}
@@ -1235,17 +1678,13 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
}
- AlgState cur3aState;
- AlgState new3aState;
- int32_t aeTriggerId = 0;
- int32_t afTriggerId = 0;
-
- NotificationListener *listener = NULL;
-
// Process the result metadata, if provided
- if (result->result != NULL) {
+ bool gotResult = false;
+ if (result->result != NULL && !partialResultQuirk) {
Mutex::Autolock l(mOutputLock);
+ gotResult = true;
+
if (frameNumber != mNextResultFrameNumber) {
SET_ERR("Out-of-order capture result metadata submitted! "
"(got frame number %d, expecting %d)",
@@ -1254,19 +1693,26 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
}
mNextResultFrameNumber++;
- CameraMetadata &captureResult =
- *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
-
+ CameraMetadata captureResult;
captureResult = result->result;
+
if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
(int32_t*)&frameNumber, 1) != OK) {
SET_ERR("Failed to set frame# in metadata (%d)",
frameNumber);
+ gotResult = false;
} else {
ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
__FUNCTION__, mId, frameNumber);
}
+ // Append any previous partials to form a complete result
+ if (mUsePartialResultQuirk && !collectedQuirkResult.isEmpty()) {
+ captureResult.append(collectedQuirkResult);
+ }
+
+ captureResult.sort();
+
// Check that there's a timestamp in the result metadata
camera_metadata_entry entry =
@@ -1274,65 +1720,20 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
if (entry.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
frameNumber);
- }
- if (timestamp != entry.data.i64[0]) {
+ gotResult = false;
+ } else if (timestamp != entry.data.i64[0]) {
SET_ERR("Timestamp mismatch between shutter notify and result"
" metadata for frame %d (%lld vs %lld respectively)",
frameNumber, timestamp, entry.data.i64[0]);
+ gotResult = false;
}
- // Get 3A states from result metadata
-
- entry = captureResult.find(ANDROID_CONTROL_AE_STATE);
- if (entry.count == 0) {
- CLOGE("No AE state provided by HAL for frame %d!",
- frameNumber);
- } else {
- new3aState.aeState =
- static_cast<camera_metadata_enum_android_control_ae_state>(
- entry.data.u8[0]);
- }
-
- entry = captureResult.find(ANDROID_CONTROL_AF_STATE);
- if (entry.count == 0) {
- CLOGE("No AF state provided by HAL for frame %d!",
- frameNumber);
- } else {
- new3aState.afState =
- static_cast<camera_metadata_enum_android_control_af_state>(
- entry.data.u8[0]);
- }
-
- entry = captureResult.find(ANDROID_CONTROL_AWB_STATE);
- if (entry.count == 0) {
- CLOGE("No AWB state provided by HAL for frame %d!",
- frameNumber);
- } else {
- new3aState.awbState =
- static_cast<camera_metadata_enum_android_control_awb_state>(
- entry.data.u8[0]);
- }
-
- entry = captureResult.find(ANDROID_CONTROL_AF_TRIGGER_ID);
- if (entry.count == 0) {
- CLOGE("No AF trigger ID provided by HAL for frame %d!",
- frameNumber);
- } else {
- afTriggerId = entry.data.i32[0];
- }
-
- entry = captureResult.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
- if (entry.count == 0) {
- CLOGE("No AE precapture trigger ID provided by HAL"
- " for frame %d!", frameNumber);
- } else {
- aeTriggerId = entry.data.i32[0];
+ if (gotResult) {
+ // Valid result, insert into queue
+ CameraMetadata& queuedResult =
+ *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
+ queuedResult.swap(captureResult);
}
-
- listener = mListener;
- cur3aState = m3AState;
-
- m3AState = new3aState;
} // scope for mOutputLock
// Return completed buffers to their streams with the timestamp
@@ -1344,36 +1745,23 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) {
// Note: stream may be deallocated at this point, if this buffer was the
// last reference to it.
if (res != OK) {
- SET_ERR("Can't return buffer %d for frame %d to its stream: "
+ ALOGE("Can't return buffer %d for frame %d to its stream: "
" %s (%d)", i, frameNumber, strerror(-res), res);
}
}
- // Finally, dispatch any 3A change events to listeners if we got metadata
+ // Finally, signal any waiters for new frames
- if (result->result != NULL) {
+ if (gotResult) {
mResultSignal.signal();
}
- if (result->result != NULL && listener != NULL) {
- if (new3aState.aeState != cur3aState.aeState) {
- ALOGVV("%s: AE state changed from 0x%x to 0x%x",
- __FUNCTION__, cur3aState.aeState, new3aState.aeState);
- listener->notifyAutoExposure(new3aState.aeState, aeTriggerId);
- }
- if (new3aState.afState != cur3aState.afState) {
- ALOGVV("%s: AF state changed from 0x%x to 0x%x",
- __FUNCTION__, cur3aState.afState, new3aState.afState);
- listener->notifyAutoFocus(new3aState.afState, afTriggerId);
- }
- if (new3aState.awbState != cur3aState.awbState) {
- listener->notifyAutoWhitebalance(new3aState.awbState, aeTriggerId);
- }
- }
-
}
+
+
void Camera3Device::notify(const camera3_notify_msg *msg) {
+ ATRACE_CALL();
NotificationListener *listener;
{
Mutex::Autolock l(mOutputLock);
@@ -1394,6 +1782,19 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
msg->message.error.error_stream);
streamId = stream->getId();
}
+ ALOGV("Camera %d: %s: HAL error, frame %d, stream %d: %d",
+ mId, __FUNCTION__, msg->message.error.frame_number,
+ streamId, msg->message.error.error_code);
+
+ // Set request error status for the request in the in-flight tracking
+ {
+ Mutex::Autolock l(mInFlightLock);
+ ssize_t idx = mInFlightMap.indexOfKey(msg->message.error.frame_number);
+ if (idx >= 0) {
+ mInFlightMap.editValueAt(idx).requestStatus = msg->message.error.error_code;
+ }
+ }
+
if (listener != NULL) {
listener->notifyError(msg->message.error.error_code,
msg->message.error.frame_number, streamId);
@@ -1416,12 +1817,17 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
mNextShutterFrameNumber++;
}
+ int32_t requestId = -1;
+
// Set timestamp for the request in the in-flight tracking
+ // and get the request ID to send upstream
{
Mutex::Autolock l(mInFlightLock);
idx = mInFlightMap.indexOfKey(frameNumber);
if (idx >= 0) {
- mInFlightMap.editValueAt(idx).captureTimestamp = timestamp;
+ InFlightRequest &r = mInFlightMap.editValueAt(idx);
+ r.captureTimestamp = timestamp;
+ requestId = r.requestId;
}
}
if (idx < 0) {
@@ -1429,10 +1835,11 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
frameNumber);
break;
}
-
+ ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %lld",
+ mId, __FUNCTION__, frameNumber, requestId, timestamp);
// Call listener, if any
if (listener != NULL) {
- listener->notifyShutter(frameNumber, timestamp);
+ listener->notifyShutter(requestId, timestamp);
}
break;
}
@@ -1442,14 +1849,28 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
}
}
+CameraMetadata Camera3Device::getLatestRequestLocked() {
+ ALOGV("%s", __FUNCTION__);
+
+ CameraMetadata retVal;
+
+ if (mRequestThread != NULL) {
+ retVal = mRequestThread->getLatestRequest();
+ }
+
+ return retVal;
+}
+
/**
* RequestThread inner class methods
*/
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
+ sp<StatusTracker> statusTracker,
camera3_device_t *hal3Device) :
Thread(false),
mParent(parent),
+ mStatusTracker(statusTracker),
mHal3Device(hal3Device),
mId(getId(parent)),
mReconfigured(false),
@@ -1457,6 +1878,7 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
mPaused(true),
mFrameNumber(0),
mLatestRequestId(NAME_NOT_FOUND) {
+ mStatusId = statusTracker->addComponent();
}
void Camera3Device::RequestThread::configurationComplete() {
@@ -1469,6 +1891,8 @@ status_t Camera3Device::RequestThread::queueRequest(
Mutex::Autolock l(mRequestLock);
mRequestQueue.push_back(request);
+ unpauseForNewRequests();
+
return OK;
}
@@ -1534,6 +1958,9 @@ status_t Camera3Device::RequestThread::setRepeatingRequests(
mRepeatingRequests.clear();
mRepeatingRequests.insert(mRepeatingRequests.begin(),
requests.begin(), requests.end());
+
+ unpauseForNewRequests();
+
return OK;
}
@@ -1543,24 +1970,20 @@ status_t Camera3Device::RequestThread::clearRepeatingRequests() {
return OK;
}
+status_t Camera3Device::RequestThread::clear() {
+ Mutex::Autolock l(mRequestLock);
+ mRepeatingRequests.clear();
+ mRequestQueue.clear();
+ mTriggerMap.clear();
+ return OK;
+}
+
void Camera3Device::RequestThread::setPaused(bool paused) {
Mutex::Autolock l(mPauseLock);
mDoPause = paused;
mDoPauseSignal.signal();
}
-status_t Camera3Device::RequestThread::waitUntilPaused(nsecs_t timeout) {
- status_t res;
- Mutex::Autolock l(mPauseLock);
- while (!mPaused) {
- res = mPausedSignal.waitRelative(mPauseLock, timeout);
- if (res == TIMED_OUT) {
- return res;
- }
- }
- return OK;
-}
-
status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
int32_t requestId, nsecs_t timeout) {
Mutex::Autolock l(mLatestRequestMutex);
@@ -1577,7 +2000,13 @@ status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
return OK;
}
-
+void Camera3Device::RequestThread::requestExit() {
+ // Call parent to set up shutdown
+ Thread::requestExit();
+ // The exit from any possible waits
+ mDoPauseSignal.signal();
+ mRequestSignal.signal();
+}
bool Camera3Device::RequestThread::threadLoop() {
@@ -1599,6 +2028,18 @@ bool Camera3Device::RequestThread::threadLoop() {
camera3_capture_request_t request = camera3_capture_request_t();
Vector<camera3_stream_buffer_t> outputBuffers;
+ // Get the request ID, if any
+ int requestId;
+ camera_metadata_entry_t requestIdEntry =
+ nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+ if (requestIdEntry.count > 0) {
+ requestId = requestIdEntry.data.i32[0];
+ } else {
+ ALOGW("%s: Did not have android.request.id set in the request",
+ __FUNCTION__);
+ requestId = NAME_NOT_FOUND;
+ }
+
// Insert any queued triggers (before metadata is locked)
int32_t triggerCount;
res = insertTriggers(nextRequest);
@@ -1616,6 +2057,19 @@ bool Camera3Device::RequestThread::threadLoop() {
// If the request is the same as last, or we had triggers last time
if (mPrevRequest != nextRequest || triggersMixedIn) {
/**
+ * HAL workaround:
+ * Insert a dummy trigger ID if a trigger is set but no trigger ID is
+ */
+ res = addDummyTriggerIds(nextRequest);
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
+ "(capture request %d, HAL device: %s (%d)",
+ (mFrameNumber+1), strerror(-res), res);
+ cleanUpFailedRequest(request, nextRequest, outputBuffers);
+ return false;
+ }
+
+ /**
* The request should be presorted so accesses in HAL
* are O(logn). Sidenote, sorting a sorted metadata is nop.
*/
@@ -1652,7 +2106,7 @@ bool Camera3Device::RequestThread::threadLoop() {
request.input_buffer = &inputBuffer;
res = nextRequest->mInputStream->getInputBuffer(&inputBuffer);
if (res != OK) {
- SET_ERR("RequestThread: Can't get input buffer, skipping request:"
+ ALOGE("RequestThread: Can't get input buffer, skipping request:"
" %s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
@@ -1668,8 +2122,8 @@ bool Camera3Device::RequestThread::threadLoop() {
res = nextRequest->mOutputStreams.editItemAt(i)->
getBuffer(&outputBuffers.editItemAt(i));
if (res != OK) {
- SET_ERR("RequestThread: Can't get output buffer, skipping request:"
- "%s (%d)", strerror(-res), res);
+ ALOGE("RequestThread: Can't get output buffer, skipping request:"
+ " %s (%d)", strerror(-res), res);
cleanUpFailedRequest(request, nextRequest, outputBuffers);
return true;
}
@@ -1686,7 +2140,7 @@ bool Camera3Device::RequestThread::threadLoop() {
return false;
}
- res = parent->registerInFlight(request.frame_number,
+ res = parent->registerInFlight(request.frame_number, requestId,
request.num_output_buffers);
if (res != OK) {
SET_ERR("RequestThread: Unable to register new in-flight request:"
@@ -1695,9 +2149,20 @@ bool Camera3Device::RequestThread::threadLoop() {
return false;
}
- // Submit request and block until ready for next one
+ // Inform waitUntilRequestProcessed thread of a new request ID
+ {
+ Mutex::Autolock al(mLatestRequestMutex);
+ mLatestRequestId = requestId;
+ mLatestRequestSignal.signal();
+ }
+
+ // Submit request and block until ready for next one
+ ATRACE_ASYNC_BEGIN("frame capture", request.frame_number);
+ ATRACE_BEGIN("camera3->process_capture_request");
res = mHal3Device->ops->process_capture_request(mHal3Device, &request);
+ ATRACE_END();
+
if (res != OK) {
SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
" device: %s (%d)", request.frame_number, strerror(-res), res);
@@ -1705,6 +2170,14 @@ bool Camera3Device::RequestThread::threadLoop() {
return false;
}
+ // Update the latest request sent to HAL
+ if (request.settings != NULL) { // Don't update them if they were unchanged
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ camera_metadata_t* cloned = clone_camera_metadata(request.settings);
+ mLatestRequest.acquire(cloned);
+ }
+
if (request.settings != NULL) {
nextRequest->mSettings.unlock(request.settings);
}
@@ -1719,24 +2192,6 @@ bool Camera3Device::RequestThread::threadLoop() {
}
mPrevTriggers = triggerCount;
- // Read android.request.id from the request settings metadata
- // - inform waitUntilRequestProcessed thread of a new request ID
- {
- Mutex::Autolock al(mLatestRequestMutex);
-
- camera_metadata_entry_t requestIdEntry =
- nextRequest->mSettings.find(ANDROID_REQUEST_ID);
- if (requestIdEntry.count > 0) {
- mLatestRequestId = requestIdEntry.data.i32[0];
- } else {
- ALOGW("%s: Did not have android.request.id set in the request",
- __FUNCTION__);
- mLatestRequestId = NAME_NOT_FOUND;
- }
-
- mLatestRequestSignal.signal();
- }
-
// Return input buffer back to framework
if (request.input_buffer != NULL) {
Camera3Stream *stream =
@@ -1752,9 +2207,15 @@ bool Camera3Device::RequestThread::threadLoop() {
}
}
+ return true;
+}
+CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
+ Mutex::Autolock al(mLatestRequestMutex);
- return true;
+ ALOGV("RequestThread::%s", __FUNCTION__);
+
+ return mLatestRequest;
}
void Camera3Device::RequestThread::cleanUpFailedRequest(
@@ -1803,12 +2264,17 @@ sp<Camera3Device::CaptureRequest>
res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);
- if (res == TIMED_OUT) {
- // Signal that we're paused by starvation
+ if ((mRequestQueue.empty() && mRepeatingRequests.empty()) ||
+ exitPending()) {
Mutex::Autolock pl(mPauseLock);
if (mPaused == false) {
+ ALOGV("%s: RequestThread: Going idle", __FUNCTION__);
mPaused = true;
- mPausedSignal.signal();
+ // Let the tracker know
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ }
}
// Stop waiting for now and let thread management happen
return NULL;
@@ -1824,8 +2290,17 @@ sp<Camera3Device::CaptureRequest>
mRequestQueue.erase(firstRequest);
}
- // Not paused
+ // In case we've been unpaused by setPaused clearing mDoPause, need to
+ // update internal pause state (capture/setRepeatingRequest unpause
+ // directly).
Mutex::Autolock pl(mPauseLock);
+ if (mPaused) {
+ ALOGV("%s: RequestThread: Unpaused", __FUNCTION__);
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
mPaused = false;
// Check if we've reconfigured since last time, and reset the preview
@@ -1842,13 +2317,18 @@ bool Camera3Device::RequestThread::waitIfPaused() {
status_t res;
Mutex::Autolock l(mPauseLock);
while (mDoPause) {
- // Signal that we're paused by request
if (mPaused == false) {
mPaused = true;
- mPausedSignal.signal();
+ ALOGV("%s: RequestThread: Paused", __FUNCTION__);
+ // Let the tracker know
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ }
}
+
res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout);
- if (res == TIMED_OUT) {
+ if (res == TIMED_OUT || exitPending()) {
return true;
}
}
@@ -1857,6 +2337,24 @@ bool Camera3Device::RequestThread::waitIfPaused() {
return false;
}
+void Camera3Device::RequestThread::unpauseForNewRequests() {
+ // With work to do, mark thread as unpaused.
+ // If paused by request (setPaused), don't resume, to avoid
+ // extra signaling/waiting overhead to waitUntilPaused
+ mRequestSignal.signal();
+ Mutex::Autolock p(mPauseLock);
+ if (!mDoPause) {
+ ALOGV("%s: RequestThread: Going active", __FUNCTION__);
+ if (mPaused) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
+ mPaused = false;
+ }
+}
+
void Camera3Device::RequestThread::setErrorState(const char *fmt, ...) {
sp<Camera3Device> parent = mParent.promote();
if (parent != NULL) {
@@ -2002,6 +2500,40 @@ status_t Camera3Device::RequestThread::removeTriggers(
return OK;
}
+status_t Camera3Device::RequestThread::addDummyTriggerIds(
+ const sp<CaptureRequest> &request) {
+ // Trigger ID 0 has special meaning in the HAL2 spec, so avoid it here
+ static const int32_t dummyTriggerId = 1;
+ status_t res;
+
+ CameraMetadata &metadata = request->mSettings;
+
+ // If AF trigger is active, insert a dummy AF trigger ID if none already
+ // exists
+ camera_metadata_entry afTrigger = metadata.find(ANDROID_CONTROL_AF_TRIGGER);
+ camera_metadata_entry afId = metadata.find(ANDROID_CONTROL_AF_TRIGGER_ID);
+ if (afTrigger.count > 0 &&
+ afTrigger.data.u8[0] != ANDROID_CONTROL_AF_TRIGGER_IDLE &&
+ afId.count == 0) {
+ res = metadata.update(ANDROID_CONTROL_AF_TRIGGER_ID, &dummyTriggerId, 1);
+ if (res != OK) return res;
+ }
+
+ // If AE precapture trigger is active, insert a dummy precapture trigger ID
+ // if none already exists
+ camera_metadata_entry pcTrigger =
+ metadata.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
+ camera_metadata_entry pcId = metadata.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
+ if (pcTrigger.count > 0 &&
+ pcTrigger.data.u8[0] != ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE &&
+ pcId.count == 0) {
+ res = metadata.update(ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &dummyTriggerId, 1);
+ if (res != OK) return res;
+ }
+
+ return OK;
+}
/**
diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index faa42b9..468f641 100644
--- a/services/camera/libcameraservice/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -22,13 +22,11 @@
#include <utils/List.h>
#include <utils/Mutex.h>
#include <utils/Thread.h>
+#include <utils/KeyedVector.h>
+#include <hardware/camera3.h>
-#include "CameraDeviceBase.h"
-#include "camera3/Camera3Stream.h"
-#include "camera3/Camera3OutputStream.h"
-#include "camera3/Camera3ZslStream.h"
-
-#include "hardware/camera3.h"
+#include "common/CameraDeviceBase.h"
+#include "device3/StatusTracker.h"
/**
* Function pointer types with C calling convention to
@@ -46,6 +44,15 @@ extern "C" {
namespace android {
+namespace camera3 {
+
+class Camera3Stream;
+class Camera3ZslStream;
+class Camera3OutputStreamInterface;
+class Camera3StreamInterface;
+
+}
+
/**
* CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0
*/
@@ -107,6 +114,7 @@ class Camera3Device :
virtual status_t waitUntilDrained();
virtual status_t setNotifyCallback(NotificationListener *listener);
+ virtual bool willNotify3A();
virtual status_t waitForNextFrame(nsecs_t timeout);
virtual status_t getNextFrame(CameraMetadata *frame);
@@ -117,27 +125,49 @@ class Camera3Device :
virtual status_t pushReprocessBuffer(int reprocessStreamId,
buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
+ virtual status_t flush();
+
+ // Methods called by subclasses
+ void notifyStatus(bool idle); // updates from StatusTracker
+
private:
+ static const size_t kDumpLockAttempts = 10;
+ static const size_t kDumpSleepDuration = 100000; // 0.10 sec
static const size_t kInFlightWarnLimit = 20;
static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
+ static const nsecs_t kActiveTimeout = 500000000; // 500 ms
struct RequestTrigger;
+ // A lock to enforce serialization on the input/configure side
+ // of the public interface.
+ // Only locked by public methods inherited from CameraDeviceBase.
+ // Not locked by methods guarded by mOutputLock, since they may act
+ // concurrently to the input/configure side of the interface.
+ // Must be locked before mLock if both will be locked by a method
+ Mutex mInterfaceLock;
+
+ // The main lock on internal state
Mutex mLock;
+ // Camera device ID
+ const int mId;
+
/**** Scope for mLock ****/
- const int mId;
camera3_device_t *mHal3Device;
CameraMetadata mDeviceInfo;
vendor_tag_query_ops_t mVendorTagOps;
- enum {
+ enum Status {
STATUS_ERROR,
STATUS_UNINITIALIZED,
- STATUS_IDLE,
+ STATUS_UNCONFIGURED,
+ STATUS_CONFIGURED,
STATUS_ACTIVE
} mStatus;
+ Vector<Status> mRecentStatusUpdates;
+ Condition mStatusChanged;
// Tracking cause of fatal errors when in STATUS_ERROR
String8 mErrorCause;
@@ -151,9 +181,16 @@ class Camera3Device :
int mNextStreamId;
bool mNeedConfig;
+ // Whether to send state updates upstream
+ // Pause when doing transparent reconfiguration
+ bool mPauseStateNotify;
+
// Need to hold on to stream references until configure completes.
Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams;
+ // Whether quirk ANDROID_QUIRKS_USE_PARTIAL_RESULT is enabled
+ bool mUsePartialResultQuirk;
+
/**** End scope for mLock ****/
class CaptureRequest : public LightRefBase<CaptureRequest> {
@@ -166,10 +203,38 @@ class Camera3Device :
typedef List<sp<CaptureRequest> > RequestList;
/**
- * Lock-held version of waitUntilDrained. Will transition to IDLE on
- * success.
+ * Get the last request submitted to the hal by the request thread.
+ *
+ * Takes mLock.
*/
- status_t waitUntilDrainedLocked();
+ virtual CameraMetadata getLatestRequestLocked();
+
+ /**
+ * Pause processing and flush everything, but don't tell the clients.
+ * This is for reconfiguring outputs transparently when according to the
+ * CameraDeviceBase interface we shouldn't need to.
+ * Must be called with mLock and mInterfaceLock both held.
+ */
+ status_t internalPauseAndWaitLocked();
+
+ /**
+ * Resume work after internalPauseAndWaitLocked()
+ * Must be called with mLock and mInterfaceLock both held.
+ */
+ status_t internalResumeLocked();
+
+ /**
+ * Wait until status tracker tells us we've transitioned to the target state
+ * set, which is either ACTIVE when active==true or IDLE (which is any
+ * non-ACTIVE state) when active==false.
+ *
+ * Needs to be called with mLock and mInterfaceLock held. This means there
+ * can ever only be one waiter at most.
+ *
+ * During the wait mLock is released.
+ *
+ */
+ status_t waitUntilStateThenRelock(bool active, nsecs_t timeout);
/**
* Do common work for setting up a streaming or single capture request.
@@ -199,6 +264,12 @@ class Camera3Device :
void setErrorStateLocked(const char *fmt, ...);
void setErrorStateLockedV(const char *fmt, va_list args);
+ /**
+ * Debugging trylock/spin method
+ * Try to acquire a lock a few times with sleeps between before giving up.
+ */
+ bool tryLockSpinRightRound(Mutex& lock);
+
struct RequestTrigger {
// Metadata tag number, e.g. android.control.aePrecaptureTrigger
uint32_t metadataTag;
@@ -224,6 +295,7 @@ class Camera3Device :
public:
RequestThread(wp<Camera3Device> parent,
+ sp<camera3::StatusTracker> statusTracker,
camera3_device_t *hal3Device);
/**
@@ -242,6 +314,11 @@ class Camera3Device :
status_t queueRequest(sp<CaptureRequest> request);
/**
+ * Remove all queued and repeating requests, and pending triggers
+ */
+ status_t clear();
+
+ /**
* Queue a trigger to be dispatched with the next outgoing
* process_capture_request. The settings for that request only
* will be temporarily rewritten to add the trigger tag/value.
@@ -256,13 +333,6 @@ class Camera3Device :
void setPaused(bool paused);
/**
- * Wait until thread is paused, either due to setPaused(true)
- * or due to lack of input requests. Returns TIMED_OUT in case
- * the thread does not pause within the timeout.
- */
- status_t waitUntilPaused(nsecs_t timeout);
-
- /**
* Wait until thread processes the capture request with settings'
* android.request.id == requestId.
*
@@ -271,6 +341,18 @@ class Camera3Device :
*/
status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
+ /**
+ * Shut down the thread. Shutdown is asynchronous, so thread may
+ * still be running once this method returns.
+ */
+ virtual void requestExit();
+
+ /**
+ * Get the latest request that was sent to the HAL
+ * with process_capture_request.
+ */
+ CameraMetadata getLatestRequest() const;
+
protected:
virtual bool threadLoop();
@@ -285,6 +367,10 @@ class Camera3Device :
// restoring the old field values for those tags.
status_t removeTriggers(const sp<CaptureRequest> &request);
+ // HAL workaround: Make sure a trigger ID always exists if
+ // a trigger does
+ status_t addDummyTriggerIds(const sp<CaptureRequest> &request);
+
static const nsecs_t kRequestTimeout = 50e6; // 50 ms
// Waits for a request, or returns NULL if times out.
@@ -300,14 +386,18 @@ class Camera3Device :
// Pause handling
bool waitIfPaused();
+ void unpauseForNewRequests();
// Relay error to parent device object setErrorState
void setErrorState(const char *fmt, ...);
wp<Camera3Device> mParent;
+ wp<camera3::StatusTracker> mStatusTracker;
camera3_device_t *mHal3Device;
- const int mId;
+ const int mId; // The camera ID
+ int mStatusId; // The RequestThread's component ID for
+ // status tracking
Mutex mRequestLock;
Condition mRequestSignal;
@@ -328,10 +418,11 @@ class Camera3Device :
uint32_t mFrameNumber;
- Mutex mLatestRequestMutex;
+ mutable Mutex mLatestRequestMutex;
Condition mLatestRequestSignal;
// android.request.id for latest process_capture_request
int32_t mLatestRequestId;
+ CameraMetadata mLatestRequest;
typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
Mutex mTriggerMutex;
@@ -346,22 +437,42 @@ class Camera3Device :
*/
struct InFlightRequest {
+ // android.request.id for the request
+ int requestId;
// Set by notify() SHUTTER call.
nsecs_t captureTimestamp;
+ int requestStatus;
// Set by process_capture_result call with valid metadata
bool haveResultMetadata;
// Decremented by calls to process_capture_result with valid output
// buffers
int numBuffersLeft;
+ // Fields used by the partial result quirk only
+ struct PartialResultQuirkInFlight {
+ // Set by process_capture_result once 3A has been sent to clients
+ bool haveSent3A;
+ // Result metadata collected so far, when partial results are in use
+ CameraMetadata collectedResult;
+
+ PartialResultQuirkInFlight():
+ haveSent3A(false) {
+ }
+ } partialResultQuirk;
+
+ // Default constructor needed by KeyedVector
InFlightRequest() :
+ requestId(0),
captureTimestamp(0),
+ requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(0) {
}
- explicit InFlightRequest(int numBuffers) :
+ InFlightRequest(int id, int numBuffers) :
+ requestId(id),
captureTimestamp(0),
+ requestStatus(OK),
haveResultMetadata(false),
numBuffersLeft(numBuffers) {
}
@@ -372,7 +483,29 @@ class Camera3Device :
Mutex mInFlightLock; // Protects mInFlightMap
InFlightMap mInFlightMap;
- status_t registerInFlight(int32_t frameNumber, int32_t numBuffers);
+ status_t registerInFlight(int32_t frameNumber, int32_t requestId,
+ int32_t numBuffers);
+
+ /**
+ * For the partial result quirk, check if all 3A state fields are available
+ * and if so, queue up 3A-only result to the client. Returns true if 3A
+ * is sent.
+ */
+ bool processPartial3AQuirk(int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial);
+
+ // Helpers for reading and writing 3A metadata into to/from partial results
+ template<typename T>
+ bool get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber);
+
+ template<typename T>
+ bool insert3AResult(CameraMetadata &result, int32_t tag, const T* value,
+ int32_t frameNumber);
+ /**
+ * Tracking for idle detection
+ */
+ sp<camera3::StatusTracker> mStatusTracker;
/**
* Output result queue and current HAL device 3A state
@@ -389,18 +522,6 @@ class Camera3Device :
Condition mResultSignal;
NotificationListener *mListener;
- struct AlgState {
- camera_metadata_enum_android_control_ae_state aeState;
- camera_metadata_enum_android_control_af_state afState;
- camera_metadata_enum_android_control_awb_state awbState;
-
- AlgState() :
- aeState(ANDROID_CONTROL_AE_STATE_INACTIVE),
- afState(ANDROID_CONTROL_AF_STATE_INACTIVE),
- awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE) {
- }
- } m3AState;
-
/**** End scope for mOutputLock ****/
/**
diff --git a/services/camera/libcameraservice/camera3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 0850566..da51228 100644
--- a/services/camera/libcameraservice/camera3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -23,7 +23,8 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "Camera3IOStreamBase.h"
+#include "device3/Camera3IOStreamBase.h"
+#include "device3/StatusTracker.h"
namespace android {
@@ -62,53 +63,6 @@ bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const {
return false;
}
-status_t Camera3IOStreamBase::waitUntilIdle(nsecs_t timeout) {
- status_t res;
- {
- Mutex::Autolock l(mLock);
- while (mDequeuedBufferCount > 0) {
- if (timeout != TIMEOUT_NEVER) {
- nsecs_t startTime = systemTime();
- res = mBufferReturnedSignal.waitRelative(mLock, timeout);
- if (res == TIMED_OUT) {
- return res;
- } else if (res != OK) {
- ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- nsecs_t deltaTime = systemTime() - startTime;
- if (timeout <= deltaTime) {
- timeout = 0;
- } else {
- timeout -= deltaTime;
- }
- } else {
- res = mBufferReturnedSignal.wait(mLock);
- if (res != OK) {
- ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
- }
- }
- }
-
- // No lock
-
- unsigned int timeoutMs;
- if (timeout == TIMEOUT_NEVER) {
- timeoutMs = Fence::TIMEOUT_NEVER;
- } else if (timeout == 0) {
- timeoutMs = 0;
- } else {
- // Round up to wait at least 1 ms
- timeoutMs = (timeout + 999999) / 1000000;
- }
-
- return mCombinedFence->wait(timeoutMs);
-}
-
void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {
(void) args;
String8 lines;
@@ -190,6 +144,14 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer,
buffer.release_fence = releaseFence;
buffer.status = status;
+ // Inform tracker about becoming busy
+ if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+ mState != STATE_IN_RECONFIG) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentActive(mStatusId);
+ }
+ }
mDequeuedBufferCount++;
}
@@ -252,20 +214,32 @@ status_t Camera3IOStreamBase::returnAnyBufferLocked(
sp<Fence> releaseFence;
res = returnBufferCheckedLocked(buffer, timestamp, output,
&releaseFence);
- if (res != OK) {
- return res;
- }
+ // Res may be an error, but we still want to decrement our owned count
+ // to enable clean shutdown. So we'll just return the error but otherwise
+ // carry on
- mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence);
+ if (releaseFence != 0) {
+ mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence);
+ }
mDequeuedBufferCount--;
+ if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+ mState != STATE_IN_RECONFIG) {
+ ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,
+ mId);
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->markComponentIdle(mStatusId, mCombinedFence);
+ }
+ }
+
mBufferReturnedSignal.signal();
if (output) {
mLastTimestamp = timestamp;
}
- return OK;
+ return res;
}
diff --git a/services/camera/libcameraservice/camera3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 74c4484..fcb9d04 100644
--- a/services/camera/libcameraservice/camera3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -43,7 +43,6 @@ class Camera3IOStreamBase :
* Camera3Stream interface
*/
- virtual status_t waitUntilIdle(nsecs_t timeout);
virtual void dump(int fd, const Vector<String16> &args) const;
protected:
@@ -77,6 +76,8 @@ class Camera3IOStreamBase :
virtual size_t getBufferCountLocked();
+ virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+
status_t getBufferPreconditionCheckLocked() const;
status_t returnBufferPreconditionCheckLocked() const;
diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 13e9c83..5aa9a3e 100644
--- a/services/camera/libcameraservice/camera3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -115,7 +115,6 @@ status_t Camera3InputStream::returnBufferCheckedLocked(
bufferFound = true;
bufferItem = tmp;
mBuffersInFlight.erase(it);
- mDequeuedBufferCount--;
}
}
}
@@ -148,12 +147,11 @@ status_t Camera3InputStream::returnBufferCheckedLocked(
if (res != OK) {
ALOGE("%s: Stream %d: Error releasing buffer back to buffer queue:"
" %s (%d)", __FUNCTION__, mId, strerror(-res), res);
- return res;
}
*releaseFenceOut = releaseFence;
- return OK;
+ return res;
}
status_t Camera3InputStream::returnInputBufferLocked(
@@ -182,10 +180,6 @@ status_t Camera3InputStream::disconnectLocked() {
return OK;
}
-sp<IGraphicBufferProducer> Camera3InputStream::getProducerInterface() const {
- return mConsumer->getProducerInterface();
-}
-
void Camera3InputStream::dump(int fd, const Vector<String16> &args) const {
(void) args;
String8 lines;
@@ -211,9 +205,9 @@ status_t Camera3InputStream::configureQueueLocked() {
mFrameCount = 0;
if (mConsumer.get() == 0) {
- mConsumer = new BufferItemConsumer(camera3_stream::usage,
- mTotalBufferCount,
- /*synchronousMode*/true);
+ sp<BufferQueue> bq = new BufferQueue();
+ mConsumer = new BufferItemConsumer(bq, camera3_stream::usage,
+ mTotalBufferCount);
mConsumer->setName(String8::format("Camera3-InputStream-%d", mId));
}
@@ -234,6 +228,12 @@ status_t Camera3InputStream::configureQueueLocked() {
return OK;
}
+status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) {
+ // Per HAL3 spec, input streams have 0 for their initial usage field.
+ *usage = 0;
+ return OK;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h
index 8adda88..681d684 100644
--- a/services/camera/libcameraservice/camera3/Camera3InputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.h
@@ -44,13 +44,6 @@ class Camera3InputStream : public Camera3IOStreamBase {
virtual void dump(int fd, const Vector<String16> &args) const;
- /**
- * Get the producer interface for this stream, to hand off to a producer.
- * The producer must be connected to the provided interface before
- * finishConfigure is called on this stream.
- */
- sp<IGraphicBufferProducer> getProducerInterface() const;
-
private:
typedef BufferItemConsumer::BufferItem BufferItem;
@@ -79,6 +72,8 @@ class Camera3InputStream : public Camera3IOStreamBase {
virtual status_t configureQueueLocked();
+ virtual status_t getEndpointUsage(uint32_t *usage);
+
}; // class Camera3InputStream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 2efeede..682755d 100644
--- a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -92,7 +92,22 @@ status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) {
ANativeWindowBuffer* anb;
int fenceFd;
- res = mConsumer->dequeueBuffer(mConsumer.get(), &anb, &fenceFd);
+ /**
+ * Release the lock briefly to avoid deadlock for below scenario:
+ * Thread 1: StreamingProcessor::startStream -> Camera3Stream::isConfiguring().
+ * This thread acquired StreamingProcessor lock and try to lock Camera3Stream lock.
+ * Thread 2: Camera3Stream::returnBuffer->StreamingProcessor::onFrameAvailable().
+ * This thread acquired Camera3Stream lock and bufferQueue lock, and try to lock
+ * StreamingProcessor lock.
+ * Thread 3: Camera3Stream::getBuffer(). This thread acquired Camera3Stream lock
+ * and try to lock bufferQueue lock.
+ * Then there is circular locking dependency.
+ */
+ sp<ANativeWindow> currentConsumer = mConsumer;
+ mLock.unlock();
+
+ res = currentConsumer->dequeueBuffer(currentConsumer.get(), &anb, &fenceFd);
+ mLock.lock();
if (res != OK) {
ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -198,12 +213,11 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
mLock.lock();
if (res != OK) {
close(anwReleaseFence);
- return res;
}
*releaseFenceOut = releaseFence;
- return OK;
+ return res;
}
void Camera3OutputStream::dump(int fd, const Vector<String16> &args) const {
@@ -301,8 +315,13 @@ status_t Camera3OutputStream::configureQueueLocked() {
return res;
}
- ALOGV("%s: Consumer wants %d buffers", __FUNCTION__,
- maxConsumerBuffers);
+ ALOGV("%s: Consumer wants %d buffers, HAL wants %d", __FUNCTION__,
+ maxConsumerBuffers, camera3_stream::max_buffers);
+ if (camera3_stream::max_buffers == 0) {
+ ALOGE("%s: Camera HAL requested max_buffer count: %d, requires at least 1",
+ __FUNCTION__, camera3_stream::max_buffers);
+ return INVALID_OPERATION;
+ }
mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers;
mDequeuedBufferCount = 0;
@@ -359,6 +378,17 @@ status_t Camera3OutputStream::disconnectLocked() {
return OK;
}
+status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) {
+
+ status_t res;
+ int32_t u = 0;
+ res = mConsumer->query(mConsumer.get(),
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS, &u);
+ *usage = u;
+
+ return res;
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 774fbdd..6cbb9f4 100644
--- a/services/camera/libcameraservice/camera3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -92,6 +92,9 @@ class Camera3OutputStream :
virtual status_t configureQueueLocked();
virtual status_t disconnectLocked();
+
+ virtual status_t getEndpointUsage(uint32_t *usage);
+
}; // class Camera3OutputStream
} // namespace camera3
diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index aae72cf..aae72cf 100644
--- a/services/camera/libcameraservice/camera3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index f05658a..6d2cf94 100644
--- a/services/camera/libcameraservice/camera3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -20,13 +20,18 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "Camera3Stream.h"
+#include "device3/Camera3Stream.h"
+#include "device3/StatusTracker.h"
namespace android {
namespace camera3 {
Camera3Stream::~Camera3Stream() {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+ statusTracker->removeComponent(mStatusId);
+ }
}
Camera3Stream* Camera3Stream::cast(camera3_stream *stream) {
@@ -44,7 +49,8 @@ Camera3Stream::Camera3Stream(int id,
mId(id),
mName(String8::format("Camera3Stream[%d]", id)),
mMaxSize(maxSize),
- mState(STATE_CONSTRUCTED) {
+ mState(STATE_CONSTRUCTED),
+ mStatusId(StatusTracker::NO_STATUS_ID) {
camera3_stream::stream_type = type;
camera3_stream::width = width;
@@ -77,7 +83,9 @@ int Camera3Stream::getFormat() const {
}
camera3_stream* Camera3Stream::startConfiguration() {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
+ status_t res;
switch (mState) {
case STATE_ERROR:
@@ -107,8 +115,24 @@ camera3_stream* Camera3Stream::startConfiguration() {
return NULL;
}
- oldUsage = usage;
- oldMaxBuffers = max_buffers;
+ oldUsage = camera3_stream::usage;
+ oldMaxBuffers = camera3_stream::max_buffers;
+
+ res = getEndpointUsage(&(camera3_stream::usage));
+ if (res != OK) {
+ ALOGE("%s: Cannot query consumer endpoint usage!",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ // Stop tracking if currently doing so
+ if (mStatusId != StatusTracker::NO_STATUS_ID) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ statusTracker->removeComponent(mStatusId);
+ }
+ mStatusId = StatusTracker::NO_STATUS_ID;
+ }
if (mState == STATE_CONSTRUCTED) {
mState = STATE_IN_CONFIG;
@@ -125,6 +149,7 @@ bool Camera3Stream::isConfiguring() const {
}
status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) {
+ ATRACE_CALL();
Mutex::Autolock l(mLock);
switch (mState) {
case STATE_ERROR:
@@ -144,11 +169,17 @@ status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) {
return INVALID_OPERATION;
}
+ // Register for idle tracking
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != 0) {
+ mStatusId = statusTracker->addComponent();
+ }
+
// Check if the stream configuration is unchanged, and skip reallocation if
// so. As documented in hardware/camera3.h:configure_streams().
if (mState == STATE_IN_RECONFIG &&
- oldUsage == usage &&
- oldMaxBuffers == max_buffers) {
+ oldUsage == camera3_stream::usage &&
+ oldMaxBuffers == camera3_stream::max_buffers) {
mState = STATE_CONFIGURED;
return OK;
}
@@ -255,6 +286,18 @@ bool Camera3Stream::hasOutstandingBuffers() const {
return hasOutstandingBuffersLocked();
}
+status_t Camera3Stream::setStatusTracker(sp<StatusTracker> statusTracker) {
+ Mutex::Autolock l(mLock);
+ sp<StatusTracker> oldTracker = mStatusTracker.promote();
+ if (oldTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+ oldTracker->removeComponent(mStatusId);
+ }
+ mStatusId = StatusTracker::NO_STATUS_ID;
+ mStatusTracker = statusTracker;
+
+ return OK;
+}
+
status_t Camera3Stream::disconnect() {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -312,8 +355,10 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) {
// Got all buffers, register with HAL
ALOGV("%s: Registering %d buffers with camera HAL",
__FUNCTION__, bufferCount);
+ ATRACE_BEGIN("camera3->register_stream_buffers");
res = hal3Device->ops->register_stream_buffers(hal3Device,
&bufferSet);
+ ATRACE_END();
}
// Return all valid buffers to stream, in ERROR state to indicate
diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 69d81e4..6eeb721 100644
--- a/services/camera/libcameraservice/camera3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -190,12 +190,11 @@ class Camera3Stream :
enum {
TIMEOUT_NEVER = -1
};
+
/**
- * Wait until the HAL is done with all of this stream's buffers, including
- * signalling all release fences. Returns TIMED_OUT if the timeout is exceeded,
- * OK on success. Pass in TIMEOUT_NEVER for timeout to indicate an indefinite wait.
+ * Set the status tracker to notify about idle transitions
*/
- virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+ virtual status_t setStatusTracker(sp<StatusTracker> statusTracker);
/**
* Disconnect stream from its non-HAL endpoint. After this,
@@ -263,6 +262,15 @@ class Camera3Stream :
// Get the total number of buffers in the queue
virtual size_t getBufferCountLocked() = 0;
+ // Get the usage flags for the other endpoint, or return
+ // INVALID_OPERATION if they cannot be obtained.
+ virtual status_t getEndpointUsage(uint32_t *usage) = 0;
+
+ // Tracking for idle state
+ wp<StatusTracker> mStatusTracker;
+ // Status tracker component ID
+ int mStatusId;
+
private:
uint32_t oldUsage;
uint32_t oldMaxBuffers;
diff --git a/services/camera/libcameraservice/camera3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
index 62ea6c0..62ea6c0 100644
--- a/services/camera/libcameraservice/camera3/Camera3StreamBufferListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
diff --git a/services/camera/libcameraservice/camera3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 4768536..c93ae15 100644
--- a/services/camera/libcameraservice/camera3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -26,6 +26,8 @@ namespace android {
namespace camera3 {
+class StatusTracker;
+
/**
* An interface for managing a single stream of input and/or output data from
* the camera device.
@@ -128,13 +130,11 @@ class Camera3StreamInterface : public virtual RefBase {
enum {
TIMEOUT_NEVER = -1
};
+
/**
- * Wait until the HAL is done with all of this stream's buffers, including
- * signalling all release fences. Returns TIMED_OUT if the timeout is
- * exceeded, OK on success. Pass in TIMEOUT_NEVER for timeout to indicate
- * an indefinite wait.
+ * Set the state tracker to use for signaling idle transitions.
*/
- virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+ virtual status_t setStatusTracker(sp<StatusTracker> statusTracker) = 0;
/**
* Disconnect stream from its non-HAL endpoint. After this,
diff --git a/services/camera/libcameraservice/camera3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 8790c8c..04f5dc5 100644
--- a/services/camera/libcameraservice/camera3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -113,11 +113,11 @@ Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height,
Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL,
width, height,
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
- mDepth(depth),
- mProducer(new RingBufferConsumer(GRALLOC_USAGE_HW_CAMERA_ZSL,
- depth)) {
+ mDepth(depth) {
- mConsumer = new Surface(mProducer->getProducerInterface());
+ sp<BufferQueue> bq = new BufferQueue();
+ mProducer = new RingBufferConsumer(bq, GRALLOC_USAGE_HW_CAMERA_ZSL, depth);
+ mConsumer = new Surface(bq);
}
Camera3ZslStream::~Camera3ZslStream() {
diff --git a/services/camera/libcameraservice/camera3/Camera3ZslStream.h b/services/camera/libcameraservice/device3/Camera3ZslStream.h
index c7f4490..c7f4490 100644
--- a/services/camera/libcameraservice/camera3/Camera3ZslStream.h
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.h
diff --git a/services/camera/libcameraservice/device3/StatusTracker.cpp b/services/camera/libcameraservice/device3/StatusTracker.cpp
new file mode 100644
index 0000000..ab5419f
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 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 "Camera3-Status"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <ui/Fence.h>
+
+#include "device3/StatusTracker.h"
+#include "device3/Camera3Device.h"
+
+namespace android {
+
+namespace camera3 {
+
+StatusTracker::StatusTracker(wp<Camera3Device> parent) :
+ mComponentsChanged(false),
+ mParent(parent),
+ mNextComponentId(0),
+ mIdleFence(new Fence()),
+ mDeviceState(IDLE) {
+}
+
+StatusTracker::~StatusTracker() {
+}
+
+int StatusTracker::addComponent() {
+ int id;
+ ssize_t err;
+ {
+ Mutex::Autolock l(mLock);
+ id = mNextComponentId++;
+ ALOGV("%s: Adding new component %d", __FUNCTION__, id);
+
+ err = mStates.add(id, IDLE);
+ ALOGE_IF(err < 0, "%s: Can't add new component %d: %s (%d)",
+ __FUNCTION__, id, strerror(-err), err);
+ }
+
+ if (err >= 0) {
+ Mutex::Autolock pl(mPendingLock);
+ mComponentsChanged = true;
+ mPendingChangeSignal.signal();
+ }
+
+ return err < 0 ? err : id;
+}
+
+void StatusTracker::removeComponent(int id) {
+ ssize_t idx;
+ {
+ Mutex::Autolock l(mLock);
+ ALOGV("%s: Removing component %d", __FUNCTION__, id);
+ idx = mStates.removeItem(id);
+ }
+
+ if (idx >= 0) {
+ Mutex::Autolock pl(mPendingLock);
+ mComponentsChanged = true;
+ mPendingChangeSignal.signal();
+ }
+
+ return;
+}
+
+
+void StatusTracker::markComponentIdle(int id, const sp<Fence>& componentFence) {
+ markComponent(id, IDLE, componentFence);
+}
+
+void StatusTracker::markComponentActive(int id) {
+ markComponent(id, ACTIVE, Fence::NO_FENCE);
+}
+
+void StatusTracker::markComponent(int id, ComponentState state,
+ const sp<Fence>& componentFence) {
+ ALOGV("%s: Component %d is now %s", __FUNCTION__, id,
+ state == IDLE ? "idle" : "active");
+ Mutex::Autolock l(mPendingLock);
+
+ StateChange newState = {
+ id,
+ state,
+ componentFence
+ };
+
+ mPendingChangeQueue.add(newState);
+ mPendingChangeSignal.signal();
+}
+
+void StatusTracker::requestExit() {
+ // First mark thread dead
+ Thread::requestExit();
+ // Then exit any waits
+ mPendingChangeSignal.signal();
+}
+
+StatusTracker::ComponentState StatusTracker::getDeviceStateLocked() {
+ for (size_t i = 0; i < mStates.size(); i++) {
+ if (mStates.valueAt(i) == ACTIVE) {
+ ALOGV("%s: Component %d not idle", __FUNCTION__,
+ mStates.keyAt(i));
+ return ACTIVE;
+ }
+ }
+ // - If not yet signaled, getSignalTime returns INT64_MAX
+ // - If invalid fence or error, returns -1
+ // - Otherwise returns time of signalling.
+ // Treat -1 as 'signalled', since HAL may not be using fences, and want
+ // to be able to idle in case of errors.
+ nsecs_t signalTime = mIdleFence->getSignalTime();
+ bool fencesDone = signalTime != INT64_MAX;
+
+ ALOGV_IF(!fencesDone, "%s: Fences still to wait on", __FUNCTION__);
+
+ return fencesDone ? IDLE : ACTIVE;
+}
+
+bool StatusTracker::threadLoop() {
+ status_t res;
+
+ // Wait for state updates
+ {
+ Mutex::Autolock pl(mPendingLock);
+ while (mPendingChangeQueue.size() == 0 && !mComponentsChanged) {
+ res = mPendingChangeSignal.waitRelative(mPendingLock,
+ kWaitDuration);
+ if (exitPending()) return false;
+ if (res != OK) {
+ if (res != TIMED_OUT) {
+ ALOGE("%s: Error waiting on state changes: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
+ // TIMED_OUT is expected
+ break;
+ }
+ }
+ }
+
+ // After new pending states appear, or timeout, check if we're idle. Even
+ // with timeout, need to check to account for fences that may still be
+ // clearing out
+ sp<Camera3Device> parent;
+ {
+ Mutex::Autolock pl(mPendingLock);
+ Mutex::Autolock l(mLock);
+
+ // Collect all pending state updates and see if the device
+ // collectively transitions between idle and active for each one
+
+ // First pass for changed components or fence completions
+ ComponentState prevState = getDeviceStateLocked();
+ if (prevState != mDeviceState) {
+ // Only collect changes to overall device state
+ mStateTransitions.add(prevState);
+ }
+ // For each pending component state update, check if we've transitioned
+ // to a new overall device state
+ for (size_t i = 0; i < mPendingChangeQueue.size(); i++) {
+ const StateChange &newState = mPendingChangeQueue[i];
+ ssize_t idx = mStates.indexOfKey(newState.id);
+ // Ignore notices for unknown components
+ if (idx >= 0) {
+ // Update single component state
+ mStates.replaceValueAt(idx, newState.state);
+ mIdleFence = Fence::merge(String8("idleFence"),
+ mIdleFence, newState.fence);
+ // .. and see if overall device state has changed
+ ComponentState newState = getDeviceStateLocked();
+ if (newState != prevState) {
+ mStateTransitions.add(newState);
+ }
+ prevState = newState;
+ }
+ }
+ mPendingChangeQueue.clear();
+ mComponentsChanged = false;
+
+ // Store final state after all pending state changes are done with
+
+ mDeviceState = prevState;
+ parent = mParent.promote();
+ }
+
+ // Notify parent for all intermediate transitions
+ if (mStateTransitions.size() > 0 && parent.get()) {
+ for (size_t i = 0; i < mStateTransitions.size(); i++) {
+ bool idle = (mStateTransitions[i] == IDLE);
+ ALOGV("Camera device is now %s", idle ? "idle" : "active");
+ parent->notifyStatus(idle);
+ }
+ }
+ mStateTransitions.clear();
+
+ return true;
+}
+
+} // namespace android
+
+} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/StatusTracker.h b/services/camera/libcameraservice/device3/StatusTracker.h
new file mode 100644
index 0000000..49cecb3
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 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_CAMERA3_STATUSTRACKER_H
+#define ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+#include <utils/KeyedVector.h>
+#include <hardware/camera3.h>
+
+#include "common/CameraDeviceBase.h"
+
+namespace android {
+
+class Camera3Device;
+class Fence;
+
+namespace camera3 {
+
+/**
+ * State tracking for idle and other collective state transitions.
+ * Collects idle notifications from different sources and calls the
+ * parent when all of them become idle.
+ *
+ * The parent is responsible for synchronizing the status updates with its
+ * internal state correctly, which means the notifyStatus call to the parent may
+ * block for a while.
+ */
+class StatusTracker: public Thread {
+ public:
+ StatusTracker(wp<Camera3Device> parent);
+ ~StatusTracker();
+
+ // An always-invalid component ID
+ static const int NO_STATUS_ID = -1;
+
+ // Add a component to track; returns non-negative unique ID for the new
+ // component on success, negative error code on failure.
+ // New components start in the idle state.
+ int addComponent();
+
+ // Remove existing component from idle tracking. Ignores unknown IDs
+ void removeComponent(int id);
+
+ // Set the state of a tracked component to be idle. Ignores unknown IDs; can
+ // accept a fence to wait on to complete idle. The fence is merged with any
+ // previous fences given, which means they all must signal before the
+ // component is considered idle.
+ void markComponentIdle(int id, const sp<Fence>& componentFence);
+
+ // Set the state of a tracked component to be active. Ignores unknown IDs.
+ void markComponentActive(int id);
+
+ virtual void requestExit();
+ protected:
+
+ virtual bool threadLoop();
+
+ private:
+ enum ComponentState {
+ IDLE,
+ ACTIVE
+ };
+
+ void markComponent(int id, ComponentState state,
+ const sp<Fence>& componentFence);
+
+ // Guards mPendingChange, mPendingStates, mComponentsChanged
+ Mutex mPendingLock;
+
+ Condition mPendingChangeSignal;
+
+ struct StateChange {
+ int id;
+ ComponentState state;
+ sp<Fence> fence;
+ };
+ // A queue of yet-to-be-processed state changes to components
+ Vector<StateChange> mPendingChangeQueue;
+ bool mComponentsChanged;
+
+ wp<Camera3Device> mParent;
+
+ // Guards rest of internals. Must be locked after mPendingLock if both used.
+ Mutex mLock;
+
+ int mNextComponentId;
+
+ // Current component states
+ KeyedVector<int, ComponentState> mStates;
+ // Merged fence for all processed state changes
+ sp<Fence> mIdleFence;
+ // Current overall device state
+ ComponentState mDeviceState;
+
+ // Private to threadLoop
+
+ // Determine current overall device state
+ // We're IDLE iff
+ // - All components are currently IDLE
+ // - The merged fence for all component updates has signalled
+ ComponentState getDeviceStateLocked();
+
+ Vector<ComponentState> mStateTransitions;
+
+ static const nsecs_t kWaitDuration = 250000000LL; // 250 ms
+};
+
+} // namespace camera3
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index cd39bad..ebc7ea7 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -34,14 +34,14 @@ typedef android::RingBufferConsumer::PinnedBufferItem PinnedBufferItem;
namespace android {
-RingBufferConsumer::RingBufferConsumer(uint32_t consumerUsage,
+RingBufferConsumer::RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer,
+ uint32_t consumerUsage,
int bufferCount) :
- ConsumerBase(new BufferQueue(true)),
+ ConsumerBase(consumer),
mBufferCount(bufferCount)
{
- mBufferQueue->setConsumerUsageBits(consumerUsage);
- mBufferQueue->setSynchronousMode(true);
- mBufferQueue->setMaxAcquiredBufferCount(bufferCount);
+ mConsumer->setConsumerUsageBits(consumerUsage);
+ mConsumer->setMaxAcquiredBufferCount(bufferCount);
assert(bufferCount > 0);
}
@@ -52,7 +52,7 @@ RingBufferConsumer::~RingBufferConsumer() {
void RingBufferConsumer::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name;
- mBufferQueue->setConsumerName(name);
+ mConsumer->setConsumerName(name);
}
sp<PinnedBufferItem> RingBufferConsumer::pinSelectedBuffer(
@@ -214,7 +214,11 @@ status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) {
// In case the object was never pinned, pass the acquire fence
// back to the release fence. If the fence was already waited on,
// it'll just be a no-op to wait on it again.
- err = addReleaseFenceLocked(item.mBuf, item.mFence);
+
+ // item.mGraphicBuffer was populated with the proper graphic-buffer
+ // at acquire even if it was previously acquired
+ err = addReleaseFenceLocked(item.mBuf,
+ item.mGraphicBuffer, item.mFence);
if (err != OK) {
BI_LOGE("Failed to add release fence to buffer "
@@ -226,7 +230,9 @@ status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) {
BI_LOGV("Attempting to release buffer timestamp %lld, frame %lld",
item.mTimestamp, item.mFrameNumber);
- err = releaseBufferLocked(item.mBuf,
+ // item.mGraphicBuffer was populated with the proper graphic-buffer
+ // at acquire even if it was previously acquired
+ err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer,
EGL_NO_DISPLAY,
EGL_NO_SYNC_KHR);
if (err != OK) {
@@ -278,7 +284,7 @@ void RingBufferConsumer::onFrameAvailable() {
/**
* Acquire new frame
*/
- err = acquireBufferLocked(&item);
+ err = acquireBufferLocked(&item, 0);
if (err != OK) {
if (err != NO_BUFFER_AVAILABLE) {
BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
@@ -310,7 +316,8 @@ void RingBufferConsumer::unpinBuffer(const BufferItem& item) {
RingBufferItem& find = *it;
if (item.mGraphicBuffer == find.mGraphicBuffer) {
- status_t res = addReleaseFenceLocked(item.mBuf, item.mFence);
+ status_t res = addReleaseFenceLocked(item.mBuf,
+ item.mGraphicBuffer, item.mFence);
if (res != OK) {
BI_LOGE("Failed to add release fence to buffer "
@@ -336,17 +343,17 @@ void RingBufferConsumer::unpinBuffer(const BufferItem& item) {
status_t RingBufferConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
Mutex::Autolock _l(mMutex);
- return mBufferQueue->setDefaultBufferSize(w, h);
+ return mConsumer->setDefaultBufferSize(w, h);
}
status_t RingBufferConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
Mutex::Autolock _l(mMutex);
- return mBufferQueue->setDefaultBufferFormat(defaultFormat);
+ return mConsumer->setDefaultBufferFormat(defaultFormat);
}
status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) {
Mutex::Autolock _l(mMutex);
- return mBufferQueue->setConsumerUsageBits(usage);
+ return mConsumer->setConsumerUsageBits(usage);
}
} // namespace android
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index 454fbae..b4ad824 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -63,7 +63,7 @@ class RingBufferConsumer : public ConsumerBase,
// the consumer usage flags passed to the graphics allocator. The
// bufferCount parameter specifies how many buffers can be pinned for user
// access at the same time.
- RingBufferConsumer(uint32_t consumerUsage,
+ RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage,
int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS);
virtual ~RingBufferConsumer();
@@ -72,8 +72,6 @@ class RingBufferConsumer : public ConsumerBase,
// log messages.
void setName(const String8& name);
- sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); }
-
// setDefaultBufferSize is used to set the size of buffers returned by
// requestBuffers when a with and height of zero is requested.
status_t setDefaultBufferSize(uint32_t w, uint32_t h);
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
new file mode 100644
index 0000000..346e15f
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 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 "CameraTraces"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "utils/CameraTraces.h"
+#include <utils/ProcessCallStack.h>
+
+#include <utils/Mutex.h>
+#include <utils/List.h>
+
+#include <utils/Log.h>
+#include <cutils/trace.h>
+
+namespace android {
+namespace camera3 {
+
+struct CameraTracesImpl {
+ Mutex tracesLock;
+ List<ProcessCallStack> pcsList;
+}; // class CameraTraces::Impl;
+
+static CameraTracesImpl gImpl;
+CameraTracesImpl& CameraTraces::sImpl = gImpl;
+
+void CameraTraces::saveTrace() {
+ ALOGV("%s: begin", __FUNCTION__);
+ ATRACE_BEGIN("CameraTraces::saveTrace");
+ Mutex::Autolock al(sImpl.tracesLock);
+
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ // Insert new ProcessCallStack, and immediately crawl all the threads
+ pcsList.push_front(ProcessCallStack());
+ ProcessCallStack& pcs = *pcsList.begin();
+ pcs.update();
+
+ if (pcsList.size() > MAX_TRACES) {
+ // Prune list periodically and discard oldest entry
+ pcsList.erase(--pcsList.end());
+ }
+
+ IF_ALOGV() {
+ pcs.log(LOG_TAG, ANDROID_LOG_VERBOSE);
+ }
+
+ ALOGD("Process trace saved. Use dumpsys media.camera to view.");
+
+ ATRACE_END();
+}
+
+status_t CameraTraces::dump(int fd, const Vector<String16> &args __attribute__((unused))) {
+ ALOGV("%s: fd = %d", __FUNCTION__, fd);
+ Mutex::Autolock al(sImpl.tracesLock);
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ if (fd < 0) {
+ ALOGW("%s: Negative FD (%d)", __FUNCTION__, fd);
+ return BAD_VALUE;
+ }
+
+ fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+
+ if (pcsList.empty()) {
+ fdprintf(fd, " No camera traces collected.\n");
+ }
+
+ // Print newest items first
+ List<ProcessCallStack>::iterator it, end;
+ for (it = pcsList.begin(), end = pcsList.end(); it != end; ++it) {
+ const ProcessCallStack& pcs = *it;
+ pcs.dump(fd, DUMP_INDENT);
+ }
+
+ return OK;
+}
+
+}; // namespace camera3
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/CameraTraces.h b/services/camera/libcameraservice/utils/CameraTraces.h
new file mode 100644
index 0000000..d10dbc9
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 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_TRACES_H_
+#define ANDROID_SERVERS_CAMERA_TRACES_H_
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace camera3 {
+
+class CameraTracesImpl;
+
+// Collect a list of the process's stack traces
+class CameraTraces {
+public:
+ /**
+ * Save the current stack trace for each thread in the process. At most
+ * MAX_TRACES will be saved, after which the oldest traces will be discarded.
+ *
+ * <p>Use CameraTraces::dump to print out the traces.</p>
+ */
+ static void saveTrace();
+
+ /**
+ * Prints all saved traces to the specified file descriptor.
+ *
+ * <p>Each line is indented by DUMP_INDENT spaces.</p>
+ */
+ static status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ enum {
+ // Don't collect more than 100 traces. Discard oldest.
+ MAX_TRACES = 100,
+
+ // Insert 2 spaces when dumping the traces
+ DUMP_INDENT = 2,
+ };
+
+ CameraTraces();
+ ~CameraTraces();
+ CameraTraces(CameraTraces& rhs);
+
+ static CameraTracesImpl& sImpl;
+}; // class CameraTraces
+
+}; // namespace camera3
+}; // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_TRACES_H_