diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/audioflinger/Android.mk | 21 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 292 | ||||
-rw-r--r-- | services/audioflinger/AudioFlinger.h | 47 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 1 | ||||
-rw-r--r-- | services/audioflinger/AudioPolicyService.cpp | 158 | ||||
-rw-r--r-- | services/audioflinger/AudioPolicyService.h | 39 | ||||
-rw-r--r-- | services/audioflinger/AudioResampler.h | 11 | ||||
-rw-r--r-- | services/audioflinger/AudioWatchdog.cpp | 5 | ||||
-rw-r--r-- | services/audioflinger/Configuration.h | 47 | ||||
-rw-r--r-- | services/audioflinger/Effects.cpp | 116 | ||||
-rw-r--r-- | services/audioflinger/Effects.h | 14 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.cpp | 246 | ||||
-rw-r--r-- | services/audioflinger/FastMixer.h | 19 | ||||
-rw-r--r-- | services/audioflinger/FastMixerState.cpp | 1 | ||||
-rw-r--r-- | services/audioflinger/PlaybackTracks.h | 32 | ||||
-rw-r--r-- | services/audioflinger/RecordTracks.h | 5 | ||||
-rw-r--r-- | services/audioflinger/ServiceUtilities.cpp | 16 | ||||
-rw-r--r-- | services/audioflinger/ServiceUtilities.h | 2 | ||||
-rw-r--r-- | services/audioflinger/StateQueue.cpp | 1 | ||||
-rw-r--r-- | services/audioflinger/StateQueue.h | 8 | ||||
-rw-r--r-- | services/audioflinger/StateQueueInstantiations.cpp | 1 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 1448 | ||||
-rw-r--r-- | services/audioflinger/Threads.h | 193 | ||||
-rw-r--r-- | services/audioflinger/TrackBase.h | 26 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 727 | ||||
-rw-r--r-- | services/camera/libcameraservice/Android.mk | 50 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraDeviceFactory.cpp | 71 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraDeviceFactory.h | 45 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 260 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 66 | ||||
-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.h | 111 | ||||
-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.cpp | 680 | ||||
-rw-r--r-- | services/camera/libcameraservice/api2/CameraDeviceClient.h | 154 | ||||
-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.h | 66 | ||||
-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.cpp | 219 | ||||
-rw-r--r-- | services/camera/libcameraservice/device3/StatusTracker.h | 130 | ||||
-rw-r--r-- | services/camera/libcameraservice/gui/RingBufferConsumer.cpp | 33 | ||||
-rw-r--r-- | services/camera/libcameraservice/gui/RingBufferConsumer.h | 4 | ||||
-rw-r--r-- | services/camera/libcameraservice/utils/CameraTraces.cpp | 94 | ||||
-rw-r--r-- | services/camera/libcameraservice/utils/CameraTraces.h | 66 |
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 = ¤t->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, ×tamp.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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms) { ATRACE_CALL(); status_t res; @@ -67,21 +97,24 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { // 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 ¶ms) { 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 ¶ms); 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 ¶ms) { 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 ¶ms) { // 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 ¶ms) { 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_ |