Merge "Add tests directory for AudioFlinger"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b8a9711..20da925 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,10 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudioflinger.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/camera/Android.mk b/camera/Android.mk
index 5774b6f..c10e38a 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -50,7 +50,7 @@
LOCAL_C_INCLUDES += \
system/media/camera/include \
- system/media/private/camera/include
+ system/media/private/camera/include \
LOCAL_MODULE:= libcamera_client
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index af091f4..161f842 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <stdlib.h>
#include <camera/CameraParameters.h>
+#include <system/graphics.h>
namespace android {
// Parameter keys to communicate between camera application and driver.
@@ -483,4 +484,45 @@
return NO_ERROR;
}
+void CameraParameters::getSupportedPreviewFormats(Vector<int>& formats) const {
+ const char* supportedPreviewFormats =
+ get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
+
+ String8 fmtStr(supportedPreviewFormats);
+ char* prevFmts = fmtStr.lockBuffer(fmtStr.size());
+
+ char* savePtr;
+ char* fmt = strtok_r(prevFmts, ",", &savePtr);
+ while (fmt) {
+ int actual = previewFormatToEnum(fmt);
+ if (actual != -1) {
+ formats.add(actual);
+ }
+ fmt = strtok_r(NULL, ",", &savePtr);
+ }
+ fmtStr.unlockBuffer(fmtStr.size());
+}
+
+
+int CameraParameters::previewFormatToEnum(const char* format) {
+ return
+ !format ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP :
+ !strcmp(format, PIXEL_FORMAT_YUV422SP) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
+ !strcmp(format, PIXEL_FORMAT_YUV420SP) ?
+ HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
+ !strcmp(format, PIXEL_FORMAT_YUV422I) ?
+ HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2
+ !strcmp(format, PIXEL_FORMAT_YUV420P) ?
+ HAL_PIXEL_FORMAT_YV12 : // YV12
+ !strcmp(format, PIXEL_FORMAT_RGB565) ?
+ HAL_PIXEL_FORMAT_RGB_565 : // RGB565
+ !strcmp(format, PIXEL_FORMAT_RGBA8888) ?
+ HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888
+ !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ?
+ HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data
+ -1;
+}
+
}; // namespace android
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 59dce91..3f72f34 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -349,18 +349,18 @@
size_t size = mTagToNameMap.size();
if (size == 0) {
- fdprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
indentation, "");
return;
}
- fdprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
+ dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
indentation, "", size);
for (size_t i = 0; i < size; ++i) {
uint32_t tag = mTagToNameMap.keyAt(i);
if (verbosity < 1) {
- fdprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
+ dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
continue;
}
String8 name = mTagToNameMap.valueAt(i);
@@ -369,7 +369,7 @@
int type = mTagToTypeMap.valueFor(tag);
const char* typeName = (type >= 0 && type < NUM_TYPES) ?
camera_metadata_type_names[type] : "UNKNOWN";
- fdprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
+ dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
"", tag, name.string(), type, typeName, sectionName.string());
}
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index ad65955..ff4a0c2 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -37,6 +37,8 @@
SUBMIT_REQUEST,
SUBMIT_REQUEST_LIST,
CANCEL_REQUEST,
+ BEGIN_CONFIGURE,
+ END_CONFIGURE,
DELETE_STREAM,
CREATE_STREAM,
CREATE_DEFAULT_REQUEST,
@@ -174,6 +176,26 @@
return res;
}
+ virtual status_t beginConfigure()
+ {
+ ALOGV("beginConfigure");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ remote()->transact(BEGIN_CONFIGURE, data, &reply);
+ reply.readExceptionCode();
+ return reply.readInt32();
+ }
+
+ virtual status_t endConfigure()
+ {
+ ALOGV("endConfigure");
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+ remote()->transact(END_CONFIGURE, data, &reply);
+ reply.readExceptionCode();
+ return reply.readInt32();
+ }
+
virtual status_t deleteStream(int streamId)
{
Parcel data, reply;
@@ -456,6 +478,18 @@
reply->writeInt64(lastFrameNumber);
return NO_ERROR;
}
+ case BEGIN_CONFIGURE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ reply->writeNoException();
+ reply->writeInt32(beginConfigure());
+ return NO_ERROR;
+ } break;
+ case END_CONFIGURE: {
+ CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+ reply->writeNoException();
+ reply->writeInt32(endConfigure());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index 94f560d..c2a8f1b 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -47,7 +47,7 @@
"ro.revision",
"dalvik.vm.heapgrowthlimit",
"dalvik.vm.heapsize",
- "persist.sys.dalvik.vm.lib.1",
+ "persist.sys.dalvik.vm.lib.2",
//"ro.product.cpu.abi",
//"ro.bootloader",
//"this-never-appears!",
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index d521543..c6074fc 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -102,6 +102,12 @@
void dump() const;
status_t dump(int fd, const Vector<String16>& args) const;
+ /**
+ * Returns a Vector containing the supported preview formats
+ * as enums given in graphics.h.
+ */
+ void getSupportedPreviewFormats(Vector<int>& formats) const;
+
// Parameter keys to communicate between camera application and driver.
// The access (read/write, read only, or write only) is viewed from the
// perspective of applications, not driver.
@@ -674,6 +680,13 @@
// High-dynamic range mode
static const char LIGHTFX_HDR[];
+ /**
+ * Returns the the supported preview formats as an enum given in graphics.h
+ * corrsponding to the format given in the input string or -1 if no such
+ * conversion exists.
+ */
+ static int previewFormatToEnum(const char* format);
+
private:
DefaultKeyedVector<String8,String8> mMap;
};
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index 913696f..35488bb 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -78,6 +78,27 @@
/*out*/
int64_t* lastFrameNumber = NULL) = 0;
+ /**
+ * Begin the device configuration.
+ *
+ * <p>
+ * beginConfigure must be called before any call to deleteStream, createStream,
+ * or endConfigure. It is not valid to call this when the device is not idle.
+ * <p>
+ */
+ virtual status_t beginConfigure() = 0;
+
+ /**
+ * End the device configuration.
+ *
+ * <p>
+ * endConfigure must be called after stream configuration is complete (i.e. after
+ * a call to beginConfigure and subsequent createStream/deleteStream calls). This
+ * must be called before any requests can be submitted.
+ * <p>
+ */
+ virtual status_t endConfigure() = 0;
+
virtual status_t deleteStream(int streamId) = 0;
virtual status_t createStream(
int width, int height, int format,
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 402b479..6fe0c7f 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
#include <hardware/audio_effect.h>
#include <media/IAudioFlingerClient.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/Errors.h>
@@ -274,8 +275,48 @@
// check presence of audio flinger service.
// returns NO_ERROR if binding to service succeeds, DEAD_OBJECT otherwise
static status_t checkAudioFlinger();
+
+ /* List available audio ports and their attributes */
+ static status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation);
+
+ /* Get attributes for a given audio port */
+ static status_t getAudioPort(struct audio_port *port);
+
+ /* Create an audio patch between several source and sink ports */
+ static status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+
+ /* Release an audio patch */
+ static status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+ /* List existing audio patches */
+ static status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation);
+ /* Set audio port configuration */
+ static status_t setAudioPortConfig(const struct audio_port_config *config);
+
// ----------------------------------------------------------------------------
+ class AudioPortCallback : public RefBase
+ {
+ public:
+
+ AudioPortCallback() {}
+ virtual ~AudioPortCallback() {}
+
+ virtual void onAudioPortListUpdate() = 0;
+ virtual void onAudioPatchListUpdate() = 0;
+ virtual void onServiceDied() = 0;
+
+ };
+
+ static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -294,7 +335,8 @@
virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
};
- class AudioPolicyServiceClient: public IBinder::DeathRecipient
+ class AudioPolicyServiceClient: public IBinder::DeathRecipient,
+ public BnAudioPolicyServiceClient
{
public:
AudioPolicyServiceClient() {
@@ -302,6 +344,10 @@
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
+
+ // IAudioPolicyServiceClient
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
};
static sp<AudioFlingerClient> gAudioFlingerClient;
@@ -324,6 +370,8 @@
// list of output descriptors containing cached parameters
// (sampling rate, framecount, channel count...)
static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+
+ static sp<AudioPortCallback> gAudioPortCallback;
};
}; // namespace android
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7db6a48..c742810 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -214,6 +214,27 @@
// and should be called at most once. For a definition of what "low RAM" means, see
// android.app.ActivityManager.isLowRamDevice().
virtual status_t setLowRamDevice(bool isLowRamDevice) = 0;
+
+ /* List available audio ports and their attributes */
+ virtual status_t listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports) = 0;
+
+ /* Get attributes for a given audio port */
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+ /* Create an audio patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) = 0;
+
+ /* Release an audio patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+ /* List existing audio patches */
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches) = 0;
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
};
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 09b9ea6..d422aa3 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -25,6 +25,7 @@
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <media/AudioSystem.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio_policy.h>
@@ -99,6 +100,32 @@
// Check if offload is possible for given format, stream type, sample rate,
// bit rate, duration, video and streaming or offload property is enabled
virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0;
+
+ /* List available audio ports and their attributes */
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation) = 0;
+
+ /* Get attributes for a given audio port */
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+ /* Create an audio patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) = 0;
+
+ /* Release an audio patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+ /* List existing audio patches */
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation) = 0;
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
};
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
new file mode 100644
index 0000000..59df046
--- /dev/null
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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_IAUDIOPOLICYSERVICECLIENT_H
+#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyServiceClient : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AudioPolicyServiceClient);
+
+ // Notifies a change of audio port configuration.
+ virtual void onAudioPortListUpdate() = 0;
+ // Notifies a change of audio patch configuration.
+ virtual void onAudioPatchListUpdate() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index e862ec3..d38d976 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -53,6 +53,7 @@
kKeyESDS = 'esds', // raw data
kKeyAACProfile = 'aacp', // int32_t
kKeyAVCC = 'avcc', // raw data
+ kKeyHVCC = 'hvcc', // raw data
kKeyD263 = 'd263', // raw data
kKeyVorbisInfo = 'vinf', // raw data
kKeyVorbisBooks = 'vboo', // raw data
@@ -170,6 +171,7 @@
enum {
kTypeESDS = 'esds',
kTypeAVCC = 'avcc',
+ kTypeHVCC = 'hvcc',
kTypeD263 = 'd263',
};
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 5121c17..5590b60 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -352,6 +352,9 @@
int64_t getDecodingTimeUs();
+ status_t parseHEVCCodecSpecificData(
+ const void *data, size_t size,
+ unsigned *profile, unsigned *level);
status_t parseAVCCodecSpecificData(
const void *data, size_t size,
unsigned *profile, unsigned *level);
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index 2f000d7..c07f4c9 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -163,17 +163,6 @@
media_status_t AMediaCodec_releaseOutputBufferAtTime(
AMediaCodec *mData, size_t idx, int64_t timestampNs);
-typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
-
-/**
- * Set a callback to be called when a new buffer is available, or there was a format
- * or buffer change.
- * Note that you cannot perform any operations on the mediacodec from within the callback.
- * If you need to perform mediacodec operations, you must do so on a different thread.
- */
-media_status_t AMediaCodec_setNotificationCallback(
- AMediaCodec*, OnCodecEvent callback, void *userdata);
-
typedef enum {
AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h
index 5a319d7..7a4e702 100644
--- a/include/ndk/NdkMediaExtractor.h
+++ b/include/ndk/NdkMediaExtractor.h
@@ -106,7 +106,7 @@
* Returns the current sample's presentation time in microseconds.
* or -1 if no more samples are available.
*/
-int64_t AMediaExtractor_getSampletime(AMediaExtractor*);
+int64_t AMediaExtractor_getSampleTime(AMediaExtractor*);
/**
* Advance to the next sample. Returns false if no more sample data
diff --git a/include/ndk/NdkMediaMuxer.h b/include/ndk/NdkMediaMuxer.h
index 1ddc51d..90d946c 100644
--- a/include/ndk/NdkMediaMuxer.h
+++ b/include/ndk/NdkMediaMuxer.h
@@ -110,7 +110,7 @@
* by the encoder.)
*/
media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
- size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info);
+ size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info);
#ifdef __cplusplus
} // extern "C"
diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h
new file mode 100644
index 0000000..5fd8eb2
--- /dev/null
+++ b/include/soundtrigger/ISoundTrigger.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_H
+#define ANDROID_HARDWARE_ISOUNDTRIGGER_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <system/sound_trigger.h>
+
+namespace android {
+
+class ISoundTrigger : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SoundTrigger);
+
+ virtual void detach() = 0;
+
+ virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle) = 0;
+
+ virtual status_t unloadSoundModel(sound_model_handle_t handle) = 0;
+
+ virtual status_t startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory) = 0;
+ virtual status_t stopRecognition(sound_model_handle_t handle) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSoundTrigger: public BnInterface<ISoundTrigger>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_H
diff --git a/include/soundtrigger/ISoundTriggerClient.h b/include/soundtrigger/ISoundTriggerClient.h
new file mode 100644
index 0000000..7f86d02
--- /dev/null
+++ b/include/soundtrigger/ISoundTriggerClient.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
+#define ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class ISoundTriggerClient : public IInterface
+{
+public:
+
+ DECLARE_META_INTERFACE(SoundTriggerClient);
+
+ virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSoundTriggerClient : public BnInterface<ISoundTriggerClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H
diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h
new file mode 100644
index 0000000..05a764a
--- /dev/null
+++ b/include/soundtrigger/ISoundTriggerHwService.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
+#define ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <system/sound_trigger.h>
+
+namespace android {
+
+class ISoundTrigger;
+class ISoundTriggerClient;
+
+class ISoundTriggerHwService : public IInterface
+{
+public:
+
+ DECLARE_META_INTERFACE(SoundTriggerHwService);
+
+ virtual status_t listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules) = 0;
+
+ virtual status_t attach(const sound_trigger_module_handle_t handle,
+ const sp<ISoundTriggerClient>& client,
+ sp<ISoundTrigger>& module) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSoundTriggerHwService: public BnInterface<ISoundTriggerHwService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H
diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h
new file mode 100644
index 0000000..1f7f286
--- /dev/null
+++ b/include/soundtrigger/SoundTrigger.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_H
+
+#include <binder/IBinder.h>
+#include <soundtrigger/SoundTriggerCallback.h>
+#include <soundtrigger/ISoundTrigger.h>
+#include <soundtrigger/ISoundTriggerHwService.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+#include <system/sound_trigger.h>
+
+namespace android {
+
+class MemoryDealer;
+
+class SoundTrigger : public BnSoundTriggerClient,
+ public IBinder::DeathRecipient
+{
+public:
+ static status_t listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules);
+ static sp<SoundTrigger> attach(const sound_trigger_module_handle_t module,
+ const sp<SoundTriggerCallback>& callback);
+
+ virtual ~SoundTrigger();
+
+ void detach();
+
+ status_t loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle);
+
+ status_t unloadSoundModel(sound_model_handle_t handle);
+
+ status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory);
+ status_t stopRecognition(sound_model_handle_t handle);
+
+ // BpSoundTriggerClient
+ virtual void onRecognitionEvent(const sp<IMemory>& eventMemory);
+
+ //IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ static status_t stringToGuid(const char *str, sound_trigger_uuid_t *guid);
+ static status_t guidToString(const sound_trigger_uuid_t *guid,
+ char *str, size_t maxLen);
+
+private:
+ SoundTrigger(sound_trigger_module_handle_t module,
+ const sp<SoundTriggerCallback>&);
+ static const sp<ISoundTriggerHwService>& getSoundTriggerHwService();
+
+ Mutex mLock;
+ sp<ISoundTrigger> mISoundTrigger;
+ const sound_trigger_module_handle_t mModule;
+ sp<SoundTriggerCallback> mCallback;
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_SOUNDTRIGGER_H
diff --git a/include/soundtrigger/SoundTriggerCallback.h b/include/soundtrigger/SoundTriggerCallback.h
new file mode 100644
index 0000000..8a5ba02
--- /dev/null
+++ b/include/soundtrigger/SoundTriggerCallback.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
+
+#include <utils/RefBase.h>
+#include <system/sound_trigger.h>
+
+namespace android {
+
+class SoundTriggerCallback : public RefBase
+{
+public:
+
+ SoundTriggerCallback() {}
+ virtual ~SoundTriggerCallback() {}
+
+ virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event) = 0;
+
+ virtual void onServiceDied() = 0;
+
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H
diff --git a/media/img_utils/include/img_utils/TagDefinitions.h b/media/img_utils/include/img_utils/TagDefinitions.h
index 9232e58..6cc42b2 100644
--- a/media/img_utils/include/img_utils/TagDefinitions.h
+++ b/media/img_utils/include/img_utils/TagDefinitions.h
@@ -172,8 +172,14 @@
TAG_ARTIST = 0x013Bu,
TAG_EXIFVERSION = 0x9000u,
TAG_CFAREPEATPATTERNDIM = 0x828Du,
+ TAG_DATETIMEORIGINAL = 0x9003u,
TAG_CFAPATTERN = 0x828Eu,
TAG_SUBIFDS = 0x014Au,
+ TAG_TIFFEPSTANDARDID = 0x9216u,
+ TAG_EXPOSURETIME = 0x829Au,
+ TAG_ISOSPEEDRATINGS = 0x8827u,
+ TAG_FOCALLENGTH = 0x920Au,
+ TAG_FNUMBER = 0x829Du,
};
/**
@@ -208,6 +214,48 @@
2,
UNDEFINED_ENDIAN
},
+ { // DateTimeOriginal
+ 0x9003u,
+ ASCII,
+ IFD_0,
+ 20,
+ UNDEFINED_ENDIAN
+ },
+ { // Tiff/EPStandardID
+ 0x9216u,
+ BYTE,
+ IFD_0,
+ 4,
+ UNDEFINED_ENDIAN
+ },
+ { // ExposureTime
+ 0x829Au,
+ RATIONAL,
+ IFD_0,
+ 0,
+ UNDEFINED_ENDIAN
+ },
+ { // ISOSpeedRatings
+ 0x8827u,
+ SHORT,
+ IFD_0,
+ 0,
+ UNDEFINED_ENDIAN
+ },
+ { // FocalLength
+ 0x920Au,
+ RATIONAL,
+ IFD_0,
+ 0,
+ UNDEFINED_ENDIAN
+ },
+ { // FNumber
+ 0x829Du,
+ RATIONAL,
+ IFD_0,
+ 0,
+ UNDEFINED_ENDIAN
+ },
/*TODO: Remaining TIFF EP tags*/
};
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 788dfc8..14b31ec 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -19,7 +19,7 @@
namespace android {
namespace img_utils {
-OpcodeListBuilder::OpcodeListBuilder() : mOpList(), mEndianOut(&mOpList, BIG) {
+OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
if(mEndianOut.open() != OK) {
ALOGE("%s: Open failed.", __FUNCTION__);
}
diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk
index b506353..ee283a6 100644
--- a/media/libcpustats/Android.mk
+++ b/media/libcpustats/Android.mk
@@ -1,4 +1,4 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -8,4 +8,6 @@
LOCAL_MODULE := libcpustats
+LOCAL_CFLAGS := -std=gnu++11 -Werror
+
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index 637402a..cfdcb51 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -21,7 +21,6 @@
#include <stdlib.h>
#include <time.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <cpustats/ThreadCpuUsage.h>
@@ -218,7 +217,7 @@
#define FREQ_SIZE 64
char freq_path[FREQ_SIZE];
#define FREQ_DIGIT 27
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+ static_assert(MAX_CPU <= 10, "MAX_CPU too large");
#define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq"
strlcpy(freq_path, FREQ_PATH, sizeof(freq_path));
freq_path[FREQ_DIGIT] = cpuNum + '0';
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index f3770e4..69eead3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -44,6 +44,7 @@
JetPlayer.cpp \
IOMX.cpp \
IAudioPolicyService.cpp \
+ IAudioPolicyServiceClient.cpp \
MediaScanner.cpp \
MediaScannerClient.cpp \
CharacterEncodingDetector.cpp \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1c808d0..db61e85 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -203,23 +203,6 @@
mFrameSize = sizeof(uint8_t);
}
- // validate framecount
- size_t minFrameCount;
- status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
- sampleRate, format, channelMask);
- if (status != NO_ERROR) {
- ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d",
- sampleRate, format, channelMask, status);
- return status;
- }
- ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
-
- if (frameCount == 0) {
- frameCount = minFrameCount;
- } else if (frameCount < minFrameCount) {
- ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
- return BAD_VALUE;
- }
// mFrameCount is initialized in openRecord_l
mReqFrameCount = frameCount;
@@ -242,7 +225,7 @@
}
// create the IAudioRecord
- status = openRecord_l(0 /*epoch*/);
+ status_t status = openRecord_l(0 /*epoch*/);
if (status != NO_ERROR) {
if (mAudioRecordThread != 0) {
@@ -464,6 +447,29 @@
size_t frameCount = mReqFrameCount;
if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) {
+ // validate framecount
+ // If fast track was not requested, this preserves
+ // the old behavior of validating on client side.
+ // FIXME Eventually the validation should be done on server side
+ // regardless of whether it's a fast or normal track. It's debatable
+ // whether to account for the input latency to provision buffers appropriately.
+ size_t minFrameCount;
+ status = AudioRecord::getMinFrameCount(&minFrameCount,
+ mSampleRate, mFormat, mChannelMask);
+ if (status != NO_ERROR) {
+ ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; "
+ "status %d",
+ mSampleRate, mFormat, mChannelMask, status);
+ return status;
+ }
+
+ if (frameCount == 0) {
+ frameCount = minFrameCount;
+ } else if (frameCount < minFrameCount) {
+ ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
+ return BAD_VALUE;
+ }
+
// Make sure that application is notified with sufficient margin before overrun
if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
mNotificationFramesAct = frameCount/2;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 2f16444..eafb3ad 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -45,6 +45,7 @@
audio_channel_mask_t AudioSystem::gPrevInChannelMask;
size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid
+sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback;
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
@@ -528,6 +529,7 @@
gAudioErrorCallback = cb;
}
+
bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType)
{
switch (streamType) {
@@ -566,6 +568,7 @@
}
binder->linkToDeath(gAudioPolicyServiceClient);
gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
+ gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
gLock.unlock();
} else {
gLock.unlock();
@@ -831,14 +834,88 @@
return aps->isOffloadSupported(info);
}
+status_t AudioSystem::listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->listAudioPorts(role, type, num_ports, ports, generation);
+}
+
+status_t AudioSystem::getAudioPort(struct audio_port *port)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->getAudioPort(port);
+}
+
+status_t AudioSystem::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->createAudioPatch(patch, handle);
+}
+
+status_t AudioSystem::releaseAudioPatch(audio_patch_handle_t handle)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->releaseAudioPatch(handle);
+}
+
+status_t AudioSystem::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->listAudioPatches(num_patches, patches, generation);
+}
+
+status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->setAudioPortConfig(config);
+}
+
+void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack)
+{
+ Mutex::Autolock _l(gLock);
+ gAudioPortCallback = callBack;
+}
+
// ---------------------------------------------------------------------------
void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
{
- Mutex::Autolock _l(AudioSystem::gLock);
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onServiceDied();
+ }
AudioSystem::gAudioPolicyService.clear();
ALOGW("AudioPolicyService server died!");
}
+void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
+{
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onAudioPortListUpdate();
+ }
+}
+
+void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate()
+{
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onAudioPatchListUpdate();
+ }
+}
+
}; // namespace android
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 27a3718..0dbfa62 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -19,9 +19,9 @@
#include <private/media/AudioTrackShared.h>
#include <utils/Log.h>
-extern "C" {
-#include "../private/bionic_futex.h"
-}
+
+#include <linux/futex.h>
+#include <sys/syscall.h>
namespace android {
@@ -134,10 +134,17 @@
ssize_t filled = rear - front;
// pipe should not be overfull
if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
- ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
- mIsShutdown = true;
- status = NO_INIT;
- goto end;
+ if (mIsOut) {
+ ALOGE("Shared memory control block is corrupt (filled=%d, mFrameCount=%u); "
+ "shutting down", filled, mFrameCount);
+ mIsShutdown = true;
+ status = NO_INIT;
+ goto end;
+ }
+ // for input, sync up on overrun
+ filled = 0;
+ cblk->u.mStreaming.mFront = rear;
+ (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
}
// don't allow filling pipe beyond the nominal size
size_t avail = mIsOut ? mFrameCount - filled : filled;
@@ -206,12 +213,12 @@
}
int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
if (!(old & CBLK_FUTEX_WAKE)) {
- int rc;
if (measure && !beforeIsValid) {
clock_gettime(CLOCK_MONOTONIC, &before);
beforeIsValid = true;
}
- int ret = __futex_syscall4(&cblk->mFutex,
+ errno = 0;
+ (void) syscall(__NR_futex, &cblk->mFutex,
mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
// update total elapsed time spent waiting
if (measure) {
@@ -230,16 +237,16 @@
before = after;
beforeIsValid = true;
}
- switch (ret) {
- case 0: // normal wakeup by server, or by binderDied()
- case -EWOULDBLOCK: // benign race condition with server
- case -EINTR: // wait was interrupted by signal or other spurious wakeup
- case -ETIMEDOUT: // time-out expired
+ switch (errno) {
+ case 0: // normal wakeup by server, or by binderDied()
+ case EWOULDBLOCK: // benign race condition with server
+ case EINTR: // wait was interrupted by signal or other spurious wakeup
+ case ETIMEDOUT: // time-out expired
// FIXME these error/non-0 status are being dropped
break;
default:
- ALOGE("%s unexpected error %d", __func__, ret);
- status = -ret;
+ status = errno;
+ ALOGE("%s unexpected error %s", __func__, strerror(status));
goto end;
}
}
@@ -295,7 +302,7 @@
audio_track_cblk_t* cblk = mCblk;
if (!(android_atomic_or(CBLK_INVALID, &cblk->mFlags) & CBLK_INVALID)) {
// it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
- (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
1);
}
}
@@ -304,7 +311,7 @@
{
audio_track_cblk_t* cblk = mCblk;
if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) {
- (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
1);
}
}
@@ -435,18 +442,18 @@
}
int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
if (!(old & CBLK_FUTEX_WAKE)) {
- int rc;
- int ret = __futex_syscall4(&cblk->mFutex,
+ errno = 0;
+ (void) syscall(__NR_futex, &cblk->mFutex,
mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
- switch (ret) {
- case 0: // normal wakeup by server, or by binderDied()
- case -EWOULDBLOCK: // benign race condition with server
- case -EINTR: // wait was interrupted by signal or other spurious wakeup
- case -ETIMEDOUT: // time-out expired
+ switch (errno) {
+ case 0: // normal wakeup by server, or by binderDied()
+ case EWOULDBLOCK: // benign race condition with server
+ case EINTR: // wait was interrupted by signal or other spurious wakeup
+ case ETIMEDOUT: // time-out expired
break;
default:
- ALOGE("%s unexpected error %d", __func__, ret);
- status = -ret;
+ status = errno;
+ ALOGE("%s unexpected error %s", __func__, strerror(status));
goto end;
}
}
@@ -535,7 +542,7 @@
if (front != rear) {
int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
if (!(old & CBLK_FUTEX_WAKE)) {
- (void) __futex_syscall3(&cblk->mFutex,
+ (void) syscall(__NR_futex, &cblk->mFutex,
mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
}
}
@@ -638,7 +645,7 @@
ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
if (!(old & CBLK_FUTEX_WAKE)) {
- (void) __futex_syscall3(&cblk->mFutex,
+ (void) syscall(__NR_futex, &cblk->mFutex,
mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
}
}
@@ -683,7 +690,7 @@
bool old =
(android_atomic_or(CBLK_STREAM_END_DONE, &cblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
if (!old) {
- (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+ (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
1);
}
return old;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 0e2463e..687fa76 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -74,6 +74,12 @@
GET_PRIMARY_OUTPUT_SAMPLING_RATE,
GET_PRIMARY_OUTPUT_FRAME_COUNT,
SET_LOW_RAM_DEVICE,
+ LIST_AUDIO_PORTS,
+ GET_AUDIO_PORT,
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ LIST_AUDIO_PATCHES,
+ SET_AUDIO_PORT_CONFIG
};
class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -801,7 +807,101 @@
remote()->transact(SET_LOW_RAM_DEVICE, data, &reply);
return reply.readInt32();
}
-
+ virtual status_t listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports)
+ {
+ if (num_ports == NULL || *num_ports == 0 || ports == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(*num_ports);
+ status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ *num_ports = (unsigned int)reply.readInt32();
+ reply.read(ports, *num_ports * sizeof(struct audio_port));
+ return status;
+ }
+ virtual status_t getAudioPort(struct audio_port *port)
+ {
+ if (port == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.write(port, sizeof(struct audio_port));
+ status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ reply.read(port, sizeof(struct audio_port));
+ return status;
+ }
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+ {
+ if (patch == NULL || handle == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.write(patch, sizeof(struct audio_patch));
+ data.write(handle, sizeof(audio_patch_handle_t));
+ status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ reply.read(handle, sizeof(audio_patch_handle_t));
+ return status;
+ }
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.write(&handle, sizeof(audio_patch_handle_t));
+ status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches)
+ {
+ if (num_patches == NULL || *num_patches == 0 || patches == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(*num_patches);
+ status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ *num_patches = (unsigned int)reply.readInt32();
+ reply.read(patches, *num_patches * sizeof(struct audio_patch));
+ return status;
+ }
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config)
+ {
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.write(config, sizeof(struct audio_port_config));
+ status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
};
IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -1199,6 +1299,76 @@
reply->writeInt32(setLowRamDevice(isLowRamDevice));
return NO_ERROR;
} break;
+ case LIST_AUDIO_PORTS: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ unsigned int num_ports = data.readInt32();
+ struct audio_port *ports =
+ (struct audio_port *)calloc(num_ports,
+ sizeof(struct audio_port));
+ status_t status = listAudioPorts(&num_ports, ports);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(num_ports);
+ reply->write(&ports, num_ports * sizeof(struct audio_port));
+ }
+ free(ports);
+ return NO_ERROR;
+ } break;
+ case GET_AUDIO_PORT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ struct audio_port port;
+ data.read(&port, sizeof(struct audio_port));
+ status_t status = getAudioPort(&port);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&port, sizeof(struct audio_port));
+ }
+ return NO_ERROR;
+ } break;
+ case CREATE_AUDIO_PATCH: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ struct audio_patch patch;
+ data.read(&patch, sizeof(struct audio_patch));
+ audio_patch_handle_t handle;
+ data.read(&handle, sizeof(audio_patch_handle_t));
+ status_t status = createAudioPatch(&patch, &handle);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&handle, sizeof(audio_patch_handle_t));
+ }
+ return NO_ERROR;
+ } break;
+ case RELEASE_AUDIO_PATCH: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ audio_patch_handle_t handle;
+ data.read(&handle, sizeof(audio_patch_handle_t));
+ status_t status = releaseAudioPatch(handle);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ } break;
+ case LIST_AUDIO_PATCHES: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ unsigned int num_patches = data.readInt32();
+ struct audio_patch *patches =
+ (struct audio_patch *)calloc(num_patches,
+ sizeof(struct audio_patch));
+ status_t status = listAudioPatches(&num_patches, patches);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(num_patches);
+ reply->write(&patches, num_patches * sizeof(struct audio_patch));
+ }
+ free(patches);
+ return NO_ERROR;
+ } break;
+ case SET_AUDIO_PORT_CONFIG: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ struct audio_port_config config;
+ data.read(&config, sizeof(struct audio_port_config));
+ status_t status = setAudioPortConfig(&config);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 9bb4a49..77d131b 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -57,7 +57,14 @@
QUERY_DEFAULT_PRE_PROCESSING,
SET_EFFECT_ENABLED,
IS_STREAM_ACTIVE_REMOTELY,
- IS_OFFLOAD_SUPPORTED
+ IS_OFFLOAD_SUPPORTED,
+ LIST_AUDIO_PORTS,
+ GET_AUDIO_PORT,
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ LIST_AUDIO_PATCHES,
+ SET_AUDIO_PORT_CONFIG,
+ REGISTER_CLIENT
};
class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -390,7 +397,140 @@
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
data.write(&info, sizeof(audio_offload_info_t));
remote()->transact(IS_OFFLOAD_SUPPORTED, data, &reply);
- return reply.readInt32(); }
+ return reply.readInt32();
+ }
+
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation)
+ {
+ if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ unsigned int numPortsReq = (ports == NULL) ? 0 : *num_ports;
+ data.writeInt32(role);
+ data.writeInt32(type);
+ data.writeInt32(numPortsReq);
+ status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ *num_ports = (unsigned int)reply.readInt32();
+ }
+ if (status == NO_ERROR) {
+ if (numPortsReq > *num_ports) {
+ numPortsReq = *num_ports;
+ }
+ if (numPortsReq > 0) {
+ reply.read(ports, numPortsReq * sizeof(struct audio_port));
+ }
+ *generation = reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t getAudioPort(struct audio_port *port)
+ {
+ if (port == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.write(port, sizeof(struct audio_port));
+ status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ reply.read(port, sizeof(struct audio_port));
+ return status;
+ }
+
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+ {
+ if (patch == NULL || handle == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.write(patch, sizeof(struct audio_patch));
+ data.write(handle, sizeof(audio_patch_handle_t));
+ status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ reply.read(handle, sizeof(audio_patch_handle_t));
+ return status;
+ }
+
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.write(&handle, sizeof(audio_patch_handle_t));
+ status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation)
+ {
+ if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ unsigned int numPatchesReq = (patches == NULL) ? 0 : *num_patches;
+ data.writeInt32(numPatchesReq);
+ status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ *num_patches = (unsigned int)reply.readInt32();
+ }
+ if (status == NO_ERROR) {
+ if (numPatchesReq > *num_patches) {
+ numPatchesReq = *num_patches;
+ }
+ if (numPatchesReq > 0) {
+ reply.read(patches, numPatchesReq * sizeof(struct audio_patch));
+ }
+ *generation = reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config)
+ {
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.write(config, sizeof(struct audio_port_config));
+ status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeStrongBinder(client->asBinder());
+ remote()->transact(REGISTER_CLIENT, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -687,6 +827,103 @@
return NO_ERROR;
}
+ case LIST_AUDIO_PORTS: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_port_role_t role = (audio_port_role_t)data.readInt32();
+ audio_port_type_t type = (audio_port_type_t)data.readInt32();
+ unsigned int numPortsReq = data.readInt32();
+ unsigned int numPorts = numPortsReq;
+ unsigned int generation;
+ struct audio_port *ports =
+ (struct audio_port *)calloc(numPortsReq, sizeof(struct audio_port));
+ status_t status = listAudioPorts(role, type, &numPorts, ports, &generation);
+ reply->writeInt32(status);
+ reply->writeInt32(numPorts);
+
+ if (status == NO_ERROR) {
+ if (numPortsReq > numPorts) {
+ numPortsReq = numPorts;
+ }
+ reply->write(ports, numPortsReq * sizeof(struct audio_port));
+ reply->writeInt32(generation);
+ }
+ free(ports);
+ return NO_ERROR;
+ }
+
+ case GET_AUDIO_PORT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ struct audio_port port;
+ data.read(&port, sizeof(struct audio_port));
+ status_t status = getAudioPort(&port);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&port, sizeof(struct audio_port));
+ }
+ return NO_ERROR;
+ }
+
+ case CREATE_AUDIO_PATCH: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ struct audio_patch patch;
+ data.read(&patch, sizeof(struct audio_patch));
+ audio_patch_handle_t handle;
+ data.read(&handle, sizeof(audio_patch_handle_t));
+ status_t status = createAudioPatch(&patch, &handle);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&handle, sizeof(audio_patch_handle_t));
+ }
+ return NO_ERROR;
+ }
+
+ case RELEASE_AUDIO_PATCH: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_patch_handle_t handle;
+ data.read(&handle, sizeof(audio_patch_handle_t));
+ status_t status = releaseAudioPatch(handle);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+
+ case LIST_AUDIO_PATCHES: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ unsigned int numPatchesReq = data.readInt32();
+ unsigned int numPatches = numPatchesReq;
+ unsigned int generation;
+ struct audio_patch *patches =
+ (struct audio_patch *)calloc(numPatchesReq,
+ sizeof(struct audio_patch));
+ status_t status = listAudioPatches(&numPatches, patches, &generation);
+ reply->writeInt32(status);
+ reply->writeInt32(numPatches);
+ if (status == NO_ERROR) {
+ if (numPatchesReq > numPatches) {
+ numPatchesReq = numPatches;
+ }
+ reply->write(patches, numPatchesReq * sizeof(struct audio_patch));
+ reply->writeInt32(generation);
+ }
+ free(patches);
+ return NO_ERROR;
+ }
+
+ case SET_AUDIO_PORT_CONFIG: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ struct audio_port_config config;
+ data.read(&config, sizeof(struct audio_port_config));
+ status_t status = setAudioPortConfig(&config);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case REGISTER_CLIENT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
+ data.readStrongBinder());
+ registerClient(client);
+ return NO_ERROR;
+ } break;
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
new file mode 100644
index 0000000..e802277
--- /dev/null
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 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 "IAudioPolicyServiceClient"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IAudioPolicyServiceClient.h>
+#include <media/AudioSystem.h>
+
+namespace android {
+
+enum {
+ PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
+ PATCH_LIST_UPDATE
+};
+
+class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
+{
+public:
+ BpAudioPolicyServiceClient(const sp<IBinder>& impl)
+ : BpInterface<IAudioPolicyServiceClient>(impl)
+ {
+ }
+
+ void onAudioPortListUpdate()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+ remote()->transact(PORT_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ void onAudioPatchListUpdate()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+ remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnAudioPolicyServiceClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case PORT_LIST_UPDATE: {
+ CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+ onAudioPortListUpdate();
+ return NO_ERROR;
+ } break;
+ case PATCH_LIST_UPDATE: {
+ CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+ onAudioPatchListUpdate();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index a9820e0..194abbb 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -95,7 +95,8 @@
status_t MediaRecorderClient::setVideoSource(int vs)
{
ALOGV("setVideoSource(%d)", vs);
- if (!checkPermission(cameraPermission)) {
+ // Check camera permission for sources other than SURFACE
+ if (vs != VIDEO_SOURCE_SURFACE && !checkPermission(cameraPermission)) {
return PERMISSION_DENIED;
}
Mutex::Autolock lock(mLock);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d8d939a..857e703 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1376,16 +1376,15 @@
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
+ // notify duration first, so that it's definitely set when
+ // the app received the "prepare complete" callback.
+ int64_t durationUs;
+ if (mSource->getDuration(&durationUs) == OK) {
+ driver->notifyDuration(durationUs);
+ }
driver->notifyPrepareCompleted(err);
}
- int64_t durationUs;
- if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
- sp<NuPlayerDriver> driver = mDriver.promote();
- if (driver != NULL) {
- driver->notifyDuration(durationUs);
- }
- }
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 469c9ca..cfbf282 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -37,6 +37,7 @@
: mNotify(notify),
mNativeWindow(nativeWindow),
mBufferGeneration(0),
+ mPaused(true),
mComponentName("decoder") {
// Every decoder has its own looper because MediaCodec operations
// are blocking, but NuPlayer needs asynchronous operations.
@@ -112,6 +113,7 @@
mOutputBuffers.size());
requestCodecNotification();
+ mPaused = false;
}
void NuPlayer::Decoder::requestCodecNotification() {
@@ -352,6 +354,11 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
+ mPaused = true;
+}
+
+void NuPlayer::Decoder::onResume() {
+ mPaused = false;
}
void NuPlayer::Decoder::onShutdown() {
@@ -380,6 +387,7 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatShutdownCompleted);
notify->post();
+ mPaused = true;
}
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
@@ -397,7 +405,9 @@
case kWhatCodecNotify:
{
if (!isStaleReply(msg)) {
- while (handleAnInputBuffer()) {
+ if (!mPaused) {
+ while (handleAnInputBuffer()) {
+ }
}
while (handleAnOutputBuffer()) {
@@ -430,6 +440,12 @@
break;
}
+ case kWhatResume:
+ {
+ onResume();
+ break;
+ }
+
case kWhatShutdown:
{
onShutdown();
@@ -447,7 +463,7 @@
}
void NuPlayer::Decoder::signalResume() {
- // nothing to do
+ (new AMessage(kWhatResume, id()))->post();
}
void NuPlayer::Decoder::initiateShutdown() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 94243fc..2892584 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -87,11 +87,13 @@
void onConfigure(const sp<AMessage> &format);
void onFlush();
+ void onResume();
void onInputBufferFilled(const sp<AMessage> &msg);
void onRenderBuffer(const sp<AMessage> &msg);
void onShutdown();
int32_t mBufferGeneration;
+ bool mPaused;
AString mComponentName;
bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index e4850f0..280b5af 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -284,6 +284,10 @@
case STATE_PREPARED:
{
mStartupSeekTimeUs = seekTimeUs;
+ // pretend that the seek completed. It will actually happen when starting playback.
+ // TODO: actually perform the seek here, so the player is ready to go at the new
+ // location
+ notifySeekComplete();
break;
}
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 4d9a1fa..4d14904 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -438,7 +438,7 @@
void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
{
if (mFd >= 0) {
- fdprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
+ dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
} else {
ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 8f154be..d3c508d 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2872,6 +2872,24 @@
break;
}
+ case OMX_AUDIO_CodingAndroidOPUS:
+ {
+ OMX_AUDIO_PARAM_ANDROID_OPUSTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = portIndex;
+
+ CHECK_EQ((status_t)OK, mOMX->getParameter(
+ mNode,
+ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
+ ¶ms,
+ sizeof(params)));
+
+ notify->setString("mime", MEDIA_MIMETYPE_AUDIO_OPUS);
+ notify->setInt32("channel-count", params.nChannels);
+ notify->setInt32("sample-rate", params.nSampleRate);
+ break;
+ }
+
default:
ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding);
TRESPASS();
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index e07b6aa..297f4fc 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -95,6 +95,7 @@
uint64_t* mCurrentSampleInfoOffsets;
bool mIsAVC;
+ bool mIsHEVC;
size_t mNALLengthSize;
bool mStarted;
@@ -317,6 +318,9 @@
case FOURCC('a', 'v', 'c', '1'):
return MEDIA_MIMETYPE_VIDEO_AVC;
+ case FOURCC('h', 'v', 'c', '1'):
+ case FOURCC('h', 'e', 'v', '1'):
+ return MEDIA_MIMETYPE_VIDEO_HEVC;
default:
CHECK(!"should not be here.");
return NULL;
@@ -478,11 +482,20 @@
off64_t offset = 0;
status_t err;
while (true) {
+ off64_t orig_offset = offset;
err = parseChunk(&offset, 0);
- if (err == OK) {
- continue;
- }
+ if (offset <= orig_offset) {
+ // only continue parsing if the offset was advanced,
+ // otherwise we might end up in an infinite loop
+ ALOGE("did not advance: 0x%lld->0x%lld", orig_offset, offset);
+ err = ERROR_MALFORMED;
+ break;
+ } else if (err == OK) {
+ continue;
+ } else if (err != UNKNOWN_ERROR) {
+ break;
+ }
uint32_t hdr[2];
if (mDataSource->readAt(offset, hdr, 8) < 8) {
break;
@@ -505,8 +518,6 @@
} else {
mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
}
-
- mInitCheck = OK;
} else {
mInitCheck = err;
}
@@ -758,8 +769,25 @@
// The smallest valid chunk is 16 bytes long in this case.
return ERROR_MALFORMED;
}
+ } else if (chunk_size == 0) {
+ if (depth == 0) {
+ // atom extends to end of file
+ off64_t sourceSize;
+ if (mDataSource->getSize(&sourceSize) == OK) {
+ chunk_size = (sourceSize - *offset);
+ } else {
+ // XXX could we just pick a "sufficiently large" value here?
+ ALOGE("atom size is 0, and data source has no size");
+ return ERROR_MALFORMED;
+ }
+ } else {
+ // not allowed for non-toplevel atoms, skip it
+ *offset += 4;
+ return OK;
+ }
} else if (chunk_size < 8) {
// The smallest valid chunk is 8 bytes long.
+ ALOGE("invalid chunk size: %d", int(chunk_size));
return ERROR_MALFORMED;
}
@@ -1288,6 +1316,8 @@
case FOURCC('H', '2', '6', '3'):
case FOURCC('h', '2', '6', '3'):
case FOURCC('a', 'v', 'c', '1'):
+ case FOURCC('h', 'v', 'c', '1'):
+ case FOURCC('h', 'e', 'v', '1'):
{
mHasVideo = true;
@@ -1580,6 +1610,21 @@
break;
}
+ case FOURCC('h', 'v', 'c', 'C'):
+ {
+ sp<ABuffer> buffer = new ABuffer(chunk_data_size);
+
+ if (mDataSource->readAt(
+ data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
case FOURCC('d', '2', '6', '3'):
{
@@ -2452,6 +2497,11 @@
|| type != kTypeAVCC) {
return ERROR_MALFORMED;
}
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
+ if (!track->meta->findData(kKeyHVCC, &type, &data, &size)
+ || type != kTypeHVCC) {
+ return ERROR_MALFORMED;
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
if (!track->meta->findData(kKeyESDS, &type, &data, &size)
@@ -2460,8 +2510,9 @@
}
}
- if (!track->sampleTable->isValid()) {
+ if (track->sampleTable == NULL || !track->sampleTable->isValid()) {
// Make sure we have all the metadata we need.
+ ALOGE("stbl atom missing/invalid.");
return ERROR_MALFORMED;
}
@@ -2776,6 +2827,7 @@
mCurrentSampleInfoOffsetsAllocSize(0),
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
+ mIsHEVC(false),
mNALLengthSize(0),
mStarted(false),
mGroup(NULL),
@@ -2800,6 +2852,7 @@
CHECK(success);
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+ mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
if (mIsAVC) {
uint32_t type;
@@ -2814,6 +2867,18 @@
// The number of bytes used to encode the length of a NAL unit.
mNALLengthSize = 1 + (ptr[4] & 3);
+ } else if (mIsHEVC) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ CHECK(format->findData(kKeyHVCC, &type, &data, &size));
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+
+ mNALLengthSize = 1 + (ptr[14 + 7] & 3);
}
CHECK(format->findInt32(kKeyTrackID, &mTrackId));
@@ -3562,7 +3627,7 @@
}
}
- if (!mIsAVC || mWantsNALFragments) {
+ if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) {
if (newBuffer) {
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
@@ -3594,7 +3659,7 @@
++mCurrentSampleIndex;
}
- if (!mIsAVC) {
+ if (!mIsAVC && !mIsHEVC) {
*out = mBuffer;
mBuffer = NULL;
@@ -3837,7 +3902,7 @@
bufmeta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
- if (!mIsAVC || mWantsNALFragments) {
+ if ((!mIsAVC && !mIsHEVC)|| mWantsNALFragments) {
if (newBuffer) {
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
@@ -3869,7 +3934,7 @@
++mCurrentSampleIndex;
}
- if (!mIsAVC) {
+ if (!mIsAVC && !mIsHEVC) {
*out = mBuffer;
mBuffer = NULL;
@@ -4043,6 +4108,8 @@
FOURCC('i', 's', 'o', 'm'),
FOURCC('i', 's', 'o', '2'),
FOURCC('a', 'v', 'c', '1'),
+ FOURCC('h', 'v', 'c', '1'),
+ FOURCC('h', 'e', 'v', '1'),
FOURCC('3', 'g', 'p', '4'),
FOURCC('m', 'p', '4', '1'),
FOURCC('m', 'p', '4', '2'),
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 11b80bf..8af0880 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -27,7 +27,6 @@
#include <media/stagefright/MetaData.h>
#include <ui/GraphicBuffer.h>
-#include <sys/atomics.h>
namespace android {
@@ -92,7 +91,7 @@
return;
}
- int prevCount = __atomic_dec(&mRefCount);
+ int prevCount = __sync_fetch_and_sub(&mRefCount, 1);
if (prevCount == 1) {
if (mObserver == NULL) {
delete this;
@@ -112,7 +111,7 @@
}
void MediaBuffer::add_ref() {
- (void) __atomic_inc(&mRefCount);
+ (void) __sync_fetch_and_add(&mRefCount, 1);
}
void *MediaBuffer::data() const {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 8a451c8..cd51582 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -70,11 +70,6 @@
return;
}
- // These are currently still used by the video editing suite.
- addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
- addMediaCodec(
- false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
-
for (size_t i = mCodecInfos.size(); i-- > 0;) {
CodecInfo *info = &mCodecInfos.editItemAt(i);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index c028dbf..354712c 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -381,6 +381,57 @@
return NULL;
}
+status_t OMXCodec::parseHEVCCodecSpecificData(
+ const void *data, size_t size,
+ unsigned *profile, unsigned *level) {
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ // verify minimum size and configurationVersion == 1.
+ if (size < 7 || ptr[0] != 1) {
+ return ERROR_MALFORMED;
+ }
+
+ *profile = (ptr[1] & 31);
+ *level = ptr[12];
+
+ ptr += 22;
+ size -= 22;
+
+ size_t numofArrays = (char)ptr[0];
+ ptr += 1;
+ size -= 1;
+ size_t j = 0, i = 0;
+ for (i = 0; i < numofArrays; i++) {
+ ptr += 1;
+ size -= 1;
+
+ // Num of nals
+ size_t numofNals = U16_AT(ptr);
+ ptr += 2;
+ size -= 2;
+
+ for (j = 0;j < numofNals;j++) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+ }
+ return OK;
+}
+
status_t OMXCodec::parseAVCCodecSpecificData(
const void *data, size_t size,
unsigned *profile, unsigned *level) {
@@ -493,6 +544,20 @@
CODEC_LOGI(
"AVC profile = %u (%s), level = %u",
profile, AVCProfileToString(profile), level);
+ } else if (meta->findData(kKeyHVCC, &type, &data, &size)) {
+ // Parse the HEVCDecoderConfigurationRecord
+
+ unsigned profile, level;
+ status_t err;
+ if ((err = parseHEVCCodecSpecificData(
+ data, size, &profile, &level)) != OK) {
+ ALOGE("Malformed HEVC codec specific data.");
+ return err;
+ }
+
+ CODEC_LOGI(
+ "HEVC profile = %u , level = %u",
+ profile, level);
} else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
addCodecSpecificData(data, size);
@@ -822,6 +887,8 @@
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
compressionFormat = OMX_VIDEO_CodingAVC;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
+ compressionFormat = OMX_VIDEO_CodingHEVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
compressionFormat = OMX_VIDEO_CodingMPEG4;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
@@ -1217,6 +1284,8 @@
compressionFormat = OMX_VIDEO_CodingAVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
compressionFormat = OMX_VIDEO_CodingMPEG4;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
+ compressionFormat = OMX_VIDEO_CodingHEVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
compressionFormat = OMX_VIDEO_CodingH263;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP8, mime)) {
@@ -1411,6 +1480,8 @@
"audio_decoder.g711alaw", "audio_encoder.g711alaw" },
{ MEDIA_MIMETYPE_VIDEO_AVC,
"video_decoder.avc", "video_encoder.avc" },
+ { MEDIA_MIMETYPE_VIDEO_HEVC,
+ "video_decoder.hevc", "video_encoder.hevc" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4,
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
@@ -3009,7 +3080,8 @@
size_t size = specific->mSize;
- if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME)
+ if ((!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mMIME))
&& !(mQuirks & kWantsNALFragments)) {
static const uint8_t kNALStartCode[4] =
{ 0x00, 0x00, 0x00, 0x01 };
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 047fac7..7ff31a1 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -217,6 +217,56 @@
buffer->meta()->setInt32("csd", true);
buffer->meta()->setInt64("timeUs", 0);
msg->setBuffer("csd-1", buffer);
+ } else if (meta->findData(kKeyHVCC, &type, &data, &size)) {
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1] & 31;
+ uint8_t level = ptr[12];
+ ptr += 22;
+ size -= 22;
+
+
+ size_t numofArrays = (char)ptr[0];
+ ptr += 1;
+ size -= 1;
+ size_t j = 0, i = 0;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (i = 0; i < numofArrays; i++) {
+ ptr += 1;
+ size -= 1;
+
+ //Num of nals
+ size_t numofNals = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ for (j = 0; j < numofNals; j++) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+ }
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+
} else if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
CHECK_EQ(esds.InitCheck(), (status_t)OK);
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 49ff238..afb00aa 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -3,7 +3,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- SoftAAC2.cpp
+ SoftAAC2.cpp \
+ DrcPresModeWrap.cpp
LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright/include \
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
new file mode 100644
index 0000000..129ad65
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "DrcPresModeWrap.h"
+
+#include <assert.h>
+
+#define LOG_TAG "SoftAAC2_DrcWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+//#define DRC_PRES_MODE_WRAP_DEBUG
+
+#define GPM_ENCODER_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 64
+
+CDrcPresModeWrapper::CDrcPresModeWrapper()
+{
+ mDataUpdate = true;
+
+ /* Data from streamInfo. */
+ /* Initialized to the same values as in the aac decoder */
+ mStreamPRL = -1;
+ mStreamDRCPresMode = -1;
+ mStreamNrAACChan = 0;
+ mStreamNrOutChan = 0;
+
+ /* Desired values (set by user). */
+ /* Initialized to the same values as in the aac decoder */
+ mDesTarget = -1;
+ mDesAttFactor = 0;
+ mDesBoostFactor = 0;
+ mDesHeavy = 0;
+
+ mEncoderTarget = -1;
+
+ /* Values from last time. */
+ /* Initialized to the same values as the desired values */
+ mLastTarget = -1;
+ mLastAttFactor = 0;
+ mLastBoostFactor = 0;
+ mLastHeavy = 0;
+}
+
+CDrcPresModeWrapper::~CDrcPresModeWrapper()
+{
+}
+
+void
+CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle)
+{
+ mHandleDecoder = handle;
+}
+
+void
+CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo)
+{
+ assert(pStreamInfo);
+
+ if (mStreamPRL != pStreamInfo->drcProgRefLev) {
+ mStreamPRL = pStreamInfo->drcProgRefLev;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL);
+#endif
+ }
+
+ if (mStreamDRCPresMode != pStreamInfo->drcPresMode) {
+ mStreamDRCPresMode = pStreamInfo->drcPresMode;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode);
+#endif
+ }
+
+ if (mStreamNrAACChan != pStreamInfo->aacNumChannels) {
+ mStreamNrAACChan = pStreamInfo->aacNumChannels;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan);
+#endif
+ }
+
+ if (mStreamNrOutChan != pStreamInfo->numChannels) {
+ mStreamNrOutChan = pStreamInfo->numChannels;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan);
+#endif
+ }
+
+
+
+ if (mStreamNrOutChan<mStreamNrAACChan) {
+ mIsDownmix = true;
+ } else {
+ mIsDownmix = false;
+ }
+
+ if (mIsDownmix && (mStreamNrOutChan == 1)) {
+ mIsMonoDownmix = true;
+ } else {
+ mIsMonoDownmix = false;
+ }
+
+ if (mIsDownmix && mStreamNrOutChan == 2){
+ mIsStereoDownmix = true;
+ } else {
+ mIsStereoDownmix = false;
+ }
+
+}
+
+void
+CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value)
+{
+ switch (param) {
+ case DRC_PRES_MODE_WRAP_DESIRED_TARGET:
+ mDesTarget = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR:
+ mDesAttFactor = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR:
+ mDesBoostFactor = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_HEAVY:
+ mDesHeavy = value;
+ break;
+ case DRC_PRES_MODE_WRAP_ENCODER_TARGET:
+ mEncoderTarget = value;
+ break;
+ default:
+ break;
+ }
+ mDataUpdate = true;
+}
+
+void
+CDrcPresModeWrapper::update()
+{
+ // Get Data from Decoder
+ int progRefLevel = mStreamPRL;
+ int drcPresMode = mStreamDRCPresMode;
+
+ // by default, do as desired
+ int newTarget = mDesTarget;
+ int newAttFactor = mDesAttFactor;
+ int newBoostFactor = mDesBoostFactor;
+ int newHeavy = mDesHeavy;
+
+ if (mDataUpdate) {
+ // sanity check
+ if (mDesTarget < MAX_TARGET_LEVEL){
+ mDesTarget = MAX_TARGET_LEVEL; // limit target level to -16 dB or below
+ newTarget = MAX_TARGET_LEVEL;
+ }
+
+ if (mEncoderTarget != -1) {
+ if (mDesTarget<124) { // if target level > -31 dB
+ if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+ // no stereo or mono downmixing, calculated scaling of light DRC
+ /* use as little compression as possible */
+ newAttFactor = 0;
+ newBoostFactor = 0;
+ if (mDesTarget<progRefLevel) { // if target level > PRL
+ if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level
+ // mEncoderTarget > target level > PRL
+ int calcFactor;
+ float calcFactor_norm;
+ // 0.0f < calcFactor_norm < 1.0f
+ calcFactor_norm = (float)(mDesTarget - progRefLevel) /
+ (float)(mEncoderTarget - progRefLevel);
+ calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127
+ // calcFactor is the lower limit
+ newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor;
+ // new AttFactor will be always = calcFactor, as it is set to 0 before.
+ newBoostFactor = newAttFactor;
+ } else {
+ /* target level > mEncoderTarget > PRL */
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127;
+ newBoostFactor = 127;
+ }
+ } else { // target level <= PRL
+ // no restrictions required
+ // newAttFactor = newAttFactor;
+ }
+ } else { // downmixing
+ // if target level > -23 dB or mono downmix
+ if ( (mDesTarget<92) || mIsMonoDownmix ) {
+ newHeavy = 1;
+ } else {
+ // we perform a downmix, so, we need at least full light DRC
+ newAttFactor = 127;
+ }
+ }
+ } else { // target level <= -31 dB
+ // playback -31 dB: light DRC only needed if we perform downmixing
+ if (mIsDownmix) { // we do downmixing
+ newAttFactor = 127;
+ }
+ }
+ }
+ else { // handle other used encoder target levels
+
+ // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+ if (mStreamNrAACChan > 6) {
+ drcPresMode = 0;
+ }
+
+ switch (drcPresMode) {
+ case 0:
+ default: // presentation mode not indicated
+ {
+
+ if (mDesTarget<124) { // if target level > -31 dB
+ // no stereo or mono downmixing
+ if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+ if (mDesTarget<progRefLevel) { // if target level > PRL
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127; // at least, use light compression
+ } else { // target level <= PRL
+ // no restrictions required
+ // newAttFactor = newAttFactor;
+ }
+ } else { // downmixing
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+
+ // if target level > -23 dB or mono downmix
+ if ( (mDesTarget < 92) || mIsMonoDownmix ) {
+ newHeavy = 1;
+ } else{
+ // we perform a downmix, so, we need at least full light DRC
+ newAttFactor = 127;
+ }
+ }
+ } else { // target level <= -31 dB
+ if (mIsDownmix) { // we do downmixing.
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ // Presentation mode 1 and 2 according to ETSI TS 101 154:
+ // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding
+ // in Broadcasting Applications based on the MPEG-2 Transport Stream,
+ // section C.5.4., "Decoding", and Table C.33
+ // ISO DRC -> newHeavy = 0 (Use light compression, MPEG-style)
+ // Compression_value -> newHeavy = 1 (Use heavy compression, DVB-style)
+ // scaling restricted -> newAttFactor = 127
+
+ case 1: // presentation mode 1, Light:-31/Heavy:-23
+ {
+ if (mDesTarget < 124) { // if target level > -31 dB
+ // playback up to -23 dB
+ newHeavy = 1;
+ } else { // target level <= -31 dB
+ // playback -31 dB
+ if (mIsDownmix) { // we do downmixing.
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ case 2: // presentation mode 2, Light:-23/Heavy:-23
+ {
+ if (mDesTarget < 124) { // if target level > -31 dB
+ // playback up to -23 dB
+ if (mIsMonoDownmix) { // if mono downmix
+ newHeavy = 1;
+ } else {
+ newHeavy = 0;
+ newAttFactor = 127;
+ }
+ } else { // target level <= -31 dB
+ // playback -31 dB
+ newHeavy = 0;
+ if (mIsDownmix) { // we do downmixing.
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ } // switch()
+ } // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL)
+
+ // sanity again
+ if (newHeavy == 1) {
+ newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
+ newAttFactor = 127;
+ }
+
+ // update the decoder
+ if (newTarget != mLastTarget) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget);
+ mLastTarget = newTarget;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newTarget != mDesTarget)
+ ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget);
+ else
+ ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget);
+#endif
+ }
+
+ if (newAttFactor != mLastAttFactor) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor);
+ mLastAttFactor = newAttFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newAttFactor != mDesAttFactor)
+ ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor);
+ else
+ ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor);
+#endif
+ }
+
+ if (newBoostFactor != mLastBoostFactor) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor);
+ mLastBoostFactor = newBoostFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newBoostFactor != mDesBoostFactor)
+ ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n",
+ newBoostFactor, mDesBoostFactor);
+ else
+ ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor);
+#endif
+ }
+
+ if (newHeavy != mLastHeavy) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy);
+ mLastHeavy = newHeavy;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newHeavy != mDesHeavy)
+ ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n",
+ newHeavy, mDesHeavy);
+ else
+ ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy);
+#endif
+ }
+
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget,
+ newAttFactor, newBoostFactor, newHeavy);
+#endif
+ mDataUpdate = false;
+
+ } // if (mDataUpdate)
+}
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
new file mode 100644
index 0000000..f0b6cf2
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#pragma once
+#include "aacdecoder_lib.h"
+
+typedef enum
+{
+ DRC_PRES_MODE_WRAP_DESIRED_TARGET = 0x0000,
+ DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR = 0x0001,
+ DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR = 0x0002,
+ DRC_PRES_MODE_WRAP_DESIRED_HEAVY = 0x0003,
+ DRC_PRES_MODE_WRAP_ENCODER_TARGET = 0x0004
+} DRC_PRES_MODE_WRAP_PARAM;
+
+
+class CDrcPresModeWrapper {
+public:
+ CDrcPresModeWrapper();
+ ~CDrcPresModeWrapper();
+ void setDecoderHandle(const HANDLE_AACDECODER handle);
+ void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value);
+ void submitStreamData(CStreamInfo*);
+ void update();
+
+protected:
+ HANDLE_AACDECODER mHandleDecoder;
+ int mDesTarget;
+ int mDesAttFactor;
+ int mDesBoostFactor;
+ int mDesHeavy;
+
+ int mEncoderTarget;
+
+ int mLastTarget;
+ int mLastAttFactor;
+ int mLastBoostFactor;
+ int mLastHeavy;
+
+ SCHAR mStreamPRL;
+ SCHAR mStreamDRCPresMode;
+ INT mStreamNrAACChan;
+ INT mStreamNrOutChan;
+
+ bool mIsDownmix;
+ bool mIsMonoDownmix;
+ bool mIsStereoDownmix;
+
+ bool mDataUpdate;
+};
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 532e36f..edb7448 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -25,16 +25,22 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
+#include <math.h>
+
#define FILEREAD_MAX_LAYERS 2
#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut"
#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
namespace android {
@@ -57,18 +63,19 @@
mStreamInfo(NULL),
mIsADTS(false),
mInputBufferCount(0),
+ mOutputBufferCount(0),
mSignalledError(false),
- mSawInputEos(false),
- mSignalledOutputEos(false),
- mAnchorTimeUs(0),
- mNumSamplesOutput(0),
mOutputPortSettingsChange(NONE) {
+ for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) {
+ mAnchorTimeUs[i] = 0;
+ }
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
+ delete mOutputDelayRingBuffer;
}
void SoftAAC2::initPorts() {
@@ -121,36 +128,72 @@
status = OK;
}
}
- mDecoderHasData = false;
- // for streams that contain metadata, use the mobile profile DRC settings unless overridden
- // by platform properties:
+ mEndOfInput = false;
+ mEndOfOutput = false;
+ mOutputDelayCompensated = 0;
+ mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
+ mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
+ mOutputDelayRingBufferWritePos = 0;
+ mOutputDelayRingBufferReadPos = 0;
+
+ if (mAACDecoder == NULL) {
+ ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
+ }
+
+ //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
+
+ //init DRC wrapper
+ mDrcWrap.setDecoderHandle(mAACDecoder);
+ mDrcWrap.submitStreamData(mStreamInfo);
+
+ // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
+ // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
char value[PROPERTY_VALUE_MAX];
- // * AAC_DRC_REFERENCE_LEVEL
+ // DRC_PRES_MODE_WRAP_DESIRED_TARGET
if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) {
unsigned refLevel = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d",
- refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel);
+ ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel,
+ DRC_DEFAULT_MOBILE_REF_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL);
}
- // * AAC_DRC_ATTENUATION_FACTOR
+ // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) {
unsigned cut = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d",
- cut, DRC_DEFAULT_MOBILE_DRC_CUT);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut);
+ ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut,
+ DRC_DEFAULT_MOBILE_DRC_CUT);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
}
- // * AAC_DRC_BOOST_FACTOR (note: no default, using cut)
+ // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) {
unsigned boost = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost);
+ ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost,
+ DRC_DEFAULT_MOBILE_DRC_BOOST);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+ }
+ // DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+ if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) {
+ unsigned heavy = atoi(value);
+ ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy,
+ DRC_DEFAULT_MOBILE_DRC_HEAVY);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy);
+ } else {
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY);
+ }
+ // DRC_PRES_MODE_WRAP_ENCODER_TARGET
+ if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) {
+ unsigned encoderRefLevel = atoi(value);
+ ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d",
+ encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel);
+ } else {
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
}
return status;
@@ -290,19 +333,101 @@
return mInputBufferCount > 0;
}
-void SoftAAC2::maybeConfigureDownmix() const {
- if (mStreamInfo->numChannels > 2) {
- char value[PROPERTY_VALUE_MAX];
- if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
- (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
- ALOGI("Downmixing multichannel AAC to stereo");
- aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
- mStreamInfo->numChannels = 2;
- // By default, the decoder creates a 5.1 channel downmix signal
- // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
- // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+void SoftAAC2::configureDownmix() const {
+ char value[PROPERTY_VALUE_MAX];
+ if (!(property_get("media.aac_51_output_enabled", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
+ ALOGI("limiting to stereo output");
+ aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
+ // By default, the decoder creates a 5.1 channel downmix signal
+ // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
+ // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+ }
+}
+
+bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+ if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
+ && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
+ || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
+ // faster memcopy loop without checks, if the preconditions allow this
+ for (int32_t i = 0; i < numSamples; i++) {
+ mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
+ }
+
+ if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+ }
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER OVERFLOW");
+ return false;
+ }
+ } else {
+ ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
+
+ for (int32_t i = 0; i < numSamples; i++) {
+ mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
+ mOutputDelayRingBufferWritePos++;
+ if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+ }
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER OVERFLOW");
+ return false;
+ }
}
}
+ return true;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+ if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
+ && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
+ || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
+ // faster memcopy loop without checks, if the preconditions allow this
+ if (samples != 0) {
+ for (int32_t i = 0; i < numSamples; i++) {
+ samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
+ }
+ } else {
+ mOutputDelayRingBufferReadPos += numSamples;
+ }
+ if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+ }
+ } else {
+ ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
+
+ for (int32_t i = 0; i < numSamples; i++) {
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER UNDERRUN");
+ return -1;
+ }
+ if (samples != 0) {
+ samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
+ }
+ mOutputDelayRingBufferReadPos++;
+ if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+ }
+ }
+ }
+ return numSamples;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
+ int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
+ if (available < 0) {
+ available += mOutputDelayRingBufferSize;
+ }
+ if (available < 0) {
+ ALOGE("FATAL RING BUFFER ERROR");
+ return 0;
+ }
+ return available;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+ return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}
void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
@@ -318,12 +443,11 @@
List<BufferInfo *> &outQueue = getPortQueue(1);
if (portIndex == 0 && mInputBufferCount == 0) {
- ++mInputBufferCount;
- BufferInfo *info = *inQueue.begin();
- OMX_BUFFERHEADERTYPE *header = info->mHeader;
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- inBuffer[0] = header->pBuffer + header->nOffset;
- inBufferLength[0] = header->nFilledLen;
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
AAC_DECODER_ERROR decoderErr =
aacDecoder_ConfigRaw(mAACDecoder,
@@ -331,19 +455,25 @@
inBufferLength);
if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
- inQueue.erase(inQueue.begin());
- info->mOwnedByUs = false;
- notifyEmptyBufferDone(header);
+ mInputBufferCount++;
+ mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ configureDownmix();
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
- maybeConfigureDownmix();
ALOGI("Initially configuring decoder: %d Hz, %d channels",
mStreamInfo->sampleRate,
mStreamInfo->numChannels);
@@ -355,146 +485,20 @@
return;
}
- while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) {
- BufferInfo *inInfo = NULL;
- OMX_BUFFERHEADERTYPE *inHeader = NULL;
+ while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
if (!inQueue.empty()) {
- inInfo = *inQueue.begin();
- inHeader = inInfo->mHeader;
- }
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- BufferInfo *outInfo = *outQueue.begin();
- OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
- outHeader->nFlags = 0;
-
- if (inHeader) {
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
- mSawInputEos = true;
- }
-
- if (inHeader->nOffset == 0 && inHeader->nFilledLen) {
- mAnchorTimeUs = inHeader->nTimeStamp;
- mNumSamplesOutput = 0;
- }
-
- if (mIsADTS && inHeader->nFilledLen) {
- size_t adtsHeaderSize = 0;
- // skip 30 bits, aac_frame_length follows.
- // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
-
- const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
-
- bool signalError = false;
- if (inHeader->nFilledLen < 7) {
- ALOGE("Audio data too short to contain even the ADTS header. "
- "Got %d bytes.", inHeader->nFilledLen);
- hexdump(adtsHeader, inHeader->nFilledLen);
- signalError = true;
- } else {
- bool protectionAbsent = (adtsHeader[1] & 1);
-
- unsigned aac_frame_length =
- ((adtsHeader[3] & 3) << 11)
- | (adtsHeader[4] << 3)
- | (adtsHeader[5] >> 5);
-
- if (inHeader->nFilledLen < aac_frame_length) {
- ALOGE("Not enough audio data for the complete frame. "
- "Got %d bytes, frame size according to the ADTS "
- "header is %u bytes.",
- inHeader->nFilledLen, aac_frame_length);
- hexdump(adtsHeader, inHeader->nFilledLen);
- signalError = true;
- } else {
- adtsHeaderSize = (protectionAbsent ? 7 : 9);
-
- inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
- inBufferLength[0] = aac_frame_length - adtsHeaderSize;
-
- inHeader->nOffset += adtsHeaderSize;
- inHeader->nFilledLen -= adtsHeaderSize;
- }
- }
-
- if (signalError) {
- mSignalledError = true;
-
- notify(OMX_EventError,
- OMX_ErrorStreamCorrupt,
- ERROR_MALFORMED,
- NULL);
-
- return;
- }
+ mEndOfInput = true;
} else {
- inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
- inBufferLength[0] = inHeader->nFilledLen;
+ mEndOfInput = false;
}
- } else {
- inBufferLength[0] = 0;
- }
-
- // Fill and decode
- INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(
- outHeader->pBuffer + outHeader->nOffset);
-
- bytesValid[0] = inBufferLength[0];
-
- int prevSampleRate = mStreamInfo->sampleRate;
- int prevNumChannels = mStreamInfo->numChannels;
-
- AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS;
- while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- mDecoderHasData |= (bytesValid[0] > 0);
- aacDecoder_Fill(mAACDecoder,
- inBuffer,
- inBufferLength,
- bytesValid);
-
- decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
- outBuffer,
- outHeader->nAllocLen,
- 0 /* flags */);
- if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- if (mSawInputEos && bytesValid[0] <= 0) {
- if (mDecoderHasData) {
- // flush out the decoder's delayed data by calling DecodeFrame
- // one more time, with the AACDEC_FLUSH flag set
- decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
- outBuffer,
- outHeader->nAllocLen,
- AACDEC_FLUSH);
- mDecoderHasData = false;
- }
- outHeader->nFlags = OMX_BUFFERFLAG_EOS;
- mSignalledOutputEos = true;
- break;
- } else {
- ALOGW("Not enough bits, bytesValid %d", bytesValid[0]);
- }
- }
- }
-
- size_t numOutBytes =
- mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
-
- if (inHeader) {
- if (decoderErr == AAC_DEC_OK) {
- UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
- inHeader->nFilledLen -= inBufferUsedLength;
- inHeader->nOffset += inBufferUsedLength;
- } else {
- ALOGW("AAC decoder returned error %d, substituting silence",
- decoderErr);
-
- memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
-
- // Discard input buffer.
- inHeader->nFilledLen = 0;
-
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
- // fall through
+ if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen?
+ mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] =
+ inHeader->nTimeStamp;
}
if (inHeader->nFilledLen == 0) {
@@ -503,54 +507,282 @@
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
+ } else {
+ if (mIsADTS) {
+ size_t adtsHeaderSize = 0;
+ // skip 30 bits, aac_frame_length follows.
+ // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+ const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+ bool signalError = false;
+ if (inHeader->nFilledLen < 7) {
+ ALOGE("Audio data too short to contain even the ADTS header. "
+ "Got %d bytes.", inHeader->nFilledLen);
+ hexdump(adtsHeader, inHeader->nFilledLen);
+ signalError = true;
+ } else {
+ bool protectionAbsent = (adtsHeader[1] & 1);
+
+ unsigned aac_frame_length =
+ ((adtsHeader[3] & 3) << 11)
+ | (adtsHeader[4] << 3)
+ | (adtsHeader[5] >> 5);
+
+ if (inHeader->nFilledLen < aac_frame_length) {
+ ALOGE("Not enough audio data for the complete frame. "
+ "Got %d bytes, frame size according to the ADTS "
+ "header is %u bytes.",
+ inHeader->nFilledLen, aac_frame_length);
+ hexdump(adtsHeader, inHeader->nFilledLen);
+ signalError = true;
+ } else {
+ adtsHeaderSize = (protectionAbsent ? 7 : 9);
+
+ inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+ inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+
+ inHeader->nOffset += adtsHeaderSize;
+ inHeader->nFilledLen -= adtsHeaderSize;
+ }
+ }
+
+ if (signalError) {
+ mSignalledError = true;
+
+ notify(OMX_EventError,
+ OMX_ErrorStreamCorrupt,
+ ERROR_MALFORMED,
+ NULL);
+
+ return;
+ }
+ } else {
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
+ }
+
+ // Fill and decode
+ bytesValid[0] = inBufferLength[0];
+
+ INT prevSampleRate = mStreamInfo->sampleRate;
+ INT prevNumChannels = mStreamInfo->numChannels;
+
+ aacDecoder_Fill(mAACDecoder,
+ inBuffer,
+ inBufferLength,
+ bytesValid);
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ 0 /* flags */);
+
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+ ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ if (bytesValid[0] != 0) {
+ ALOGE("bytesValid[0] != 0 should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ size_t numOutBytes =
+ mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+ if (decoderErr == AAC_DEC_OK) {
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+ inHeader->nFilledLen -= inBufferUsedLength;
+ inHeader->nOffset += inBufferUsedLength;
+ } else {
+ ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+ memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+
+ // Discard input buffer.
+ inHeader->nFilledLen = 0;
+
+ aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+ // fall through
+ }
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mOutputBufferCount > 1) {
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGE("can not reconfigure AAC output");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ }
+ if (mInputBufferCount <= 2) { // TODO: <= 1
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+ prevSampleRate, mStreamInfo->sampleRate,
+ prevNumChannels, mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ return;
+ }
+ } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+ ALOGW("Invalid AAC stream");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ } else {
+ ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+ }
}
}
- /*
- * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
- * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
- * rate system and the sampling rate in the final output is actually
- * doubled compared with the core AAC decoder sampling rate.
- *
- * Explicit signalling is done by explicitly defining SBR audio object
- * type in the bitstream. Implicit signalling is done by embedding
- * SBR content in AAC extension payload specific to SBR, and hence
- * requires an AAC decoder to perform pre-checks on actual audio frames.
- *
- * Thus, we could not say for sure whether a stream is
- * AAC+/eAAC+ until the first data frame is decoded.
- */
- if (mInputBufferCount <= 2) {
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- maybeConfigureDownmix();
- ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
- prevSampleRate, mStreamInfo->sampleRate,
- prevNumChannels, mStreamInfo->numChannels);
+ int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
+ if (!mEndOfInput && mOutputDelayCompensated < outputDelay) {
+ // discard outputDelay at the beginning
+ int32_t toCompensate = outputDelay - mOutputDelayCompensated;
+ int32_t discard = outputDelayRingBufferSamplesAvailable();
+ if (discard > toCompensate) {
+ discard = toCompensate;
+ }
+ int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
+ mOutputDelayCompensated += discarded;
+ continue;
+ }
+
+ if (mEndOfInput) {
+ while (mOutputDelayCompensated > 0) {
+ // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ AACDEC_FLUSH);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+ if (tmpOutBufferSamples > mOutputDelayCompensated) {
+ tmpOutBufferSamples = mOutputDelayCompensated;
+ }
+ outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+ mOutputDelayCompensated -= tmpOutBufferSamples;
+ }
+ }
+
+ while (!outQueue.empty()
+ && outputDelayRingBufferSamplesAvailable()
+ >= mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (outHeader->nOffset != 0) {
+ ALOGE("outHeader->nOffset != 0 is not handled");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
- } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
- ALOGW("Invalid AAC stream");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
- if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
- // We'll only output data if we successfully decoded it or
- // we've previously decoded valid data, in the latter case
- // (decode failed) we'll output a silent frame.
- outHeader->nFilledLen = numOutBytes;
+ INT_PCM *outBuffer =
+ reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+ if (outHeader->nOffset
+ + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
+ > outHeader->nAllocLen) {
+ ALOGE("buffer overflow");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
- outHeader->nTimeStamp =
- mAnchorTimeUs
- + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate;
+ }
+ int32_t ns = outputDelayRingBufferGetSamples(outBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow
+ if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
- mNumSamplesOutput += mStreamInfo->frameSize;
+ outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
+ * sizeof(int16_t);
+ if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ mEndOfOutput = true;
+ } else {
+ outHeader->nFlags = 0;
+ }
+ outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+ % kNumDelayBlocksMax];
+
+ mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
@@ -558,8 +790,48 @@
outHeader = NULL;
}
- if (decoderErr == AAC_DEC_OK) {
- ++mInputBufferCount;
+ if (mEndOfInput) {
+ if (outputDelayRingBufferSamplesAvailable() > 0
+ && outputDelayRingBufferSamplesAvailable()
+ < mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+ if (!mEndOfOutput) {
+ // send empty block signaling EOS
+ mEndOfOutput = true;
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (outHeader->nOffset != 0) {
+ ALOGE("outHeader->nOffset != 0 is not handled");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer
+ + outHeader->nOffset);
+ int32_t ns = 0;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+ % kNumDelayBlocksMax];
+
+ mOutputBufferCount++;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+ break; // if outQueue not empty but no more output
+ }
}
}
}
@@ -574,34 +846,68 @@
// but only if initialization has already happened.
if (mInputBufferCount != 0) {
mInputBufferCount = 1;
- mStreamInfo->sampleRate = 0;
+ mOutputBufferCount = -1;
}
+ } else {
+ while (outputDelayRingBufferSamplesAvailable() > 0) {
+ int32_t ns = outputDelayRingBufferGetSamples(0,
+ mStreamInfo->frameSize * mStreamInfo->numChannels);
+ if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ }
+ mOutputBufferCount++;
+ }
+ mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
}
}
void SoftAAC2::drainDecoder() {
- // a buffer big enough for 6 channels of decoded HE-AAC
- short buf [2048*6];
- aacDecoder_DecodeFrame(mAACDecoder,
- buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
- aacDecoder_DecodeFrame(mAACDecoder,
- buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
- mDecoderHasData = false;
+ int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+ // flush decoder until outputDelay is compensated
+ while (mOutputDelayCompensated > 0) {
+ // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ AACDEC_FLUSH);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+ if (tmpOutBufferSamples > mOutputDelayCompensated) {
+ tmpOutBufferSamples = mOutputDelayCompensated;
+ }
+ outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+
+ mOutputDelayCompensated -= tmpOutBufferSamples;
+ }
}
void SoftAAC2::onReset() {
drainDecoder();
// reset the "configured" state
mInputBufferCount = 0;
- mNumSamplesOutput = 0;
+ mOutputBufferCount = 0;
+ mOutputDelayCompensated = 0;
+ mOutputDelayRingBufferWritePos = 0;
+ mOutputDelayRingBufferReadPos = 0;
+ mEndOfInput = false;
+ mEndOfOutput = false;
+
// To make the codec behave the same before and after a reset, we need to invalidate the
// streaminfo struct. This does that:
- mStreamInfo->sampleRate = 0;
+ mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
mSignalledError = false;
- mSawInputEos = false;
- mSignalledOutputEos = false;
mOutputPortSettingsChange = NONE;
}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index a7ea1e2..5cde03a 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -20,6 +20,7 @@
#include "SimpleSoftOMXComponent.h"
#include "aacdecoder_lib.h"
+#include "DrcPresModeWrap.h"
namespace android {
@@ -47,18 +48,19 @@
enum {
kNumInputBuffers = 4,
kNumOutputBuffers = 4,
+ kNumDelayBlocksMax = 8,
};
HANDLE_AACDECODER mAACDecoder;
CStreamInfo *mStreamInfo;
bool mIsADTS;
- bool mDecoderHasData;
+ bool mIsFirst;
size_t mInputBufferCount;
+ size_t mOutputBufferCount;
bool mSignalledError;
- bool mSawInputEos;
- bool mSignalledOutputEos;
- int64_t mAnchorTimeUs;
- int64_t mNumSamplesOutput;
+ int64_t mAnchorTimeUs[kNumDelayBlocksMax];
+
+ CDrcPresModeWrapper mDrcWrap;
enum {
NONE,
@@ -69,9 +71,22 @@
void initPorts();
status_t initDecoder();
bool isConfigured() const;
- void maybeConfigureDownmix() const;
+ void configureDownmix() const;
void drainDecoder();
+// delay compensation
+ bool mEndOfInput;
+ bool mEndOfOutput;
+ int32_t mOutputDelayCompensated;
+ int32_t mOutputDelayRingBufferSize;
+ short *mOutputDelayRingBuffer;
+ int32_t mOutputDelayRingBufferWritePos;
+ int32_t mOutputDelayRingBufferReadPos;
+ bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
+ int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
+ int32_t outputDelayRingBufferSamplesAvailable();
+ int32_t outputDelayRingBufferSamplesLeft();
+
DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
};
diff --git a/media/libstagefright/codecs/hevcdec/Android.mk b/media/libstagefright/codecs/hevcdec/Android.mk
new file mode 100644
index 0000000..960602f
--- /dev/null
+++ b/media/libstagefright/codecs/hevcdec/Android.mk
@@ -0,0 +1,26 @@
+ifeq ($(if $(wildcard external/libhevc),1,0),1)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libstagefright_soft_hevcdec
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_LIBRARIES := libhevcdec
+LOCAL_SRC_FILES := SoftHEVC.cpp
+
+LOCAL_C_INCLUDES := $(TOP)/external/libhevc/decoder
+LOCAL_C_INCLUDES += $(TOP)/external/libhevc/common
+LOCAL_C_INCLUDES += $(TOP)/frameworks/av/media/libstagefright/include
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax
+
+LOCAL_SHARED_LIBRARIES := libstagefright
+LOCAL_SHARED_LIBRARIES += libstagefright_omx
+LOCAL_SHARED_LIBRARIES += libstagefright_foundation
+LOCAL_SHARED_LIBRARIES += libutils
+LOCAL_SHARED_LIBRARIES += liblog
+
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
new file mode 100644
index 0000000..0aae5ed
--- /dev/null
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) 2014 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 "SoftHEVC"
+#include <utils/Log.h>
+
+#include "ihevc_typedefs.h"
+#include "iv.h"
+#include "ivd.h"
+#include "ithread.h"
+#include "ihevcd_cxa.h"
+#include "SoftHEVC.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <OMX_VideoExt.h>
+
+namespace android {
+
+#define componentName "video_decoder.hevc"
+#define codingType OMX_VIDEO_CodingHEVC
+#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_HEVC
+
+/** Function and structure definitions to keep code similar for each codec */
+#define ivdec_api_function ihevcd_cxa_api_function
+#define ivdext_init_ip_t ihevcd_cxa_init_ip_t
+#define ivdext_init_op_t ihevcd_cxa_init_op_t
+#define ivdext_fill_mem_rec_ip_t ihevcd_cxa_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t ihevcd_cxa_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
+
+#define IVDEXT_CMD_CTL_SET_NUM_CORES \
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
+
+static const CodecProfileLevel kProfileLevels[] = {
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel1 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel2 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel21 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel3 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel31 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel4 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel41 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel5 },
+ { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel51 },
+};
+
+SoftHEVC::SoftHEVC(
+ const char *name,
+ const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData,
+ OMX_COMPONENTTYPE **component)
+ : SoftVideoDecoderOMXComponent(name, componentName, codingType,
+ kProfileLevels, ARRAY_SIZE(kProfileLevels),
+ CODEC_MAX_WIDTH /* width */, CODEC_MAX_HEIGHT /* height */, callbacks,
+ appData, component) {
+ initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers,
+ CODEC_MIME_TYPE);
+
+ mOmxColorFormat = OMX_COLOR_FormatYUV420Planar;
+ mStride = mWidth;
+
+ if (OMX_COLOR_FormatYUV420Planar == mOmxColorFormat) {
+ mIvColorFormat = IV_YUV_420P;
+ } else if (OMX_COLOR_FormatYUV420SemiPlanar == mOmxColorFormat) {
+ mIvColorFormat = IV_YUV_420SP_UV;
+ }
+
+ mInitWidth = mWidth;
+ mInitHeight = mHeight;
+
+ CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftHEVC::~SoftHEVC() {
+ ALOGD("In SoftHEVC::~SoftHEVC");
+ CHECK_EQ(deInitDecoder(), (status_t)OK);
+}
+
+static size_t GetCPUCoreCount() {
+ long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ ALOGD("Number of CPU cores: %ld", cpuCoreCount);
+ return (size_t)cpuCoreCount;
+}
+
+status_t SoftHEVC::getVersion() {
+ ivd_ctl_getversioninfo_ip_t s_ctl_ip;
+ ivd_ctl_getversioninfo_op_t s_ctl_op;
+ UWORD8 au1_buf[512];
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+ s_ctl_ip.pv_version_buffer = au1_buf;
+ s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
+ (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in getting version number: 0x%x",
+ s_ctl_op.u4_error_code);
+ } else {
+ ALOGD("Ittiam decoder version number: %s",
+ (char *)s_ctl_ip.pv_version_buffer);
+ }
+ return OK;
+}
+
+status_t SoftHEVC::setParams(WORD32 stride, IVD_VIDEO_DECODE_MODE_T decMode) {
+ ivd_ctl_set_config_ip_t s_ctl_ip;
+ ivd_ctl_set_config_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+ s_ctl_ip.u4_disp_wd = stride;
+ s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+
+ s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+ s_ctl_ip.e_vid_dec_mode = decMode;
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+
+ ALOGD("Set the run-time (dynamic) parameters");
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
+ (void *)&s_ctl_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the run-time parameters: 0x%x",
+ s_ctl_op.u4_error_code);
+
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftHEVC::resetPlugin() {
+ mIsInFlush = false;
+ mReceivedEOS = false;
+ memset(mTimeStamps, 0, sizeof(mTimeStamps));
+ memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+ /* Initialize both start and end times */
+ gettimeofday(&mTimeStart, NULL);
+ gettimeofday(&mTimeEnd, NULL);
+
+ return OK;
+}
+
+status_t SoftHEVC::resetDecoder() {
+ ivd_ctl_reset_ip_t s_ctl_ip;
+ ivd_ctl_reset_op_t s_ctl_op;
+ IV_API_CALL_STATUS_T status;
+
+ s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+ s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+ s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
+ (void *)&s_ctl_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ /* Set the run-time (dynamic) parameters */
+ setParams(0, IVD_DECODE_FRAME);
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ return OK;
+}
+
+status_t SoftHEVC::setNumCores() {
+ ivdext_ctl_set_num_cores_ip_t s_set_cores_ip;
+ ivdext_ctl_set_num_cores_op_t s_set_cores_op;
+ IV_API_CALL_STATUS_T status;
+ s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+ s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES);
+ s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+ s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+ ALOGD("Set number of cores to %u", s_set_cores_ip.u4_num_cores);
+ status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip,
+ (void *)&s_set_cores_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in setting number of cores: 0x%x",
+ s_set_cores_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t SoftHEVC::setFlushMode() {
+ IV_API_CALL_STATUS_T status;
+ ivd_ctl_flush_ip_t s_video_flush_ip;
+ ivd_ctl_flush_op_t s_video_flush_op;
+
+ s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+ s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+ s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+ ALOGD("Set the decoder in flush mode ");
+
+ /* Set the decoder in Flush mode, subsequent decode() calls will flush */
+ status = ivdec_api_function(mCodecCtx, (void *)&s_video_flush_ip,
+ (void *)&s_video_flush_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
+ s_video_flush_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mIsInFlush = true;
+ return OK;
+}
+
+status_t SoftHEVC::initDecoder() {
+ IV_API_CALL_STATUS_T status;
+
+ UWORD32 u4_num_reorder_frames;
+ UWORD32 u4_num_ref_frames;
+ UWORD32 u4_share_disp_buf;
+ WORD32 i4_level;
+
+ mNumCores = GetCPUCoreCount();
+
+ /* Initialize number of ref and reorder modes (for HEVC) */
+ u4_num_reorder_frames = 16;
+ u4_num_ref_frames = 16;
+ u4_share_disp_buf = 0;
+
+ if ((mWidth * mHeight) > (1920 * 1088)) {
+ i4_level = 50;
+ } else if ((mWidth * mHeight) > (1280 * 720)) {
+ i4_level = 41;
+ } else {
+ i4_level = 31;
+ }
+
+ {
+ iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+ iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+ s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+ s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+ s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+ ALOGV("Get number of mem records");
+ status = ivdec_api_function(mCodecCtx, (void*)&s_num_mem_rec_ip,
+ (void*)&s_num_mem_rec_op);
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in getting mem records: 0x%x",
+ s_num_mem_rec_op.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+
+ mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+ }
+
+ mMemRecords = (iv_mem_rec_t*)ivd_aligned_malloc(
+ 128, mNumMemRecords * sizeof(iv_mem_rec_t));
+ if (mMemRecords == NULL) {
+ ALOGE("Allocation failure");
+ return NO_MEMORY;
+ }
+
+ {
+ size_t i;
+ ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+ ivdext_fill_mem_rec_op_t s_fill_mem_op;
+ iv_mem_rec_t *ps_mem_rec;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_ip_t);
+ s_fill_mem_ip.i4_level = i4_level;
+ s_fill_mem_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+ s_fill_mem_ip.u4_num_ref_frames = u4_num_ref_frames;
+ s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf;
+ s_fill_mem_ip.u4_num_extra_disp_buf = 0;
+ s_fill_mem_ip.e_output_format = mIvColorFormat;
+
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = mWidth;
+ s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = mHeight;
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size =
+ sizeof(ivdext_fill_mem_rec_op_t);
+
+ ps_mem_rec = mMemRecords;
+ for (i = 0; i < mNumMemRecords; i++)
+ ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_fill_mem_ip,
+ (void *)&s_fill_mem_op);
+
+ if (IV_SUCCESS != status) {
+ ALOGE("Error in filling mem records: 0x%x",
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ mNumMemRecords =
+ s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled;
+
+ ps_mem_rec = mMemRecords;
+
+ for (i = 0; i < mNumMemRecords; i++) {
+ ps_mem_rec->pv_base = ivd_aligned_malloc(
+ ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+ if (ps_mem_rec->pv_base == NULL) {
+ ALOGE("Allocation failure for memory record #%zu of size %u",
+ i, ps_mem_rec->u4_mem_size);
+ status = IV_FAIL;
+ return NO_MEMORY;
+ }
+
+ ps_mem_rec++;
+ }
+ }
+
+ /* Initialize the decoder */
+ {
+ ivdext_init_ip_t s_init_ip;
+ ivdext_init_op_t s_init_op;
+
+ void *dec_fxns = (void *)ivdec_api_function;
+
+ s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+ s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+ s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = mWidth;
+ s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
+
+ s_init_ip.i4_level = i4_level;
+ s_init_ip.u4_num_reorder_frames = u4_num_reorder_frames;
+ s_init_ip.u4_num_ref_frames = u4_num_ref_frames;
+ s_init_ip.u4_share_disp_buf = u4_share_disp_buf;
+ s_init_ip.u4_num_extra_disp_buf = 0;
+
+ s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op);
+
+ s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+ s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat;
+
+ mCodecCtx = (iv_obj_t*)mMemRecords[0].pv_base;
+ mCodecCtx->pv_fxns = dec_fxns;
+ mCodecCtx->u4_size = sizeof(iv_obj_t);
+
+ ALOGD("Initializing decoder");
+ status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip,
+ (void *)&s_init_op);
+ if (status != IV_SUCCESS) {
+ ALOGE("Error in init: 0x%x",
+ s_init_op.s_ivd_init_op_t.u4_error_code);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ /* Reset the plugin state */
+ resetPlugin();
+
+ /* Set the run time (dynamic) parameters */
+ setParams(0, IVD_DECODE_FRAME);
+
+ /* Set number of cores/threads to be used by the codec */
+ setNumCores();
+
+ /* Get codec version */
+ getVersion();
+
+ /* Allocate internal picture buffer */
+ mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, mStride * mHeight * 3 / 2);
+ if (NULL == mFlushOutBuffer) {
+ ALOGE("Could not allocate flushOutputBuffer of size %zu", mStride * mHeight * 3 / 2);
+ return NO_MEMORY;
+ }
+
+ return OK;
+}
+
+status_t SoftHEVC::deInitDecoder() {
+ size_t i;
+ iv_mem_rec_t *ps_mem_rec;
+ ps_mem_rec = mMemRecords;
+ ALOGD("Freeing codec memory");
+ for (i = 0; i < mNumMemRecords; i++) {
+ ivd_aligned_free(ps_mem_rec->pv_base);
+ ps_mem_rec++;
+ }
+
+ ivd_aligned_free(mMemRecords);
+ ivd_aligned_free(mFlushOutBuffer);
+ return OK;
+}
+
+void SoftHEVC::onReset() {
+ ALOGD("onReset called");
+ SoftVideoDecoderOMXComponent::onReset();
+
+ resetDecoder();
+ resetPlugin();
+}
+
+void SoftHEVC::onPortFlushCompleted(OMX_U32 portIndex) {
+ ALOGD("onPortFlushCompleted on port %d", portIndex);
+
+ /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
+ if (kOutputPortIndex == portIndex) {
+ setFlushMode();
+
+ /* Reset the time stamp arrays */
+ memset(mTimeStamps, 0, sizeof(mTimeStamps));
+ memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
+
+ while (true) {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ IV_API_CALL_STATUS_T status;
+ size_t sizeY, sizeUV;
+
+ s_dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE;
+
+ s_dec_ip.u4_ts = 0;
+ s_dec_ip.pv_stream_buffer = NULL;
+ s_dec_ip.u4_num_Bytes = 0;
+
+ s_dec_ip.u4_size = sizeof(ivd_video_decode_ip_t);
+ s_dec_op.u4_size = sizeof(ivd_video_decode_op_t);
+
+ sizeY = mStride * mHeight;
+ sizeUV = sizeY / 4;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+ s_dec_ip.s_out_buffer.pu1_bufs[0] = mFlushOutBuffer;
+ s_dec_ip.s_out_buffer.pu1_bufs[1] =
+ s_dec_ip.s_out_buffer.pu1_bufs[0] + sizeY;
+ s_dec_ip.s_out_buffer.pu1_bufs[2] =
+ s_dec_ip.s_out_buffer.pu1_bufs[1] + sizeUV;
+ s_dec_ip.s_out_buffer.u4_num_bufs = 3;
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip,
+ (void *)&s_dec_op);
+ if (0 == s_dec_op.u4_output_present) {
+ resetPlugin();
+ break;
+ }
+ }
+ }
+}
+
+void SoftHEVC::onQueueFilled(OMX_U32 portIndex) {
+ IV_API_CALL_STATUS_T status;
+
+ UNUSED(portIndex);
+
+ if (mOutputPortSettingsChange != NONE) {
+ return;
+ }
+
+ List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex);
+ List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
+
+ /* If input EOS is seen and decoder is not in flush mode,
+ * set the decoder in flush mode.
+ * There can be a case where EOS is sent along with last picture data
+ * In that case, only after decoding that input data, decoder has to be
+ * put in flush. This case is handled here */
+
+ if (mReceivedEOS && !mIsInFlush) {
+ setFlushMode();
+ }
+
+ while (outQueue.size() == kNumBuffers) {
+ BufferInfo *inInfo;
+ OMX_BUFFERHEADERTYPE *inHeader;
+
+ BufferInfo *outInfo;
+ OMX_BUFFERHEADERTYPE *outHeader;
+ size_t timeStampIx;
+
+ inInfo = NULL;
+ inHeader = NULL;
+
+ if (!mIsInFlush) {
+ if (!inQueue.empty()) {
+ inInfo = *inQueue.begin();
+ inHeader = inInfo->mHeader;
+ } else {
+ break;
+ }
+ }
+
+ outInfo = *outQueue.begin();
+ outHeader = outInfo->mHeader;
+ outHeader->nFlags = 0;
+ outHeader->nTimeStamp = 0;
+ outHeader->nOffset = 0;
+
+ if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) {
+ ALOGD("EOS seen on input");
+ mReceivedEOS = true;
+ if (inHeader->nFilledLen == 0) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ setFlushMode();
+ }
+ }
+
+ /* Get a free slot in timestamp array to hold input timestamp */
+ {
+ size_t i;
+ timeStampIx = 0;
+ for (i = 0; i < MAX_TIME_STAMPS; i++) {
+ if (!mTimeStampsValid[i]) {
+ timeStampIx = i;
+ break;
+ }
+ }
+ if (inHeader != NULL) {
+ mTimeStampsValid[timeStampIx] = true;
+ mTimeStamps[timeStampIx] = inHeader->nTimeStamp;
+ }
+ }
+
+ {
+ ivd_video_decode_ip_t s_dec_ip;
+ ivd_video_decode_op_t s_dec_op;
+ WORD32 timeDelay, timeTaken;
+ size_t sizeY, sizeUV;
+
+ s_dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE;
+
+ /* When in flush and after EOS with zero byte input,
+ * inHeader is set to zero. Hence check for non-null */
+ if (inHeader != NULL) {
+ s_dec_ip.u4_ts = timeStampIx;
+ s_dec_ip.pv_stream_buffer = inHeader->pBuffer
+ + inHeader->nOffset;
+ s_dec_ip.u4_num_Bytes = inHeader->nFilledLen;
+ } else {
+ s_dec_ip.u4_ts = 0;
+ s_dec_ip.pv_stream_buffer = NULL;
+ s_dec_ip.u4_num_Bytes = 0;
+ }
+
+ s_dec_ip.u4_size = sizeof(ivd_video_decode_ip_t);
+ s_dec_op.u4_size = sizeof(ivd_video_decode_op_t);
+
+ sizeY = mStride * mHeight;
+ sizeUV = sizeY / 4;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[0] = sizeY;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[1] = sizeUV;
+ s_dec_ip.s_out_buffer.u4_min_out_buf_size[2] = sizeUV;
+
+ s_dec_ip.s_out_buffer.pu1_bufs[0] = outHeader->pBuffer;
+ s_dec_ip.s_out_buffer.pu1_bufs[1] =
+ s_dec_ip.s_out_buffer.pu1_bufs[0] + sizeY;
+ s_dec_ip.s_out_buffer.pu1_bufs[2] =
+ s_dec_ip.s_out_buffer.pu1_bufs[1] + sizeUV;
+ s_dec_ip.s_out_buffer.u4_num_bufs = 3;
+
+ GETTIME(&mTimeStart, NULL);
+ /* Compute time elapsed between end of previous decode()
+ * to start of current decode() */
+ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+ status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip,
+ (void *)&s_dec_op);
+
+ GETTIME(&mTimeEnd, NULL);
+ /* Compute time taken for decode() */
+ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+ ALOGD("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+ s_dec_op.u4_num_bytes_consumed);
+
+ if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) {
+ /* If the input did not contain picture data, then ignore
+ * the associated timestamp */
+ mTimeStampsValid[timeStampIx] = false;
+ }
+
+ /* If valid height and width are decoded,
+ * then look at change in resolution */
+ if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
+ uint32_t width = s_dec_op.u4_pic_wd;
+ uint32_t height = s_dec_op.u4_pic_ht;
+
+ if ((width != mWidth || height != mHeight)) {
+ mWidth = width;
+ mHeight = height;
+ mStride = mWidth;
+
+ /* If width and height are greater than the
+ * the dimensions used during codec create, then
+ * delete the current instance and recreate an instance with
+ * new dimensions */
+ /* TODO: The following does not work currently, since the decoder
+ * currently returns 0 x 0 as width height when it is not supported
+ * Once the decoder is updated to return actual width and height,
+ * then this can be validated*/
+
+ if ((mWidth * mHeight) > (mInitWidth * mInitHeight)) {
+ status_t ret;
+ ALOGD("Trying reInit");
+ ret = deInitDecoder();
+ if (OK != ret) {
+ // TODO: Handle graceful exit
+ ALOGE("Create failure");
+ return;
+ }
+
+ mInitWidth = mWidth;
+ mInitHeight = mHeight;
+
+ ret = initDecoder();
+ if (OK != ret) {
+ // TODO: Handle graceful exit
+ ALOGE("Create failure");
+ return;
+ }
+ }
+ updatePortDefinitions();
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ return;
+ }
+ }
+
+ if (s_dec_op.u4_output_present) {
+ outHeader->nFilledLen = (mStride * mHeight * 3) / 2;
+
+ outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts];
+ mTimeStampsValid[s_dec_op.u4_ts] = false;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ } else {
+ /* If in flush mode and no output is returned by the codec,
+ * then come out of flush mode */
+ mIsInFlush = false;
+
+ /* If EOS was recieved on input port and there is no output
+ * from the codec, then signal EOS on output port */
+ if (mReceivedEOS) {
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ resetPlugin();
+ }
+ }
+ }
+
+ // TODO: Handle more than one picture data
+ if (inHeader != NULL) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ }
+}
+
+} // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(const char *name,
+ const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
+ OMX_COMPONENTTYPE **component) {
+ return new android::SoftHEVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.h b/media/libstagefright/codecs/hevcdec/SoftHEVC.h
new file mode 100644
index 0000000..20db0e1
--- /dev/null
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 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 SOFT_HEVC_H_
+
+#define SOFT_HEVC_H_
+
+#include "SoftVideoDecoderOMXComponent.h"
+#include <sys/time.h>
+
+namespace android {
+
+#define ivd_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ivd_aligned_free(buf) free(buf)
+
+/** Number of entries in the time-stamp array */
+#define MAX_TIME_STAMPS 64
+
+/** Maximum number of cores supported by the codec */
+#define CODEC_MAX_NUM_CORES 4
+
+#define CODEC_MAX_WIDTH 1920
+
+#define CODEC_MAX_HEIGHT 1088
+
+/** Input buffer size */
+#define INPUT_BUF_SIZE (1024 * 1024)
+
+#define MIN(a, b) ((a) < (b)) ? (a) : (b)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+ diff = ((end.tv_sec - start.tv_sec) * 1000000) + \
+ (end.tv_usec - start.tv_usec);
+
+struct SoftHEVC: public SoftVideoDecoderOMXComponent {
+ SoftHEVC(const char *name, const OMX_CALLBACKTYPE *callbacks,
+ OMX_PTR appData, OMX_COMPONENTTYPE **component);
+
+protected:
+ virtual ~SoftHEVC();
+
+ virtual void onQueueFilled(OMX_U32 portIndex);
+ virtual void onPortFlushCompleted(OMX_U32 portIndex);
+ virtual void onReset();
+private:
+ // Number of input and output buffers
+ enum {
+ kNumBuffers = 8
+ };
+
+ iv_obj_t *mCodecCtx; // Codec context
+ iv_mem_rec_t *mMemRecords; // Memory records requested by the codec
+ size_t mNumMemRecords; // Number of memory records requested by the codec
+
+ uint32_t mNewWidth; // New width after change in resolution
+ uint32_t mNewHeight; // New height after change in resolution
+ uint32_t mInitWidth; // Width used during codec creation
+ uint32_t mInitHeight; // Height used during codec creation
+ size_t mStride; // Stride to be used for display buffers
+
+ size_t mNumCores; // Number of cores to be uesd by the codec
+
+ struct timeval mTimeStart; // Time at the start of decode()
+ struct timeval mTimeEnd; // Time at the end of decode()
+
+ // Internal buffer to be used to flush out the buffers from decoder
+ uint8_t *mFlushOutBuffer;
+
+ // Status of entries in the timestamp array
+ bool mTimeStampsValid[MAX_TIME_STAMPS];
+
+ // Timestamp array - Since codec does not take 64 bit timestamps,
+ // they are maintained in the plugin
+ OMX_S64 mTimeStamps[MAX_TIME_STAMPS];
+
+ OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format
+ IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format
+
+ bool mIsInFlush; // codec is flush mode
+ bool mReceivedEOS; // EOS is receieved on input port
+ bool mIsAdapting; // plugin in middle of change in resolution
+
+ status_t initDecoder();
+ status_t deInitDecoder();
+ status_t setFlushMode();
+ status_t setParams(WORD32 stride, IVD_VIDEO_DECODE_MODE_T decMode);
+ status_t getVersion();
+ status_t setNumCores();
+ status_t resetDecoder();
+ status_t resetPlugin();
+
+ DISALLOW_EVIL_CONSTRUCTORS (SoftHEVC);
+};
+
+} // namespace android
+
+#endif // SOFT_HEVC_H_
diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s
deleted file mode 100644
index 3a6dd4f..0000000
--- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s
+++ /dev/null
@@ -1,210 +0,0 @@
-; ------------------------------------------------------------------
-; Copyright (C) 1998-2009 PacketVideo
-;
-; 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.
-; -------------------------------------------------------------------
-
-;
-;
-; Filename: pvmp3_dct_9.s
-;
-;------------------------------------------------------------------------------
-; REVISION HISTORY
-;
-;
-; Who: Date: MM/DD/YYYY
-; Description:
-;
-;------------------------------------------------------------------------------
-
- AREA |.drectve|, DRECTVE
-
- DCB "-defaultlib:coredll.lib "
- DCB "-defaultlib:corelibc.lib "
-
- IMPORT pvmp3_mdct_18 ; pvmp3_mdct_18.cpp
-
-;------------------------------------------------------------------------------
-
- AREA |.rdata|, DATA, READONLY
- % 4
-
-
-;------------------------------------------------------------------------------
-
- AREA |.text|, CODE, READONLY
-
-
-;------------------------------------------------------------------------------
-
- EXPORT |pvmp3_dct_9|
-
-|pvmp3_dct_9| PROC
- stmfd sp!,{r4-r10,lr}
- ldr r2, [r0, #0x20]
- ldr r3, [r0]
- ldr r12,[r0, #4]
- add r1,r2,r3
- sub lr,r2,r3
- ldr r3,[r0, #0x1c]
- ldr r4,[r0, #0x18]
- add r2,r3,r12
- ldr r5,[r0,#8]
- sub r3,r3,r12
- add r12,r4,r5
- sub r4,r4,r5
- ldr r5,[r0, #0x14]
- ldr r7,[r0, #0xc]
- ldr r9,[r0, #0x10]
- add r6,r5,r7
- sub r5,r5,r7
- add r7,r1,r12
- add r8,r9,r2
- add r7,r7,r6
- add r10,r7,r8
- rsb r7,r8,r7,asr #1
- str r7,[r0, #0x18]
- rsb r2,r9,r2,asr #1
- str r10,[r0]
- ldr r11,|cos_2pi_9|
- rsb r7,r2,#0
-
- mov r9,r1,lsl #1
- mov r1,r9 ;;;;;; !!!!!!
- mov r8,r7
-
-; vec[4] = fxp_mac32_Q32( vec[4], tmp0<<1, cos_2pi_9);
-
- smlal r1,r8,r11,r9
- ldr r10,|cos_4pi_9|
- ldr r11,|cos_pi_9|
-
-; vec[8] = fxp_mac32_Q32( vec[8], tmp0<<1, cos_4pi_9);
-
- smlal r1,r7,r10,r9
-
-
-
-; vec[2] = fxp_mac32_Q32( vec[2], tmp0<<1, cos_pi_9);
-
- smlal r9,r2,r11,r9
- mov r1,r12,lsl #1
- rsb r9,r10,#0
- ldr r11,|cos_5pi_9|
-
- smlal r12,r2,r9,r1
-
-
-
-; vec[2] = fxp_mac32_Q32( vec[2], tmp2<<1, cos_5pi_9);
-
- ldr r9,|cos_2pi_9|
- mov r12,r1 ;;;;;; !!!!!!
- smlal r12,r8,r11,r1
-
-
-; vec[8] = fxp_mac32_Q32( vec[8], tmp2<<1, cos_2pi_9);
-
- smlal r1,r7,r9,r1
- mov r1,r6,lsl #1
- smlal r12,r7,r11,r1
- and r6,r10,r11,asr #14
- smlal r12,r8,r6,r1
- ldr r10,|cos_11pi_18|
- add r12,r11,r6
- smlal r1,r2,r12,r1
- ldr r9,|cos_8pi_9|
- str r2,[r0,#8]
- mov r1,r5,lsl #1
-
-; vec[8] = fxp_mac32_Q32( vec[8], tmp3<<1, cos_8pi_9);
-
- smull r2,r6,r9,r1
- str r7,[r0,#0x20]
- mov r2,r4,lsl #1
- ldr r7,|cos_13pi_18|
- smlal r12,r6,r10,r2
-
- mov r3,r3,lsl #1
-
-; vec[5] = fxp_mac32_Q32( vec[5], tmp8<<1, cos_13pi_18);
-
- smlal r12,r6,r7,r3
- add r4,r5,r4
- mov r12,lr,lsl #1
- sub lr,r4,lr
- ldr r7,|cos_17pi_18|
- str r8,[r0, #0x10]
- ldr r4,|cos_pi_6|
-
- mov lr,lr,lsl #1
-
-; vec[1] = fxp_mac32_Q32( vec[1], tmp8<<1, cos_17pi_18);
-
- smlal r8,r6,r7,r12
-
-; vec[3] = fxp_mul32_Q32((tmp5 + tmp6 - tmp8)<<1, cos_pi_6);
-
- smull r5,lr,r4,lr
- str r6,[r0, #4]
- str lr,[r0, #0xc]
-
-
-; vec[5] = fxp_mul32_Q32(tmp5<<1, cos_17pi_18);
- smull r5,lr,r7,r1
- rsb r6,r9,#0
-; vec[5] = fxp_mac32_Q32( vec[5], tmp6<<1, cos_7pi_18);
- smlal r5,lr,r6,r2
-; vec[5] = fxp_mac32_Q32( vec[5], tmp7<<1, cos_pi_6);
- smlal r5,lr,r4,r3
-; vec[5] = fxp_mac32_Q32( vec[5], tmp8<<1, cos_13pi_18);
- smlal r5,lr,r10,r12
- str lr,[r0, #0x14]
- rsb lr,r10,#0
-
-; vec[7] = fxp_mul32_Q32(tmp5<<1, cos_5pi_18);
- smull r5,r1,lr,r1
-; vec[7] = fxp_mac32_Q32( vec[7], tmp6<<1, cos_17pi_18);
- smlal r2,r1,r7,r2
-; vec[7] = fxp_mac32_Q32( vec[7], tmp7<<1, cos_pi_6);
- smlal r3,r1,r4,r3
-; vec[7] = fxp_mac32_Q32( vec[7], tmp8<<1, cos_11pi_18);
- smlal r12,r1,r9,r12
- str r1,[r0, #0x1c]
- ldmfd sp!,{r4-r10,pc}
-|cos_2pi_9|
- DCD 0x620dbe80
-|cos_4pi_9|
- DCD 0x163a1a80
-|cos_pi_9|
- DCD 0x7847d900
-|cos_5pi_9|
- DCD 0x87b82700
-|cos_8pi_9|
- DCD 0xd438af00
-|cos_11pi_18|
- DCD 0xadb92280
-|cos_13pi_18|
- DCD 0x91261480
-|cos_17pi_18|
- DCD 0x81f1d200
-|cos_pi_6|
- DCD 0x6ed9eb80
- ENDP
-
-
-
-
-
- END
diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s
deleted file mode 100644
index 9401d8c..0000000
--- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s
+++ /dev/null
@@ -1,369 +0,0 @@
-; ------------------------------------------------------------------
-; Copyright (C) 1998-2009 PacketVideo
-;
-; 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.
-; -------------------------------------------------------------------
-
-;
-;
-; Filename: pvmp3_dct_18.s
-;
-;------------------------------------------------------------------------------
-; REVISION HISTORY
-;
-;
-; Who: Date: MM/DD/YYYY
-; Description:
-;
-;------------------------------------------------------------------------------
-
- EXPORT pvmp3_mdct_18
-
- IMPORT ||Lib$$Request$$armlib|| [WEAK]
- IMPORT ||Lib$$Request$$cpplib|| [WEAK]
- IMPORT pvmp3_dct_9
-
-
-;------------------------------------------------------------------------------
-
- AREA |.text|, CODE, READONLY, ALIGN=2
-
-
-;------------------------------------------------------------------------------
-
-|pvmp3_mdct_18| PROC
- stmfd sp!,{r4-r10,lr}
- mov r7,r2
- ldr r2,table
- mov r6,r1
- add r3,r2,#0x24
- add r12,r3,#0x44
- add r1,r0,#0x44
- mov r5,r0
-
-; for ( i=9; i!=0; i--)
-; {
-
- mov r4,#9
-Loop_1
-
-; tmp = *(pt_vec);
-; tmp1 = *(pt_vec_o);
-
- ldr lr,[r0] ;; tmp == lr
- ldr r8,[r3],#4 ;; tmp1 == r8
-
-; tmp = fxp_mul32_Q32( tmp<<1, *(pt_cos++ ));
-; tmp1 = fxp_mul32_Q27( tmp1, *(pt_cos_x--));
-
- mov lr,lr,lsl #1
- smull r10,lr,r8,lr
- ldr r8,[r12],#-4
- ldr r9,[r1]
- subs r4,r4,#1
- smull r9,r10,r8,r9
- mov r8,r9,lsr #27
- add r8,r8,r10,lsl #5
-
-; *(pt_vec++) = tmp + tmp1 ;
-; *(pt_vec_o--) = fxp_mul32_Q28( (tmp - tmp1), *(pt_cos_split++));
-
- add r9,lr,r8
- sub r8,lr,r8
- ldr lr,[r2],#4
- str r9,[r0],#4
- smull r8,r9,lr,r8
- mov lr,r8,lsr #28
- add lr,lr,r9,lsl #4
- str lr,[r1],#-4
- bne Loop_1
-
-; }
-
- mov r0,r5 ;; r0 = vec
- bl pvmp3_dct_9
- add r0,r5,#0x24 ;; r0 = &vec[9]
- bl pvmp3_dct_9
-
- ldr r0,[r5,#0x20]
- ldr r2,[r5,#0x40]
- str r0,[r5,#0x40]
- ldr r0,[r5,#0x1c]
- ldr r3,[r5,#0x38]
- str r0,[r5,#0x38]
- ldr r1,[r5,#0x18]
- ldr r0,[r5,#0x30]
- str r1,[r5,#0x30]
- ldr r12,[r5,#0x14]
- ldr r1,[r5,#0x28]
- str r12,[r5,#0x28]
- ldr r12,[r5,#0x10]
- str r12,[r5,#0x20]
- ldr r12,[r5,#0xc]
- str r12,[r5,#0x18]
- ldr r12,[r5,#8]
- str r12,[r5,#0x10]
- ldr r12,[r5,#4]
- str r12,[r5,#8]
- ldr r12,[r5,#0x24]
- sub r12,r12,r1
- str r12,[r5,#4]
- ldr r12,[r5,#0x2c]
- sub r1,r12,r1
- str r1,[r5,#0xc]
- sub r1,r12,r0
- str r1,[r5,#0x14]
- ldr r1,[r5,#0x34]
- sub r0,r1,r0
- str r0,[r5,#0x1c]
- sub r0,r1,r3
- str r0,[r5,#0x24]
- ldr r1,[r5,#0x3c]
- sub r3,r1,r3
- sub r1,r1,r2
- str r1,[r5,#0x34]
- str r3,[r5,#0x2c]
- ldr r1,[r5,#0x44]
- sub r1,r1,r2
- str r1,[r5,#0x3c]
- ldr r12,[r5,#0]
-
-Loop_2
- add r1,r5,r4,lsl #2
- ldr r2,[r1,#0x28]
- ldr r3,[r6,r4,lsl #2]
- add r0,r0,r2
- str r0,[r1,#0x28]
- ldr lr,[r7,r4,lsl #2]
- ldr r1,[r1,#4]
- smlal r0,r3,lr,r0
- mov r0,r2
- add r2,r12,r1
- rsb r2,r2,#0
- str r3,[r5,r4,lsl #2]
- str r2,[r6,r4,lsl #2]
- add r4,r4,#1
- cmp r4,#6
- mov r12,r1
-
- blt Loop_2
-
- ldr r1,[r5,#0x40]
- ldr r2,[r6,#0x18]
- add r3,r0,r1
- str r3,[r5,#0x40]
- ldr lr,[r7,r4,lsl #2]
- mov r3,r3,lsl #1
- ldr r0,[r5,#0x1c]
- smlal r3,r2,lr,r3
- add r3,r12,r0
- str r2,[r5,#0x18]
- ldr r2,[r6,#0x1c]
- rsb r3,r3,#0
- str r3,[r6,#0x18]
- ldr r3,[r5,#0x20]
- add r0,r3,r0
- rsb r0,r0,#0
- str r0,[r6,#0x1c]
- ldr r3,[r5,#0x44]
- ldr r0,[r6,#0x20]
- add r3,r3,r1
- mov r1,r2
- ldr r10,[r7,#0x1c]
- mov r2,r3,lsl #1
- smlal r12,r1,r10,r2
- str r1,[r5,#0x1c]
- ldr r1,[r5,#0x20]
- ldr r3,[r5,#0x24]
- add r1,r1,r3
- rsb r1,r1,#0
- str r1,[r6,#0x20]
- ldr r1,[r5,#0x44]
- ldr r3,[r7,#0x20]
- mov r1,r1,lsl #1
- smlal r12,r0,r3,r1
- ldr lr,[r7,#0x24]
- ldr r3,[r6,#0x24]
- str r0,[r5,#0x20]
- smlal r1,r3,lr,r1
- ldr r0,[r6,#0x40]
- ldr r12,[r6,#0x44]
- str r3,[r5,#0x24]
- ldr r1,[r5,#0x28]
- ldr r3,[r7,#0x44]
- mov r1,r1,lsl #1
- smlal r1,r12,r3,r1
- ldr r1,[r5,#0x40]
- str r12,[r5,#0x44]
- rsb r8,r1,#0
- str r8,[r5,#0x28]
- ldr r1,[r5,#0x2c]
- ldr r3,[r7,#0x40]
- mov r1,r1,lsl #1
- smlal r1,r0,r3,r1
- str r0,[r5,#0x40]
- ldr r0,[r5,#0x3c]
- ldr r1,[r6,#0x38]
- ldr r3,[r6,#0x3c]
- rsb r9,r0,#0
- str r9,[r5,#0x2c]
- ldr r0,[r5,#0x30]
- ldr r12,[r7,#0x3c]
- mov r0,r0,lsl #1
- smlal r0,r3,r12,r0
- str r3,[r5,#0x3c]
- ldr r0,[r5,#0x38]
- rsb r0,r0,#0
- str r0,[r5,#0x30]
- ldr r3,[r5,#0x34]
- ldr r12,[r7,#0x38]
- mov r3,r3,lsl #1
- smlal r3,r1,r12,r3
- mov r0,r0,lsl #1
- str r1,[r5,#0x38]
- ldr r4,[r7,#0x34]
- ldr r1,[r6,#0x34]
- ldr r3,[r6,#0x30]
- smlal r0,r1,r4,r0
- ldr r12,[r6,#0x2c]
- ldr lr,[r6,#0x28]
- str r1,[r5,#0x34]
- ldr r1,[r7,#0x30]
- mov r0,r9,lsl #1
- smlal r0,r3,r1,r0
- mov r0,r8,lsl #1
- ldr r1,[r7,#0x2c]
- str r3,[r5,#0x30]
- smlal r0,r12,r1,r0
- ldr r0,[r7,#0x28]
- str r12,[r5,#0x2c]
- smlal r2,lr,r0,r2
- str lr,[r5,#0x28]
- ldr r1,[r6,#4]
- ldr r12,[r7,#0x48]
- mov r2,r1,lsl #1
- ldr r1,[r6,#0x20]
- ldr r0,[r6]
- mov r1,r1,lsl #1
- smull r4,lr,r12,r1
- ldr r3,[r6,#0x1c]
- str lr,[r6]
- ldr r12,[r7,#0x4c]
- mov r3,r3,lsl #1
- smull r4,lr,r12,r3
- mov r0,r0,lsl #1
- ldr r12,[r7,#0x64]
- str lr,[r6,#4]
- smull r4,lr,r12,r2
- ldr r12,[r7,#0x68]
- str lr,[r6,#0x1c]
- smull r4,lr,r12,r0
- ldr r12,[r7,#0x6c]
- str lr,[r6,#0x20]
- smull lr,r0,r12,r0
- ldr r12,[r7,#0x70]
- str r0,[r6,#0x24]
- smull r0,r2,r12,r2
- ldr r0,[r7,#0x88]
- str r2,[r6,#0x28]
- smull r3,r2,r0,r3
- ldr r0,[r7,#0x8c]
- str r2,[r6,#0x40]
- smull r2,r1,r0,r1
- str r1,[r6,#0x44]
- ldr r0,[r6,#0x18]
- ldr lr,[r7,#0x50]
- mov r1,r0,lsl #1
- ldr r0,[r6,#0x14]
- smull r5,r4,lr,r1
- ldr r12,[r6,#0x10]
- mov r3,r0,lsl #1
- ldr r0,[r6,#0xc]
- mov r12,r12,lsl #1
- mov r2,r0,lsl #1
- ldr r0,[r6,#8]
- str r4,[r6,#8]
- ldr lr,[r7,#0x54]
- mov r0,r0,lsl #1
- smull r5,r4,lr,r3
- ldr lr,[r7,#0x58]
- str r4,[r6,#0xc]
- smull r5,r4,lr,r12
- ldr lr,[r7,#0x5c]
- str r4,[r6,#0x10]
- smull r5,r4,lr,r2
- ldr lr,[r7,#0x60]
- str r4,[r6,#0x14]
- smull r5,r4,lr,r0
- ldr lr,[r7,#0x74]
- str r4,[r6,#0x18]
- smull r4,r0,lr,r0
- ldr lr,[r7,#0x78]
- str r0,[r6,#0x2c]
- smull r0,r2,lr,r2
- ldr r0,[r7,#0x7c]
- str r2,[r6,#0x30]
- smull r12,r2,r0,r12
- ldr r0,[r7,#0x80]
- str r2,[r6,#0x34]
- smull r3,r2,r0,r3
- ldr r0,[r7,#0x84]
- str r2,[r6,#0x38]
- smull r2,r1,r0,r1
- str r1,[r6,#0x3c]
- ldmfd sp!,{r4-r10,pc}
-table
- DCD ||.constdata$1||
- ENDP
-
-;------------------------------------------------------------------------------
-
- AREA |.constdata|, DATA, READONLY, ALIGN=2
-
-;------------------------------------------------------------------------------
-
-||.constdata$1||
-cosTerms_dct18
- DCD 0x0807d2b0
- DCD 0x08483ee0
- DCD 0x08d3b7d0
- DCD 0x09c42570
- DCD 0x0b504f30
- DCD 0x0df29440
- DCD 0x12edfb20
- DCD 0x1ee8dd40
- DCD 0x5bca2a00
-cosTerms_1_ov_cos_phi
- DCD 0x400f9c00
- DCD 0x408d6080
- DCD 0x418dcb80
- DCD 0x431b1a00
- DCD 0x4545ea00
- DCD 0x48270680
- DCD 0x4be25480
- DCD 0x50ab9480
- DCD 0x56ce4d80
- DCD 0x05ebb630
- DCD 0x06921a98
- DCD 0x0771d3a8
- DCD 0x08a9a830
- DCD 0x0a73d750
- DCD 0x0d4d5260
- DCD 0x127b1ca0
- DCD 0x1ea52b40
- DCD 0x5bb3cc80
-
-
-
- END
diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm
deleted file mode 100644
index 5be75d4..0000000
--- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm
+++ /dev/null
@@ -1,366 +0,0 @@
-; ------------------------------------------------------------------
-; Copyright (C) 1998-2009 PacketVideo
-;
-; 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.
-; -------------------------------------------------------------------
-
-;
-;
-; Filename: pvmp3_dct_18.s
-;
-;------------------------------------------------------------------------------
-; REVISION HISTORY
-;
-;
-; Who: Date: MM/DD/YYYY
-; Description:
-;
-;------------------------------------------------------------------------------
-
- EXPORT |pvmp3_mdct_18|
-
- IMPORT pvmp3_dct_9
-
-
-;------------------------------------------------------------------------------
-
- AREA |.text|, CODE, READONLY, ALIGN=2
-
-
-;------------------------------------------------------------------------------
-
-|pvmp3_mdct_18| PROC
- stmfd sp!,{r4-r10,lr}
- mov r7,r2
- ldr r2,table
- mov r6,r1
- add r3,r2,#0x24
- add r12,r3,#0x44
- add r1,r0,#0x44
- mov r5,r0
-
-; for ( i=9; i!=0; i--)
-; {
-
- mov r4,#9
-Loop_1
-
-; tmp = *(pt_vec);
-; tmp1 = *(pt_vec_o);
-
- ldr lr,[r0] ;; tmp == lr
- ldr r8,[r3],#4 ;; tmp1 == r8
-
-; tmp = fxp_mul32_Q32( tmp<<1, *(pt_cos++ ));
-; tmp1 = fxp_mul32_Q27( tmp1, *(pt_cos_x--));
-
- mov lr,lr,lsl #1
- smull r10,lr,r8,lr
- ldr r8,[r12],#-4
- ldr r9,[r1]
- subs r4,r4,#1
- smull r9,r10,r8,r9
- mov r8,r9,lsr #27
- add r8,r8,r10,lsl #5
-
-; *(pt_vec++) = tmp + tmp1 ;
-; *(pt_vec_o--) = fxp_mul32_Q28( (tmp - tmp1), *(pt_cos_split++));
-
- add r9,lr,r8
- sub r8,lr,r8
- ldr lr,[r2],#4
- str r9,[r0],#4
- smull r8,r9,lr,r8
- mov lr,r8,lsr #28
- add lr,lr,r9,lsl #4
- str lr,[r1],#-4
- bne Loop_1
-
-; }
-
- mov r0,r5 ;; r0 = vec
- bl pvmp3_dct_9
- add r0,r5,#0x24 ;; r0 = &vec[9]
- bl pvmp3_dct_9
-
- ldr r0,[r5,#0x20]
- ldr r2,[r5,#0x40]
- str r0,[r5,#0x40]
- ldr r0,[r5,#0x1c]
- ldr r3,[r5,#0x38]
- str r0,[r5,#0x38]
- ldr r1,[r5,#0x18]
- ldr r0,[r5,#0x30]
- str r1,[r5,#0x30]
- ldr r12,[r5,#0x14]
- ldr r1,[r5,#0x28]
- str r12,[r5,#0x28]
- ldr r12,[r5,#0x10]
- str r12,[r5,#0x20]
- ldr r12,[r5,#0xc]
- str r12,[r5,#0x18]
- ldr r12,[r5,#8]
- str r12,[r5,#0x10]
- ldr r12,[r5,#4]
- str r12,[r5,#8]
- ldr r12,[r5,#0x24]
- sub r12,r12,r1
- str r12,[r5,#4]
- ldr r12,[r5,#0x2c]
- sub r1,r12,r1
- str r1,[r5,#0xc]
- sub r1,r12,r0
- str r1,[r5,#0x14]
- ldr r1,[r5,#0x34]
- sub r0,r1,r0
- str r0,[r5,#0x1c]
- sub r0,r1,r3
- str r0,[r5,#0x24]
- ldr r1,[r5,#0x3c]
- sub r3,r1,r3
- sub r1,r1,r2
- str r1,[r5,#0x34]
- str r3,[r5,#0x2c]
- ldr r1,[r5,#0x44]
- sub r1,r1,r2
- str r1,[r5,#0x3c]
- ldr r12,[r5,#0]
-
-Loop_2
- add r1,r5,r4,lsl #2
- ldr r2,[r1,#0x28]
- ldr r3,[r6,r4,lsl #2]
- add r0,r0,r2
- str r0,[r1,#0x28]
- ldr lr,[r7,r4,lsl #2]
- ldr r1,[r1,#4]
- smlal r0,r3,lr,r0
- mov r0,r2
- add r2,r12,r1
- rsb r2,r2,#0
- str r3,[r5,r4,lsl #2]
- str r2,[r6,r4,lsl #2]
- add r4,r4,#1
- cmp r4,#6
- mov r12,r1
-
- blt Loop_2
-
- ldr r1,[r5,#0x40]
- ldr r2,[r6,#0x18]
- add r3,r0,r1
- str r3,[r5,#0x40]
- ldr lr,[r7,r4,lsl #2]
- mov r3,r3,lsl #1
- ldr r0,[r5,#0x1c]
- smlal r3,r2,lr,r3
- add r3,r12,r0
- str r2,[r5,#0x18]
- ldr r2,[r6,#0x1c]
- rsb r3,r3,#0
- str r3,[r6,#0x18]
- ldr r3,[r5,#0x20]
- add r0,r3,r0
- rsb r0,r0,#0
- str r0,[r6,#0x1c]
- ldr r3,[r5,#0x44]
- ldr r0,[r6,#0x20]
- add r3,r3,r1
- mov r1,r2
- ldr r10,[r7,#0x1c]
- mov r2,r3,lsl #1
- smlal r12,r1,r10,r2
- str r1,[r5,#0x1c]
- ldr r1,[r5,#0x20]
- ldr r3,[r5,#0x24]
- add r1,r1,r3
- rsb r1,r1,#0
- str r1,[r6,#0x20]
- ldr r1,[r5,#0x44]
- ldr r3,[r7,#0x20]
- mov r1,r1,lsl #1
- smlal r12,r0,r3,r1
- ldr lr,[r7,#0x24]
- ldr r3,[r6,#0x24]
- str r0,[r5,#0x20]
- smlal r1,r3,lr,r1
- ldr r0,[r6,#0x40]
- ldr r12,[r6,#0x44]
- str r3,[r5,#0x24]
- ldr r1,[r5,#0x28]
- ldr r3,[r7,#0x44]
- mov r1,r1,lsl #1
- smlal r1,r12,r3,r1
- ldr r1,[r5,#0x40]
- str r12,[r5,#0x44]
- rsb r8,r1,#0
- str r8,[r5,#0x28]
- ldr r1,[r5,#0x2c]
- ldr r3,[r7,#0x40]
- mov r1,r1,lsl #1
- smlal r1,r0,r3,r1
- str r0,[r5,#0x40]
- ldr r0,[r5,#0x3c]
- ldr r1,[r6,#0x38]
- ldr r3,[r6,#0x3c]
- rsb r9,r0,#0
- str r9,[r5,#0x2c]
- ldr r0,[r5,#0x30]
- ldr r12,[r7,#0x3c]
- mov r0,r0,lsl #1
- smlal r0,r3,r12,r0
- str r3,[r5,#0x3c]
- ldr r0,[r5,#0x38]
- rsb r0,r0,#0
- str r0,[r5,#0x30]
- ldr r3,[r5,#0x34]
- ldr r12,[r7,#0x38]
- mov r3,r3,lsl #1
- smlal r3,r1,r12,r3
- mov r0,r0,lsl #1
- str r1,[r5,#0x38]
- ldr r4,[r7,#0x34]
- ldr r1,[r6,#0x34]
- ldr r3,[r6,#0x30]
- smlal r0,r1,r4,r0
- ldr r12,[r6,#0x2c]
- ldr lr,[r6,#0x28]
- str r1,[r5,#0x34]
- ldr r1,[r7,#0x30]
- mov r0,r9,lsl #1
- smlal r0,r3,r1,r0
- mov r0,r8,lsl #1
- ldr r1,[r7,#0x2c]
- str r3,[r5,#0x30]
- smlal r0,r12,r1,r0
- ldr r0,[r7,#0x28]
- str r12,[r5,#0x2c]
- smlal r2,lr,r0,r2
- str lr,[r5,#0x28]
- ldr r1,[r6,#4]
- ldr r12,[r7,#0x48]
- mov r2,r1,lsl #1
- ldr r1,[r6,#0x20]
- ldr r0,[r6]
- mov r1,r1,lsl #1
- smull r4,lr,r12,r1
- ldr r3,[r6,#0x1c]
- str lr,[r6]
- ldr r12,[r7,#0x4c]
- mov r3,r3,lsl #1
- smull r4,lr,r12,r3
- mov r0,r0,lsl #1
- ldr r12,[r7,#0x64]
- str lr,[r6,#4]
- smull r4,lr,r12,r2
- ldr r12,[r7,#0x68]
- str lr,[r6,#0x1c]
- smull r4,lr,r12,r0
- ldr r12,[r7,#0x6c]
- str lr,[r6,#0x20]
- smull lr,r0,r12,r0
- ldr r12,[r7,#0x70]
- str r0,[r6,#0x24]
- smull r0,r2,r12,r2
- ldr r0,[r7,#0x88]
- str r2,[r6,#0x28]
- smull r3,r2,r0,r3
- ldr r0,[r7,#0x8c]
- str r2,[r6,#0x40]
- smull r2,r1,r0,r1
- str r1,[r6,#0x44]
- ldr r0,[r6,#0x18]
- ldr lr,[r7,#0x50]
- mov r1,r0,lsl #1
- ldr r0,[r6,#0x14]
- smull r5,r4,lr,r1
- ldr r12,[r6,#0x10]
- mov r3,r0,lsl #1
- ldr r0,[r6,#0xc]
- mov r12,r12,lsl #1
- mov r2,r0,lsl #1
- ldr r0,[r6,#8]
- str r4,[r6,#8]
- ldr lr,[r7,#0x54]
- mov r0,r0,lsl #1
- smull r5,r4,lr,r3
- ldr lr,[r7,#0x58]
- str r4,[r6,#0xc]
- smull r5,r4,lr,r12
- ldr lr,[r7,#0x5c]
- str r4,[r6,#0x10]
- smull r5,r4,lr,r2
- ldr lr,[r7,#0x60]
- str r4,[r6,#0x14]
- smull r5,r4,lr,r0
- ldr lr,[r7,#0x74]
- str r4,[r6,#0x18]
- smull r4,r0,lr,r0
- ldr lr,[r7,#0x78]
- str r0,[r6,#0x2c]
- smull r0,r2,lr,r2
- ldr r0,[r7,#0x7c]
- str r2,[r6,#0x30]
- smull r12,r2,r0,r12
- ldr r0,[r7,#0x80]
- str r2,[r6,#0x34]
- smull r3,r2,r0,r3
- ldr r0,[r7,#0x84]
- str r2,[r6,#0x38]
- smull r2,r1,r0,r1
- str r1,[r6,#0x3c]
- ldmfd sp!,{r4-r10,pc}
-table
- DCD cosTerms_dct18
- ENDP
-
-;------------------------------------------------------------------------------
-
- AREA |.constdata|, DATA, READONLY, ALIGN=2
-
-;------------------------------------------------------------------------------
-
-cosTerms_dct18
- DCD 0x0807d2b0
- DCD 0x08483ee0
- DCD 0x08d3b7d0
- DCD 0x09c42570
- DCD 0x0b504f30
- DCD 0x0df29440
- DCD 0x12edfb20
- DCD 0x1ee8dd40
- DCD 0x5bca2a00
-cosTerms_1_ov_cos_phi
- DCD 0x400f9c00
- DCD 0x408d6080
- DCD 0x418dcb80
- DCD 0x431b1a00
- DCD 0x4545ea00
- DCD 0x48270680
- DCD 0x4be25480
- DCD 0x50ab9480
- DCD 0x56ce4d80
- DCD 0x05ebb630
- DCD 0x06921a98
- DCD 0x0771d3a8
- DCD 0x08a9a830
- DCD 0x0a73d750
- DCD 0x0d4d5260
- DCD 0x127b1ca0
- DCD 0x1ea52b40
- DCD 0x5bb3cc80
-
-
-
- END
diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s
deleted file mode 100644
index abec599..0000000
--- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s
+++ /dev/null
@@ -1,237 +0,0 @@
-; ------------------------------------------------------------------
-; Copyright (C) 1998-2009 PacketVideo
-;
-; 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.
-; -------------------------------------------------------------------
-
-;
-;
-; Filename: pvmp3_polyphase_filter_window.s
-;
-;------------------------------------------------------------------------------
-; REVISION HISTORY
-;
-;
-; Who: Date: MM/DD/YYYY
-; Description:
-;
-;------------------------------------------------------------------------------
-
- EXPORT pvmp3_polyphase_filter_window
-
- IMPORT ||Lib$$Request$$armlib|| [WEAK]
- IMPORT ||Lib$$Request$$cpplib|| [WEAK]
- IMPORT pqmfSynthWin
-
-
-
-;------------------------------------------------------------------------------
-
- AREA |.text|, CODE, READONLY, ALIGN=2
-
-
-;------------------------------------------------------------------------------
-
-|pvmp3_polyphase_filter_window| PROC
-
- stmfd sp!,{r0-r2,r4-r11,lr}
-
- sub sp,sp,#4
- ldr r2,[sp,#0xc]
- ldr r1,PolyPh_filter_coeff
-
- sub r2,r2,#1
- mov r10,#1
- str r2,[sp]
-
-; Accumulators r9, r11::> Initialization
-
-Loop_j
- mov r9, #0x20
- mov r11, #0x20
- mov r4, #0x10
-Loop_i
- add r2,r4,r10
- add r3,r0,r2,lsl #2
- sub r2,r4,r10
- ldr r5,[r3]
- ldr lr,[r1]
- add r12,r0,r2,lsl #2
- ldr r6,[r12,#0x780]
- smlal r2,r9,lr,r5
- smlal r2,r11,lr,r6
- ldr r2,[r1,#4]
- ldr r7,[r12,#0x80]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- sub r9,r9,r5
- ldr r5,[r1,#8]
- ldr r8,[r3,#0x700]
- add r4,r4,#0x200
- smlal r6,r9,r5,r7
- smull r6,r2,r5,r8
- ldr r5,[r1,#0xc]
- sub r11,r11,r2
- smlal r8,r9,r5,r8
- smlal r7,r11,r5,r7
- ldr r5,[r3,#0x100]
- ldr r2,[r1,#0x10]
- ldr r6,[r12,#0x680]
- smlal lr,r9,r2,r5
- smlal lr,r11,r2,r6
- ldr r2,[r1,#0x14]
- ldr r7,[r12,#0x180]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- ldr r6,[r1,#0x18]
- ldr r8,[r3,#0x600]
- sub r9,r9,r5
- smlal r5,r9,r6,r7
- smull r2,r5,r6,r8
- ldr r6,[r1,#0x1c]
- sub r11,r11,r5
- smlal r8,r9,r6,r8
- ldr r2,[r1,#0x20]
- ldr r5,[r3,#0x200]
- smlal r7,r11,r6,r7
- ldr r6,[r12,#0x580]
- smlal lr,r9,r2,r5
- smlal lr,r11,r2,r6
- ldr r2,[r1,#0x24]
- ldr r7,[r12,#0x280]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- ldr r6,[r1,#0x28]
- ldr r8,[r3,#0x500]
- sub r9,r9,r5
- smlal r5,r9,r6,r7
- smull r2,r5,r6,r8
- ldr r6,[r1,#0x2c]
- sub r11,r11,r5
-
- smlal r8,r9,r6,r8
- smlal r7,r11,r6,r7
- ldr r5,[r3,#0x300]
- ldr r8,[r1,#0x30]
- ldr r6,[r12,#0x480]
- smlal r7,r9,r8,r5
- smlal r7,r11,r8,r6
- ldr r8,[r1,#0x34]
- ldr r12,[r12,#0x380]
- smlal r5,r11,r8,r5
- smull r6,r5,r8,r6
- ldr r6,[r1,#0x38]
-
-
- ldr r3,[r3,#0x400]
- sub r9,r9,r5
- smlal r7,r9,r6,r12
- smull r8,r7,r6,r3
- cmp r4,#0x210
- sub r11,r11,r7
-
- ldr r2,[r1,#0x3c]
- add r1,r1,#0x40
- smlal r3,r9,r2,r3
- smlal r12,r11,r2,r12
-
- blt Loop_i
-
- mov r3,r9, asr #6
- mov r4,r3, asr #15
- teq r4,r3, asr #31
- ldr r12,LOW_16BITS
- ldr r2,[sp]
- eorne r3,r12,r3,asr #31
- ldr r4,[sp,#8]
- mov r2,r10,lsl r2
- add r4,r4,r2,lsl #1
- strh r3,[r4]
-
- mov r3,r11,asr #6
- mov r4,r3,asr #15
- teq r4,r3,asr #31
- eorne r3,r12,r3,asr #31
- ldr r12,[sp,#0xc]
- ldr r11,[sp,#8]
- rsb r2,r2,r12,lsl #5
- add r2,r11,r2,lsl #1
- strh r3,[r2]
-
- add r10,r10,#1
- cmp r10,#0x10
- blt Loop_j
-
-; Accumulators r4, r5 Initialization
-
- mov r4,#0x20
- mov r5,#0x20
- mov r3,#0x10
-PolyPh_filter_loop2
- add r2,r0,r3,lsl #2
- ldr r12,[r2]
- ldr r8,[r1]
- ldr r6,[r2,#0x80]
- smlal r12,r4,r8,r12
- ldr r12,[r1,#4]
- ldr r7,[r2,#0x40]
- smlal r6,r4,r12,r6
-
- ldr r12,[r1,#8]
- ldr r6,[r2,#0x180]
- smlal r7,r5,r12,r7
- ldr r12,[r2,#0x100]
- ldr r7,[r1,#0xc]
- ldr r2,[r2,#0x140]
- smlal r12,r4,r7,r12
- ldr r12,[r1,#0x10]
- add r3,r3,#0x80
- smlal r6,r4,r12,r6
- ldr r6,[r1,#0x14]
- cmp r3,#0x210
- smlal r2,r5,r6,r2
- add r1,r1,#0x18
-
- blt PolyPh_filter_loop2
- mov r0,r4,asr #6
- mov r2,r0,asr #15
- teq r2,r0,asr #31
- ldrne r12,LOW_16BITS
- ldr r1,[sp,#8]
- eorne r0,r12,r0,asr #31
- strh r0,[r1,#0]
- mov r0,r5,asr #6
- mov r2,r0,asr #15
- teq r2,r0,asr #31
- ldrne r12,LOW_16BITS
- ldr r2,[sp]
- mov r1,#0x10
- eorne r0,r12,r0,asr #31
- ldr r12,[sp,#8]
- mov r1,r1,lsl r2
- add r1,r12,r1,lsl #1
- strh r0,[r1]
- add sp,sp,#0x10
- ldmfd sp!,{r4-r11,pc}
-
-
-PolyPh_filter_coeff
- DCD pqmfSynthWin
-LOW_16BITS
- DCD 0x00007fff
-
- ENDP
-
-
- END
diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm
deleted file mode 100644
index f957267..0000000
--- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm
+++ /dev/null
@@ -1,231 +0,0 @@
-; ------------------------------------------------------------------
-; Copyright (C) 1998-2009 PacketVideo
-;
-; 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.
-; -------------------------------------------------------------------
-
-;
-;
-; Filename: pvmp3_polyphase_filter_window.s
-;
-;------------------------------------------------------------------------------
-; REVISION HISTORY
-;
-;
-; Who: Date: MM/DD/YYYY
-; Description:
-;
-;------------------------------------------------------------------------------
-
- CODE32
-
- AREA |.drectve|, DRECTVE
-
- EXPORT |pvmp3_polyphase_filter_window|
- IMPORT |pqmfSynthWin|
-
- AREA |.pdata|, PDATA
-
- AREA |.text|, CODE, ARM
-
-|pvmp3_polyphase_filter_window| PROC
- stmfd sp!,{r0-r2,r4-r11,lr}
-
- sub sp,sp,#4
- ldr r2,[sp,#0xc]
- ldr r1,PolyPh_filter_coeff
-
- sub r2,r2,#1
- mov r10,#1
- str r2,[sp]
-
-; Accumulators r9, r11::> Initialization
-
-Loop_j
- mov r9, #0x20
- mov r11, #0x20
- mov r4, #0x10
-Loop_i
- add r2,r4,r10
- add r3,r0,r2,lsl #2
- sub r2,r4,r10
- ldr r5,[r3]
- ldr lr,[r1]
- add r12,r0,r2,lsl #2
- ldr r6,[r12,#0x780]
- smlal r2,r9,lr,r5
- smlal r2,r11,lr,r6
- ldr r2,[r1,#4]
- ldr r7,[r12,#0x80]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- sub r9,r9,r5
- ldr r5,[r1,#8]
- ldr r8,[r3,#0x700]
- add r4,r4,#0x200
- smlal r6,r9,r5,r7
- smull r6,r2,r5,r8
- ldr r5,[r1,#0xc]
- sub r11,r11,r2
- smlal r8,r9,r5,r8
- smlal r7,r11,r5,r7
- ldr r5,[r3,#0x100]
- ldr r2,[r1,#0x10]
- ldr r6,[r12,#0x680]
- smlal lr,r9,r2,r5
- smlal lr,r11,r2,r6
- ldr r2,[r1,#0x14]
- ldr r7,[r12,#0x180]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- ldr r6,[r1,#0x18]
- ldr r8,[r3,#0x600]
- sub r9,r9,r5
- smlal r5,r9,r6,r7
- smull r2,r5,r6,r8
- ldr r6,[r1,#0x1c]
- sub r11,r11,r5
- smlal r8,r9,r6,r8
- ldr r2,[r1,#0x20]
- ldr r5,[r3,#0x200]
- smlal r7,r11,r6,r7
- ldr r6,[r12,#0x580]
- smlal lr,r9,r2,r5
- smlal lr,r11,r2,r6
- ldr r2,[r1,#0x24]
- ldr r7,[r12,#0x280]
- smlal r5,r11,r2,r5
- smull r6,r5,r2,r6
- ldr r6,[r1,#0x28]
- ldr r8,[r3,#0x500]
- sub r9,r9,r5
- smlal r5,r9,r6,r7
- smull r2,r5,r6,r8
- ldr r6,[r1,#0x2c]
- sub r11,r11,r5
-
- smlal r8,r9,r6,r8
- smlal r7,r11,r6,r7
- ldr r5,[r3,#0x300]
- ldr r8,[r1,#0x30]
- ldr r6,[r12,#0x480]
- smlal r7,r9,r8,r5
- smlal r7,r11,r8,r6
- ldr r8,[r1,#0x34]
- ldr r12,[r12,#0x380]
- smlal r5,r11,r8,r5
- smull r6,r5,r8,r6
- ldr r6,[r1,#0x38]
-
-
- ldr r3,[r3,#0x400]
- sub r9,r9,r5
- smlal r7,r9,r6,r12
- smull r8,r7,r6,r3
- cmp r4,#0x210
- sub r11,r11,r7
-
- ldr r2,[r1,#0x3c]
- add r1,r1,#0x40
- smlal r3,r9,r2,r3
- smlal r12,r11,r2,r12
-
- blt Loop_i
-
- mov r3,r9, asr #6
- mov r4,r3, asr #15
- teq r4,r3, asr #31
- ldr r12,LOW_16BITS
- ldr r2,[sp]
- eorne r3,r12,r3,asr #31
- ldr r4,[sp,#8]
- mov r2,r10,lsl r2
- add r4,r4,r2,lsl #1
- strh r3,[r4]
-
- mov r3,r11,asr #6
- mov r4,r3,asr #15
- teq r4,r3,asr #31
- eorne r3,r12,r3,asr #31
- ldr r12,[sp,#0xc]
- ldr r11,[sp,#8]
- rsb r2,r2,r12,lsl #5
- add r2,r11,r2,lsl #1
- strh r3,[r2]
-
- add r10,r10,#1
- cmp r10,#0x10
- blt Loop_j
-
-; Accumulators r4, r5 Initialization
-
- mov r4,#0x20
- mov r5,#0x20
- mov r3,#0x10
-PolyPh_filter_loop2
- add r2,r0,r3,lsl #2
- ldr r12,[r2]
- ldr r8,[r1]
- ldr r6,[r2,#0x80]
- smlal r12,r4,r8,r12
- ldr r12,[r1,#4]
- ldr r7,[r2,#0x40]
- smlal r6,r4,r12,r6
-
- ldr r12,[r1,#8]
- ldr r6,[r2,#0x180]
- smlal r7,r5,r12,r7
- ldr r12,[r2,#0x100]
- ldr r7,[r1,#0xc]
- ldr r2,[r2,#0x140]
- smlal r12,r4,r7,r12
- ldr r12,[r1,#0x10]
- add r3,r3,#0x80
- smlal r6,r4,r12,r6
- ldr r6,[r1,#0x14]
- cmp r3,#0x210
- smlal r2,r5,r6,r2
- add r1,r1,#0x18
-
- blt PolyPh_filter_loop2
- mov r0,r4,asr #6
- mov r2,r0,asr #15
- teq r2,r0,asr #31
- ldrne r12,LOW_16BITS
- ldr r1,[sp,#8]
- eorne r0,r12,r0,asr #31
- strh r0,[r1,#0]
- mov r0,r5,asr #6
- mov r2,r0,asr #15
- teq r2,r0,asr #31
- ldrne r12,LOW_16BITS
- ldr r2,[sp]
- mov r1,#0x10
- eorne r0,r12,r0,asr #31
- ldr r12,[sp,#8]
- mov r1,r1,lsl r2
- add r1,r12,r1,lsl #1
- strh r0,[r1]
- add sp,sp,#0x10
- ldmfd sp!,{r4-r11,pc}
-
-
-PolyPh_filter_coeff
- DCD pqmfSynthWin
-LOW_16BITS
- DCD 0x00007fff
-
- ENDP ; |pvmp3_polyphase_filter_window|
- END
-
diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml
index b1f93de..f6db0cc 100644
--- a/media/libstagefright/data/media_codecs_google_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_audio.xml
@@ -24,6 +24,7 @@
<MediaCodec name="OMX.google.g711.mlaw.decoder" type="audio/g711-mlaw" />
<MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis" />
<MediaCodec name="OMX.google.opus.decoder" type="audio/opus" />
+ <MediaCodec name="OMX.google.raw.decoder" type="audio/raw" />
</Decoders>
<Encoders>
diff --git a/media/libstagefright/data/media_codecs_google_video.xml b/media/libstagefright/data/media_codecs_google_video.xml
index 41e0efb..9b930bc 100644
--- a/media/libstagefright/data/media_codecs_google_video.xml
+++ b/media/libstagefright/data/media_codecs_google_video.xml
@@ -19,6 +19,7 @@
<MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" />
<MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" />
<MediaCodec name="OMX.google.h264.decoder" type="video/avc" />
+ <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" />
<MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" />
<MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" />
</Decoders>
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index d1afd8b..338e899 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -555,7 +555,9 @@
}
#endif
- return OK;
+ if (!payload_unit_start_indicator) {
+ return OK;
+ }
}
mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f;
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 65f5404..9b6958a 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -42,6 +42,7 @@
{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
{ "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
{ "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
+ { "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },
{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index eb9fcf7..1c383f7 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -183,12 +183,12 @@
return OMX_ErrorUnsupportedIndex;
}
- if (index >= mNumProfileLevels) {
+ if (profileLevel->nProfileIndex >= mNumProfileLevels) {
return OMX_ErrorNoMore;
}
- profileLevel->eProfile = mProfileLevels[index].mProfile;
- profileLevel->eLevel = mProfileLevels[index].mLevel;
+ profileLevel->eProfile = mProfileLevels[profileLevel->nProfileIndex].mProfile;
+ profileLevel->eLevel = mProfileLevels[profileLevel->nProfileIndex].mLevel;
return OMX_ErrorNone;
}
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 5bc3f2f..3a280f0 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -15,7 +15,7 @@
LOCAL_SHARED_LIBRARIES := \
libaudioflinger \
- libaudiopolicy \
+ libaudiopolicyservice \
libcamera_metadata\
libcameraservice \
libmedialogservice \
@@ -25,7 +25,8 @@
libmediaplayerservice \
libutils \
liblog \
- libbinder
+ libbinder \
+ libsoundtriggerservice
LOCAL_STATIC_LIBRARIES := \
libregistermsext
@@ -36,7 +37,8 @@
frameworks/av/services/audioflinger \
frameworks/av/services/audiopolicy \
frameworks/av/services/camera/libcameraservice \
- $(call include-path-for, audio-utils)
+ $(call include-path-for, audio-utils) \
+ frameworks/av/services/soundtrigger
LOCAL_MODULE:= mediaserver
LOCAL_32_BIT_ONLY := true
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index a347951..af1c9e6 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -34,6 +34,7 @@
#include "MediaLogService.h"
#include "MediaPlayerService.h"
#include "AudioPolicyService.h"
+#include "SoundTriggerHwService.h"
using namespace android;
@@ -128,6 +129,7 @@
MediaPlayerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
+ SoundTriggerHwService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index bd2541f..c5d8858 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaCodec"
#include "NdkMediaCodec.h"
@@ -61,6 +61,8 @@
virtual void onMessageReceived(const sp<AMessage> &msg);
};
+typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
+
struct AMediaCodec {
sp<android::MediaCodec> mCodec;
sp<ALooper> mLooper;
@@ -347,7 +349,7 @@
return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
}
-EXPORT
+//EXPORT
media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
mData->mCallback = callback;
mData->mCallbackUserData = userdata;
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
index cbadea5..1cc2f1a 100644
--- a/media/ndk/NdkMediaCrypto.cpp
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaCrypto"
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index a0cbb70..7a1048c 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaDrm"
#include "NdkMediaDrm.h"
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index b0a9590..492e002 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaExtractor"
@@ -205,7 +205,7 @@
}
EXPORT
-int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
+int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) {
int64_t time;
if (mData->mImpl->getSampleTime(&time) != OK) {
return -1;
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 77018ec..67dc2c2 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaFormat"
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 19b9fc4..b1b0362 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaMuxer"
@@ -96,10 +96,10 @@
EXPORT
media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
- size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info) {
- sp<ABuffer> buf = new ABuffer((void*)(data + info.offset), info.size);
+ size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info) {
+ sp<ABuffer> buf = new ABuffer((void*)(data + info->offset), info->size);
return translate_error(
- muxer->mImpl->writeSampleData(buf, trackIdx, info.presentationTimeUs, info.flags));
+ muxer->mImpl->writeSampleData(buf, trackIdx, info->presentationTimeUs, info->flags));
}
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 8d0a705..0bdf5a3 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -29,6 +29,7 @@
Tracks.cpp \
Effects.cpp \
AudioMixer.cpp.arm \
+ PatchPanel.cpp
LOCAL_SRC_FILES += StateQueue.cpp
@@ -63,6 +64,7 @@
LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp
+LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 45e17f8..527fd65 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -143,7 +143,7 @@
if (rc) {
goto out;
}
- if ((*dev)->common.version != AUDIO_DEVICE_API_VERSION_CURRENT) {
+ if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
rc = BAD_VALUE;
goto out;
@@ -169,7 +169,8 @@
mBtNrecIsOff(false),
mIsLowRamDevice(true),
mIsDeviceTypeKnown(false),
- mGlobalEffectEnableTime(0)
+ mGlobalEffectEnableTime(0),
+ mPrimaryOutputSampleRate(0)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -177,6 +178,7 @@
if (doLog) {
mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY);
}
+
#ifdef TEE_SINK
(void) property_get("ro.debuggable", value, "0");
int debuggable = atoi(value);
@@ -218,6 +220,8 @@
}
}
+ mPatchPanel = new PatchPanel(this);
+
mMode = AUDIO_MODE_NORMAL;
}
@@ -427,7 +431,7 @@
if (mLogMemoryDealer != 0) {
sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
if (binder != 0) {
- fdprintf(fd, "\nmedia.log:\n");
+ dprintf(fd, "\nmedia.log:\n");
Vector<String16> args;
binder->dump(fd, args);
}
@@ -635,8 +639,12 @@
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the Track so that the
// Client destructor is called by the TrackBase destructor with mClientLock held
- Mutex::Autolock _cl(mClientLock);
- client.clear();
+ // Don't hold mClientLock when releasing the reference on the track as the
+ // destructor will acquire it.
+ {
+ Mutex::Autolock _cl(mClientLock);
+ client.clear();
+ }
track.clear();
goto Exit;
}
@@ -1173,7 +1181,7 @@
}
// mClientLock should not be held here because ThreadBase::sendIoConfigEvent() will lock the
- // ThreadBase mutex and teh locknig order is ThreadBase::mLock then AudioFlinger::mClientLock.
+ // ThreadBase mutex and the locking order is ThreadBase::mLock then AudioFlinger::mClientLock.
if (clientAdded) {
// the config change is always sent from playback or record threads to avoid deadlock
// with AudioSystem::gLock
@@ -1419,8 +1427,12 @@
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the RecordTrack so that the
// Client destructor is called by the TrackBase destructor with mClientLock held
- Mutex::Autolock _cl(mClientLock);
- client.clear();
+ // Don't hold mClientLock when releasing the reference on the track as the
+ // destructor will acquire it.
+ {
+ Mutex::Autolock _cl(mClientLock);
+ client.clear();
+ }
recordTrack.clear();
goto Exit;
}
@@ -1668,6 +1680,8 @@
mHardwareStatus = AUDIO_HW_SET_MODE;
hwDevHal->set_mode(hwDevHal, mMode);
mHardwareStatus = AUDIO_HW_IDLE;
+
+ mPrimaryOutputSampleRate = config.sample_rate;
}
return id;
}
@@ -2380,6 +2394,11 @@
if (handle != 0 && id != NULL) {
*id = handle->id();
}
+ if (handle == 0) {
+ // remove local strong reference to Client with mClientLock held
+ Mutex::Autolock _cl(mClientLock);
+ client.clear();
+ }
}
Exit:
@@ -2590,7 +2609,7 @@
}
} else {
if (fd >= 0) {
- fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
+ dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
}
}
char teeTime[16];
@@ -2644,11 +2663,11 @@
write(teeFd, &temp, sizeof(temp));
close(teeFd);
if (fd >= 0) {
- fdprintf(fd, "tee copied to %s\n", teePath);
+ dprintf(fd, "tee copied to %s\n", teePath);
}
} else {
if (fd >= 0) {
- fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
+ dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
}
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index d2ded9a..6e73a14 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -50,6 +50,8 @@
#include <media/AudioBufferProvider.h>
#include <media/ExtendedAudioBufferProvider.h>
+
+#include "FastCapture.h"
#include "FastMixer.h"
#include <media/nbaio/NBAIO.h>
#include "AudioWatchdog.h"
@@ -223,6 +225,27 @@
virtual status_t setLowRamDevice(bool isLowRamDevice);
+ /* List available audio ports and their attributes */
+ virtual status_t listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports);
+
+ /* Get attributes for a given audio port */
+ virtual status_t getAudioPort(struct audio_port *port);
+
+ /* Create an audio patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+
+ /* Release an audio patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+ /* List existing audio patches */
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches);
+
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
virtual status_t onTransact(
uint32_t code,
const Parcel& data,
@@ -397,6 +420,8 @@
#include "Effects.h"
+#include "PatchPanel.h"
+
// server side of the client's IAudioTrack
class TrackHandle : public android::BnAudioTrack {
public:
@@ -504,6 +529,8 @@
const char *moduleName() const { return mModuleName; }
audio_hw_device_t *hwDevice() const { return mHwDevice; }
+ uint32_t version() const { return mHwDevice->common.version; }
+
private:
const char * const mModuleName;
audio_hw_device_t * const mHwDevice;
@@ -664,6 +691,11 @@
bool mIsLowRamDevice;
bool mIsDeviceTypeKnown;
nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled
+
+ sp<PatchPanel> mPatchPanel;
+
+ uint32_t mPrimaryOutputSampleRate; // sample rate of the primary output, or zero if none
+ // protected by mHardwareLock
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 805eaa4..d73292e 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -34,6 +34,7 @@
#include <system/audio.h>
#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
#include <common_time/local_clock.h>
#include <common_time/cc_helper.h>
@@ -88,6 +89,103 @@
}
}
+template <typename T>
+T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
+ audio_format_t inputFormat, audio_format_t outputFormat) :
+ mTrackBufferProvider(NULL),
+ mChannels(channels),
+ mInputFormat(inputFormat),
+ mOutputFormat(outputFormat),
+ mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)),
+ mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)),
+ mOutputData(NULL),
+ mOutputCount(0),
+ mConsumed(0)
+{
+ ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
+ if (requiresInternalBuffers()) {
+ mOutputCount = 256;
+ (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize);
+ }
+ mBuffer.frameCount = 0;
+}
+
+AudioMixer::ReformatBufferProvider::~ReformatBufferProvider()
+{
+ ALOGV("~ReformatBufferProvider(%p)", this);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mOutputData);
+}
+
+status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+ int64_t pts) {
+ //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ // this, pBuffer, pBuffer->frameCount, pts);
+ if (!requiresInternalBuffers()) {
+ status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+ if (res == OK) {
+ memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat,
+ pBuffer->frameCount * mChannels);
+ }
+ return res;
+ }
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = pBuffer->frameCount;
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+ // TODO: Track down a bug in the upstream provider
+ // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0,
+ // "ReformatBufferProvider::getNextBuffer():"
+ // " Invalid zero framecount returned from getNextBuffer()");
+ if (res != OK || mBuffer.frameCount == 0) {
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ }
+ }
+ ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+ size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed);
+ count = min(count, pBuffer->frameCount);
+ pBuffer->raw = mOutputData;
+ pBuffer->frameCount = count;
+ //ALOGV("reformatting %d frames from %#x to %#x, %d chan",
+ // pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels);
+ memcpy_by_audio_format(pBuffer->raw, mOutputFormat,
+ (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat,
+ pBuffer->frameCount * mChannels);
+ return OK;
+}
+
+void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
+ //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))",
+ // this, pBuffer, pBuffer->frameCount);
+ if (!requiresInternalBuffers()) {
+ mTrackBufferProvider->releaseBuffer(pBuffer);
+ return;
+ }
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+ if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+ mConsumed = 0;
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ // ALOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void AudioMixer::ReformatBufferProvider::reset() {
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ mConsumed = 0;
+}
// ----------------------------------------------------------------------------
bool AudioMixer::sIsMultichannelCapable = false;
@@ -153,8 +251,13 @@
mState.mLog = log;
}
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId)
{
+ if (!isValidPcmTrackFormat(format)) {
+ ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
+ return -1;
+ }
uint32_t names = (~mTrackNames) & mConfiguredNames;
if (names != 0) {
int n = __builtin_ctz(names);
@@ -162,8 +265,8 @@
// assume default parameters for the track, except where noted below
track_t* t = &mState.tracks[n];
t->needs = 0;
- t->volume[0] = UNITY_GAIN;
- t->volume[1] = UNITY_GAIN;
+ t->volume[0] = UNITY_GAIN_INT;
+ t->volume[1] = UNITY_GAIN_INT;
// no initialization needed
// t->prevVolume[0]
// t->prevVolume[1]
@@ -176,7 +279,8 @@
// t->frameCount
t->channelCount = audio_channel_count_from_out_mask(channelMask);
t->enabled = false;
- t->format = 16;
+ ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO,
+ "Non-stereo channel mask: %d\n", channelMask);
t->channelMask = channelMask;
t->sessionId = sessionId;
// setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
@@ -191,9 +295,15 @@
// setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
t->mainBuffer = NULL;
t->auxBuffer = NULL;
+ t->mInputBufferProvider = NULL;
+ t->mReformatBufferProvider = NULL;
t->downmixerBufferProvider = NULL;
t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
-
+ t->mFormat = format;
+ t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT;
+ if (t->mFormat != t->mMixerInFormat) {
+ prepareTrackForReformat(t, n);
+ }
status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
@@ -237,9 +347,9 @@
if (pTrack->downmixerBufferProvider != NULL) {
// this track had previously been configured with a downmixer, delete it
ALOGV(" deleting old downmixer");
- pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
delete pTrack->downmixerBufferProvider;
pTrack->downmixerBufferProvider = NULL;
+ reconfigureBufferProviders(pTrack);
} else {
ALOGV(" nothing to do, no downmixer to delete");
}
@@ -333,21 +443,51 @@
}// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"
// initialization successful:
- // - keep track of the real buffer provider in case it was set before
- pDbp->mTrackBufferProvider = pTrack->bufferProvider;
- // - we'll use the downmix effect integrated inside this
- // track's buffer provider, and we'll use it as the track's buffer provider
pTrack->downmixerBufferProvider = pDbp;
- pTrack->bufferProvider = pDbp;
-
+ reconfigureBufferProviders(pTrack);
return NO_ERROR;
noDownmixForActiveTrack:
delete pDbp;
pTrack->downmixerBufferProvider = NULL;
+ reconfigureBufferProviders(pTrack);
return NO_INIT;
}
+void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) {
+ ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName);
+ if (pTrack->mReformatBufferProvider != NULL) {
+ delete pTrack->mReformatBufferProvider;
+ pTrack->mReformatBufferProvider = NULL;
+ reconfigureBufferProviders(pTrack);
+ }
+}
+
+status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName)
+{
+ ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat);
+ // discard the previous reformatter if there was one
+ unprepareTrackForReformat(pTrack, trackName);
+ pTrack->mReformatBufferProvider = new ReformatBufferProvider(
+ audio_channel_count_from_out_mask(pTrack->channelMask),
+ pTrack->mFormat, pTrack->mMixerInFormat);
+ reconfigureBufferProviders(pTrack);
+ return NO_ERROR;
+}
+
+void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
+{
+ pTrack->bufferProvider = pTrack->mInputBufferProvider;
+ if (pTrack->mReformatBufferProvider) {
+ pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+ pTrack->bufferProvider = pTrack->mReformatBufferProvider;
+ }
+ if (pTrack->downmixerBufferProvider) {
+ pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+ pTrack->bufferProvider = pTrack->downmixerBufferProvider;
+ }
+}
+
void AudioMixer::deleteTrackName(int name)
{
ALOGV("AudioMixer::deleteTrackName(%d)", name);
@@ -364,6 +504,8 @@
track.resampler = NULL;
// delete the downmixer
unprepareTrackForDownmix(&mState.tracks[name], name);
+ // delete the reformatter
+ unprepareTrackForReformat(&mState.tracks[name], name);
mTrackNames &= ~(1<<name);
}
@@ -394,6 +536,44 @@
}
}
+/* Sets the volume ramp variables for the AudioMixer.
+ *
+ * The volume ramp variables are used to transition between the previous
+ * volume to the target volume. The duration of the transition is
+ * set by ramp, which is either 0 for immediate, or typically one state
+ * framecount period.
+ *
+ * @param newFloatValue new volume target in float [0.0, 1.0].
+ * @param ramp number of frames to increment over. ramp is 0 if the volume
+ * should be set immediately.
+ * @param volume reference to the U4.12 target volume, set on return.
+ * @param prevVolume reference to the U4.27 previous volume, set on return.
+ * @param volumeInc reference to the increment per output audio frame, set on return.
+ * @return true if the volume has changed, false if volume is same.
+ */
+static inline bool setVolumeRampVariables(float newFloatValue, int32_t ramp,
+ int16_t &volume, int32_t &prevVolume, int32_t &volumeInc) {
+ int32_t newValue = newFloatValue * AudioMixer::UNITY_GAIN_INT;
+ if (newValue > AudioMixer::UNITY_GAIN_INT) {
+ newValue = AudioMixer::UNITY_GAIN_INT;
+ } else if (newValue < 0) {
+ ALOGE("negative volume %.7g", newFloatValue);
+ newValue = 0; // should never happen, but for safety check.
+ }
+ if (newValue == volume) {
+ return false;
+ }
+ if (ramp != 0) {
+ volumeInc = ((newValue - volume) << 16) / ramp;
+ prevVolume = (volumeInc == 0 ? newValue : volume) << 16;
+ } else {
+ volumeInc = 0;
+ prevVolume = newValue << 16;
+ }
+ volume = newValue;
+ return true;
+}
+
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
name -= TRACK0;
@@ -435,9 +615,20 @@
invalidateState(1 << name);
}
break;
- case FORMAT:
- ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT);
- break;
+ case FORMAT: {
+ audio_format_t format = static_cast<audio_format_t>(valueInt);
+ if (track.mFormat != format) {
+ ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
+ track.mFormat = format;
+ ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
+ //if (track.mFormat != track.mMixerInFormat)
+ {
+ ALOGD("Reformatting!");
+ prepareTrackForReformat(&track, name);
+ }
+ invalidateState(1 << name);
+ }
+ } break;
// FIXME do we want to support setting the downmix type from AudioFlinger?
// for a specific track? or per mixer?
/* case DOWNMIX_TYPE:
@@ -484,41 +675,23 @@
switch (param) {
case VOLUME0:
case VOLUME1:
- if (track.volume[param-VOLUME0] != valueInt) {
- ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
- track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16;
- track.volume[param-VOLUME0] = valueInt;
- if (target == VOLUME) {
- track.prevVolume[param-VOLUME0] = valueInt << 16;
- track.volumeInc[param-VOLUME0] = 0;
- } else {
- int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0];
- int32_t volInc = d / int32_t(mState.frameCount);
- track.volumeInc[param-VOLUME0] = volInc;
- if (volInc == 0) {
- track.prevVolume[param-VOLUME0] = valueInt << 16;
- }
- }
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mState.frameCount : 0,
+ track.volume[param - VOLUME0], track.prevVolume[param - VOLUME0],
+ track.volumeInc[param - VOLUME0])) {
+ ALOGV("setParameter(%s, VOLUME%d: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
+ track.volume[param - VOLUME0]);
invalidateState(1 << name);
}
break;
case AUXLEVEL:
//ALOG_ASSERT(0 <= valueInt && valueInt <= MAX_GAIN_INT, "bad aux level %d", valueInt);
- if (track.auxLevel != valueInt) {
- ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
- track.prevAuxLevel = track.auxLevel << 16;
- track.auxLevel = valueInt;
- if (target == VOLUME) {
- track.prevAuxLevel = valueInt << 16;
- track.auxInc = 0;
- } else {
- int32_t d = (valueInt<<16) - track.prevAuxLevel;
- int32_t volInc = d / int32_t(mState.frameCount);
- track.auxInc = volInc;
- if (volInc == 0) {
- track.prevAuxLevel = valueInt << 16;
- }
- }
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mState.frameCount : 0,
+ track.auxLevel, track.prevAuxLevel, track.auxInc)) {
+ ALOGV("setParameter(%s, AUXLEVEL: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel);
invalidateState(1 << name);
}
break;
@@ -550,8 +723,9 @@
} else {
quality = AudioResampler::DEFAULT_QUALITY;
}
+ const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32;
resampler = AudioResampler::create(
- format,
+ bits,
// the resampler sees the number of channels after the downmixer, if any
(int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
devSampleRate, quality);
@@ -596,21 +770,16 @@
name -= TRACK0;
ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
- if (mState.tracks[name].downmixerBufferProvider != NULL) {
- // update required?
- if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) {
- ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider);
- // setting the buffer provider for a track that gets downmixed consists in:
- // 1/ setting the buffer provider to the "downmix / buffer provider" wrapper
- // so it's the one that gets called when the buffer provider is needed,
- mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider;
- // 2/ saving the buffer provider for the track so the wrapper can use it
- // when it downmixes.
- mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider;
- }
- } else {
- mState.tracks[name].bufferProvider = bufferProvider;
+ if (mState.tracks[name].mInputBufferProvider == bufferProvider) {
+ return; // don't reset any buffer providers if identical.
}
+ if (mState.tracks[name].mReformatBufferProvider != NULL) {
+ mState.tracks[name].mReformatBufferProvider->reset();
+ } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
+ }
+
+ mState.tracks[name].mInputBufferProvider = bufferProvider;
+ reconfigureBufferProviders(&mState.tracks[name]);
}
@@ -769,7 +938,7 @@
// always resample with unity gain when sending to auxiliary buffer to be able
// to apply send level after resampling
// TODO: modify each resampler to support aux channel?
- t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
@@ -779,7 +948,7 @@
}
} else {
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
- t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
volumeRampStereo(t, out, outFrameCount, temp, aux);
@@ -1301,6 +1470,7 @@
AudioBufferProvider::Buffer& b(t.buffer);
int32_t* out = t.mainBuffer;
+ float *fout = reinterpret_cast<float*>(out);
size_t numFrames = state->frameCount;
const int16_t vl = t.volume[0];
@@ -1314,9 +1484,10 @@
// in == NULL can happen if the track was flushed just after having
// been enabled for mixing.
- if (in == NULL || ((unsigned long)in & 3)) {
- memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t));
- ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: "
+ if (in == NULL || (((uintptr_t)in) & 3)) {
+ memset(out, 0, numFrames
+ * MAX_NUM_CHANNELS * audio_bytes_per_sample(t.mMixerFormat));
+ ALOGE_IF((((uintptr_t)in) & 3), "process stereo track: input buffer alignment pb: "
"buffer %p track %d, channels %d, needs %08x",
in, i, t.channelCount, t.needs);
return;
@@ -1324,8 +1495,7 @@
size_t outFrames = b.frameCount;
switch (t.mMixerFormat) {
- case AUDIO_FORMAT_PCM_FLOAT: {
- float *fout = reinterpret_cast<float*>(out);
+ case AUDIO_FORMAT_PCM_FLOAT:
do {
uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
in += 2;
@@ -1336,9 +1506,9 @@
// Note: In case of later int16_t sink output,
// conversion and clamping is done by memcpy_to_i16_from_float().
} while (--outFrames);
- } break;
+ break;
case AUDIO_FORMAT_PCM_16_BIT:
- if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
+ if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) {
// volume is boosted, so we might need to clamp even though
// we process only one track.
do {
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 09e63a6..766ff60 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -31,7 +31,7 @@
#include <media/nbaio/NBLog.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
-#define MAX_GAIN_INT AudioMixer::UNITY_GAIN
+#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
namespace android {
@@ -58,7 +58,8 @@
// maximum number of channels supported for the content
static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8;
- static const uint16_t UNITY_GAIN = 0x1000;
+ static const uint16_t UNITY_GAIN_INT = 0x1000;
+ static const float UNITY_GAIN_FLOAT = 1.0f;
enum { // names
@@ -104,7 +105,10 @@
// For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
// Allocate a track name. Returns new track name if successful, -1 on failure.
- int getTrackName(audio_channel_mask_t channelMask, int sessionId);
+ // The failure could be because of an invalid channelMask or format, or that
+ // the track capacity of the mixer is exceeded.
+ int getTrackName(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId);
// Free an allocated track by name
void deleteTrackName(int name);
@@ -122,6 +126,13 @@
size_t getUnreleasedFrames(int name) const;
+ static inline bool isValidPcmTrackFormat(audio_format_t format) {
+ return format == AUDIO_FORMAT_PCM_16_BIT ||
+ format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
+ format == AUDIO_FORMAT_PCM_32_BIT ||
+ format == AUDIO_FORMAT_PCM_FLOAT;
+ }
+
private:
enum {
@@ -143,6 +154,7 @@
struct state_t;
struct track_t;
class DownmixerBufferProvider;
+ class ReformatBufferProvider;
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
int32_t* aux);
@@ -170,7 +182,7 @@
uint16_t frameCount;
uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
- uint8_t format; // always 16
+ uint8_t unused_padding; // formerly format, was always 16
uint16_t enabled; // actually bool
audio_channel_mask_t channelMask;
@@ -193,14 +205,19 @@
int32_t* auxBuffer;
// 16-byte boundary
-
+ AudioBufferProvider* mInputBufferProvider; // 4 bytes
+ ReformatBufferProvider* mReformatBufferProvider; // 4 bytes
DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
int32_t sessionId;
- audio_format_t mMixerFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ // 16-byte boundary
+ audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ audio_format_t mFormat; // input track format
+ audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ // each track must be converted to this format.
- int32_t padding[1];
+ int32_t mUnused[1]; // alignment padding
// 16-byte boundary
@@ -239,6 +256,35 @@
effect_config_t mDownmixConfig;
};
+ // AudioBufferProvider wrapper that reformats track to acceptable mixer input type
+ class ReformatBufferProvider : public AudioBufferProvider {
+ public:
+ ReformatBufferProvider(int32_t channels,
+ audio_format_t inputFormat, audio_format_t outputFormat);
+ virtual ~ReformatBufferProvider();
+
+ // overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer* buffer);
+
+ void reset();
+ inline bool requiresInternalBuffers() {
+ return true; //mInputFrameSize < mOutputFrameSize;
+ }
+
+ AudioBufferProvider* mTrackBufferProvider;
+ int32_t mChannels;
+ audio_format_t mInputFormat;
+ audio_format_t mOutputFormat;
+ size_t mInputFrameSize;
+ size_t mOutputFrameSize;
+ // (only) required for reformatting to a larger size.
+ AudioBufferProvider::Buffer mBuffer;
+ void* mOutputData;
+ size_t mOutputCount;
+ size_t mConsumed;
+ };
+
// bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames;
@@ -266,6 +312,9 @@
static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask);
static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
+ static status_t prepareTrackForReformat(track_t* pTrack, int trackNum);
+ static void unprepareTrackForReformat(track_t* pTrack, int trackName);
+ static void reconfigureBufferProviders(track_t* pTrack);
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
int32_t* aux);
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 3abe8fd..318eb57 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -455,13 +455,20 @@
const Constants& c(mConstants);
const TC* const coefs = mConstants.mFirCoefs;
TI* impulse = mInBuffer.getImpulse();
- size_t inputIndex = mInputIndex;
+ size_t inputIndex = 0;
uint32_t phaseFraction = mPhaseFraction;
const uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2; // stereo output
- size_t inFrameCount = getInFrameCountRequired(outFrameCount);
const uint32_t phaseWrapLimit = c.mL << c.mShift;
+ size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction)
+ / phaseWrapLimit;
+ // sanity check that inFrameCount is in signed 32 bit integer range.
+ ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31));
+
+ //ALOGV("inFrameCount:%d outFrameCount:%d"
+ // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u",
+ // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit);
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
@@ -471,29 +478,39 @@
// the following logic is a bit convoluted to keep the main processing loop
// as tight as possible with register allocation.
while (outputIndex < outputSampleCount) {
- // buffer is empty, fetch a new one
- while (mBuffer.frameCount == 0) {
+ //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d"
+ // " phaseFraction:%u phaseWrapLimit:%u",
+ // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit);
+
+ // check inputIndex overflow
+ ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d",
+ inputIndex, mBuffer.frameCount);
+ // Buffer is empty, fetch a new one if necessary (inFrameCount > 0).
+ // We may not fetch a new buffer if the existing data is sufficient.
+ while (mBuffer.frameCount == 0 && inFrameCount > 0) {
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer,
calculateOutputPTS(outputIndex / 2));
if (mBuffer.raw == NULL) {
goto resample_exit;
}
+ inFrameCount -= mBuffer.frameCount;
if (phaseFraction >= phaseWrapLimit) { // read in data
mInBuffer.template readAdvance<CHANNELS>(
impulse, c.mHalfNumCoefs,
reinterpret_cast<TI*>(mBuffer.raw), inputIndex);
+ inputIndex++;
phaseFraction -= phaseWrapLimit;
while (phaseFraction >= phaseWrapLimit) {
- inputIndex++;
if (inputIndex >= mBuffer.frameCount) {
- inputIndex -= mBuffer.frameCount;
+ inputIndex = 0;
provider->releaseBuffer(&mBuffer);
break;
}
mInBuffer.template readAdvance<CHANNELS>(
impulse, c.mHalfNumCoefs,
reinterpret_cast<TI*>(mBuffer.raw), inputIndex);
+ inputIndex++;
phaseFraction -= phaseWrapLimit;
}
}
@@ -504,9 +521,6 @@
const int halfNumCoefs = c.mHalfNumCoefs;
const TO* const volumeSimd = mVolumeSimd;
- // reread the last input in.
- mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
-
// main processing loop
while (CC_LIKELY(outputIndex < outputSampleCount)) {
// caution: fir() is inlined and may be large.
@@ -515,6 +529,10 @@
// from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs]
// from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs.
//
+ //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d"
+ // " phaseFraction:%u phaseWrapLimit:%u",
+ // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit);
+ ALOG_ASSERT(phaseFraction < phaseWrapLimit);
fir<CHANNELS, LOCKED, STRIDE>(
&out[outputIndex],
phaseFraction, phaseWrapLimit,
@@ -524,26 +542,34 @@
phaseFraction += phaseIncrement;
while (phaseFraction >= phaseWrapLimit) {
- inputIndex++;
if (inputIndex >= frameCount) {
goto done; // need a new buffer
}
mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex);
+ inputIndex++;
phaseFraction -= phaseWrapLimit;
}
}
done:
- // often arrives here when input buffer runs out
- if (inputIndex >= frameCount) {
- inputIndex -= frameCount;
+ // We arrive here when we're finished or when the input buffer runs out.
+ // Regardless we need to release the input buffer if we've acquired it.
+ if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount)
+ ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)",
+ inputIndex, frameCount); // must have been fully read.
+ inputIndex = 0;
provider->releaseBuffer(&mBuffer);
- // mBuffer.frameCount MUST be zero here.
+ ALOG_ASSERT(mBuffer.frameCount == 0);
}
}
resample_exit:
+ // inputIndex must be zero in all three cases:
+ // (1) the buffer never was been acquired; (2) the buffer was
+ // released at "done:"; or (3) getNextBuffer() failed.
+ ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u",
+ inputIndex, mBuffer.frameCount, phaseFraction);
+ ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer
mInBuffer.setImpulse(impulse);
- mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
}
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 93d185e..877e776 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -34,7 +34,7 @@
} else {
strcpy(buf, "N/A\n");
}
- fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
+ dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
mUnderruns, mLogs, buf);
}
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
new file mode 100644
index 0000000..0c9b976
--- /dev/null
+++ b/services/audioflinger/FastCapture.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 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 "FastCapture"
+//#define LOG_NDEBUG 0
+
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <media/AudioBufferProvider.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "FastCapture.h"
+
+namespace android {
+
+/*static*/ const FastCaptureState FastCapture::initial;
+
+FastCapture::FastCapture() : FastThread(),
+ inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0),
+ readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0),
+ // dummyDumpState
+ totalNativeFramesRead(0)
+{
+ previous = &initial;
+ current = &initial;
+
+ mDummyDumpState = &dummyDumpState;
+}
+
+FastCapture::~FastCapture()
+{
+}
+
+FastCaptureStateQueue* FastCapture::sq()
+{
+ return &mSQ;
+}
+
+const FastThreadState *FastCapture::poll()
+{
+ return mSQ.poll();
+}
+
+void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+{
+}
+
+void FastCapture::onIdle()
+{
+ preIdle = *(const FastCaptureState *)current;
+ current = &preIdle;
+}
+
+void FastCapture::onExit()
+{
+ delete[] readBuffer;
+}
+
+bool FastCapture::isSubClassCommand(FastThreadState::Command command)
+{
+ switch ((FastCaptureState::Command) command) {
+ case FastCaptureState::READ:
+ case FastCaptureState::WRITE:
+ case FastCaptureState::READ_WRITE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void FastCapture::onStateChange()
+{
+ const FastCaptureState * const current = (const FastCaptureState *) this->current;
+ const FastCaptureState * const previous = (const FastCaptureState *) this->previous;
+ FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+ const size_t frameCount = current->mFrameCount;
+
+ bool eitherChanged = false;
+
+ // check for change in input HAL configuration
+ NBAIO_Format previousFormat = format;
+ if (current->mInputSourceGen != inputSourceGen) {
+ inputSource = current->mInputSource;
+ inputSourceGen = current->mInputSourceGen;
+ if (inputSource == NULL) {
+ format = Format_Invalid;
+ sampleRate = 0;
+ } else {
+ format = inputSource->format();
+ sampleRate = Format_sampleRate(format);
+ unsigned channelCount = Format_channelCount(format);
+ ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+ }
+ dumpState->mSampleRate = sampleRate;
+ eitherChanged = true;
+ }
+
+ // check for change in pipe
+ if (current->mPipeSinkGen != pipeSinkGen) {
+ pipeSink = current->mPipeSink;
+ pipeSinkGen = current->mPipeSinkGen;
+ eitherChanged = true;
+ }
+
+ // input source and pipe sink must be compatible
+ if (eitherChanged && inputSource != NULL && pipeSink != NULL) {
+ ALOG_ASSERT(Format_isEqual(format, pipeSink->format()));
+ }
+
+ if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
+ // FIXME to avoid priority inversion, don't delete here
+ delete[] readBuffer;
+ readBuffer = NULL;
+ if (frameCount > 0 && sampleRate > 0) {
+ // FIXME new may block for unbounded time at internal mutex of the heap
+ // implementation; it would be better to have normal capture thread allocate for
+ // us to avoid blocking here and to prevent possible priority inversion
+ unsigned channelCount = Format_channelCount(format);
+ // FIXME frameSize
+ readBuffer = new short[frameCount * channelCount];
+ periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
+ underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
+ overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
+ forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95
+ warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50
+ } else {
+ periodNs = 0;
+ underrunNs = 0;
+ overrunNs = 0;
+ forceNs = 0;
+ warmupNs = 0;
+ }
+ readBufferState = -1;
+ dumpState->mFrameCount = frameCount;
+ }
+
+}
+
+void FastCapture::onWork()
+{
+ const FastCaptureState * const current = (const FastCaptureState *) this->current;
+ FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+ const FastCaptureState::Command command = this->command;
+ const size_t frameCount = current->mFrameCount;
+
+ if ((command & FastCaptureState::READ) /*&& isWarm*/) {
+ ALOG_ASSERT(inputSource != NULL);
+ ALOG_ASSERT(readBuffer != NULL);
+ dumpState->mReadSequence++;
+ ATRACE_BEGIN("read");
+ ssize_t framesRead = inputSource->read(readBuffer, frameCount,
+ AudioBufferProvider::kInvalidPTS);
+ ATRACE_END();
+ dumpState->mReadSequence++;
+ if (framesRead >= 0) {
+ LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
+ totalNativeFramesRead += framesRead;
+ dumpState->mFramesRead = totalNativeFramesRead;
+ readBufferState = framesRead;
+ } else {
+ dumpState->mReadErrors++;
+ readBufferState = 0;
+ }
+ // FIXME rename to attemptedIO
+ attemptedWrite = true;
+ }
+
+ if (command & FastCaptureState::WRITE) {
+ ALOG_ASSERT(pipeSink != NULL);
+ ALOG_ASSERT(readBuffer != NULL);
+ if (readBufferState < 0) {
+ unsigned channelCount = Format_channelCount(format);
+ // FIXME frameSize
+ memset(readBuffer, 0, frameCount * channelCount * sizeof(short));
+ readBufferState = frameCount;
+ }
+ if (readBufferState > 0) {
+ ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState);
+ // FIXME This supports at most one fast capture client.
+ // To handle multiple clients this could be converted to an array,
+ // or with a lot more work the control block could be shared by all clients.
+ audio_track_cblk_t* cblk = current->mCblk;
+ if (cblk != NULL && framesWritten > 0) {
+ int32_t rear = cblk->u.mStreaming.mRear;
+ android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear);
+ cblk->mServer += framesWritten;
+ int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+ if (!(old & CBLK_FUTEX_WAKE)) {
+ // client is never in server process, so don't use FUTEX_WAKE_PRIVATE
+ (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1);
+ }
+ }
+ }
+ }
+}
+
+FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(),
+ mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0)
+{
+}
+
+FastCaptureDumpState::~FastCaptureDumpState()
+{
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
new file mode 100644
index 0000000..e535b9d
--- /dev/null
+++ b/services/audioflinger/FastCapture.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_CAPTURE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_H
+
+#include "FastThread.h"
+#include "StateQueue.h"
+#include "FastCaptureState.h"
+
+namespace android {
+
+typedef StateQueue<FastCaptureState> FastCaptureStateQueue;
+
+struct FastCaptureDumpState : FastThreadDumpState {
+ FastCaptureDumpState();
+ /*virtual*/ ~FastCaptureDumpState();
+
+ // FIXME by renaming, could pull up many of these to FastThreadDumpState
+ uint32_t mReadSequence; // incremented before and after each read()
+ uint32_t mFramesRead; // total number of frames read successfully
+ uint32_t mReadErrors; // total number of read() errors
+ uint32_t mSampleRate;
+ size_t mFrameCount;
+};
+
+class FastCapture : public FastThread {
+
+public:
+ FastCapture();
+ virtual ~FastCapture();
+
+ FastCaptureStateQueue* sq();
+
+private:
+ FastCaptureStateQueue mSQ;
+
+ // callouts
+ virtual const FastThreadState *poll();
+ virtual void setLog(NBLog::Writer *logWriter);
+ virtual void onIdle();
+ virtual void onExit();
+ virtual bool isSubClassCommand(FastThreadState::Command command);
+ virtual void onStateChange();
+ virtual void onWork();
+
+ static const FastCaptureState initial;
+ FastCaptureState preIdle; // copy of state before we went into idle
+ // FIXME by renaming, could pull up many of these to FastThread
+ NBAIO_Source *inputSource;
+ int inputSourceGen;
+ NBAIO_Sink *pipeSink;
+ int pipeSinkGen;
+ short *readBuffer;
+ ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear
+ NBAIO_Format format;
+ unsigned sampleRate;
+ FastCaptureDumpState dummyDumpState;
+ uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead
+
+}; // class FastCapture
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_CAPTURE_H
diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp
new file mode 100644
index 0000000..1d029b7
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FastCaptureState.h"
+
+namespace android {
+
+FastCaptureState::FastCaptureState() : FastThreadState(),
+ mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mFrameCount(0)
+{
+}
+
+FastCaptureState::~FastCaptureState()
+{
+}
+
+} // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
new file mode 100644
index 0000000..29c865a
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+
+#include <media/nbaio/NBAIO.h>
+#include "FastThreadState.h"
+#include <private/media/AudioTrackShared.h>
+
+namespace android {
+
+// Represent a single state of the fast capture
+struct FastCaptureState : FastThreadState {
+ FastCaptureState();
+ /*virtual*/ ~FastCaptureState();
+
+ // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread
+ NBAIO_Source *mInputSource; // HAL input device, must already be negotiated
+ // FIXME by renaming, could pull up these fields to FastThreadState
+ int mInputSourceGen; // increment when mInputSource is assigned
+ NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink
+ int mPipeSinkGen; // increment when mPipeSink is assigned
+ size_t mFrameCount; // number of frames per fast capture buffer
+ audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL
+
+ // Extends FastThreadState::Command
+ static const Command
+ // The following commands also process configuration changes, and can be "or"ed:
+ READ = 0x8, // read from input source
+ WRITE = 0x10, // write to pipe sink
+ READ_WRITE = 0x18; // read from input source and write to pipe sink
+
+}; // struct FastCaptureState
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_CAPTURE_STATE_H
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 1caed11..c486630 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -26,7 +26,6 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include "Configuration.h"
-#include <sys/atomics.h>
#include <time.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -37,6 +36,7 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
#endif
+#include <audio_utils/format.h>
#include "AudioMixer.h"
#include "FastMixer.h"
@@ -53,8 +53,12 @@
outputSink(NULL),
outputSinkGen(0),
mixer(NULL),
- mixBuffer(NULL),
- mixBufferState(UNDEFINED),
+ mSinkBuffer(NULL),
+ mSinkBufferSize(0),
+ mMixerBuffer(NULL),
+ mMixerBufferSize(0),
+ mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT),
+ mMixerBufferState(UNDEFINED),
format(Format_Invalid),
sampleRate(0),
fastTracksGen(0),
@@ -109,7 +113,8 @@
void FastMixer::onExit()
{
delete mixer;
- delete[] mixBuffer;
+ free(mMixerBuffer);
+ free(mSinkBuffer);
}
bool FastMixer::isSubClassCommand(FastThreadState::Command command)
@@ -155,14 +160,23 @@
// FIXME to avoid priority inversion, don't delete here
delete mixer;
mixer = NULL;
- delete[] mixBuffer;
- mixBuffer = NULL;
+ free(mMixerBuffer);
+ mMixerBuffer = NULL;
+ free(mSinkBuffer);
+ mSinkBuffer = NULL;
if (frameCount > 0 && sampleRate > 0) {
// FIXME new may block for unbounded time at internal mutex of the heap
// 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 * FCC_2];
+ const size_t mixerFrameSize = FCC_2 * audio_bytes_per_sample(mMixerBufferFormat);
+ mMixerBufferSize = mixerFrameSize * frameCount;
+ (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
+ const size_t sinkFrameSize = FCC_2 * audio_bytes_per_sample(format.mFormat);
+ if (sinkFrameSize > mixerFrameSize) { // need a sink buffer
+ mSinkBufferSize = sinkFrameSize * frameCount;
+ (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize);
+ }
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
@@ -175,7 +189,7 @@
forceNs = 0;
warmupNs = 0;
}
- mixBufferState = UNDEFINED;
+ mMixerBufferState = UNDEFINED;
#if !LOG_NDEBUG
for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
fastTrackNames[i] = -1;
@@ -193,7 +207,7 @@
const unsigned currentTrackMask = current->mTrackMask;
dumpState->mTrackMask = currentTrackMask;
if (current->mFastTracksGen != fastTracksGen) {
- ALOG_ASSERT(mixBuffer != NULL);
+ ALOG_ASSERT(mMixerBuffer != NULL);
int name;
// process removed tracks first to avoid running out of track names
@@ -224,13 +238,20 @@
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
if (mixer != NULL) {
- name = mixer->getTrackName(fastTrack->mChannelMask, AUDIO_SESSION_OUTPUT_MIX);
+ name = mixer->getTrackName(fastTrack->mChannelMask,
+ fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
ALOG_ASSERT(name >= 0);
fastTrackNames[i] = name;
mixer->setBufferProvider(name, bufferProvider);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
- (void *) mixBuffer);
+ (void *) mMixerBuffer);
// newly allocated track names default to full scale volume
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+ mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+ (void *)(uintptr_t)fastTrack->mFormat);
mixer->enable(name);
}
generations[i] = fastTrack->mGeneration;
@@ -252,13 +273,18 @@
ALOG_ASSERT(name >= 0);
mixer->setBufferProvider(name, bufferProvider);
if (fastTrack->mVolumeProvider == NULL) {
- mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
- (void *) MAX_GAIN_INT);
- mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
- (void *) MAX_GAIN_INT);
+ float f = AudioMixer::UNITY_GAIN_FLOAT;
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
}
mixer->setParameter(name, AudioMixer::RESAMPLE,
AudioMixer::REMOVE, NULL);
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+ mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+ (void *)(uintptr_t)fastTrack->mFormat);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
(void *)(uintptr_t) fastTrack->mChannelMask);
// already enabled
@@ -281,7 +307,7 @@
const size_t frameCount = current->mFrameCount;
if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) {
- ALOG_ASSERT(mixBuffer != NULL);
+ ALOG_ASSERT(mMixerBuffer != NULL);
// for each track, update volume and check for underrun
unsigned currentTrackMask = current->mTrackMask;
while (currentTrackMask != 0) {
@@ -309,12 +335,11 @@
ALOG_ASSERT(name >= 0);
if (fastTrack->mVolumeProvider != NULL) {
gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
- mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
- (void *) (uintptr_t)
- (float_from_gain(gain_minifloat_unpack_left(vlr)) * MAX_GAIN_INT));
- mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
- (void *) (uintptr_t)
- (float_from_gain(gain_minifloat_unpack_right(vlr)) * MAX_GAIN_INT));
+ float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+ float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
}
// FIXME The current implementation of framesReady() for fast tracks
// takes a tryLock, which can block
@@ -358,26 +383,31 @@
// process() is CPU-bound
mixer->process(pts);
- mixBufferState = MIXED;
- } else if (mixBufferState == MIXED) {
- mixBufferState = UNDEFINED;
+ mMixerBufferState = MIXED;
+ } else if (mMixerBufferState == MIXED) {
+ mMixerBufferState = UNDEFINED;
}
//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 * FCC_2 * sizeof(short));
- mixBufferState = ZEROED;
+ if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) {
+ if (mMixerBufferState == UNDEFINED) {
+ memset(mMixerBuffer, 0, mMixerBufferSize);
+ mMixerBufferState = ZEROED;
+ }
+ void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
+ if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
+ memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat,
+ frameCount * Format_channelCount(format));
}
// if non-NULL, then duplicate write() to this non-blocking sink
NBAIO_Sink* teeSink;
if ((teeSink = current->mTeeSink) != NULL) {
- (void) teeSink->write(mixBuffer, frameCount);
+ (void) teeSink->write(mMixerBuffer, frameCount);
}
// FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
// but this code should be modified to handle both non-blocking and blocking sinks
dumpState->mWriteSequence++;
ATRACE_BEGIN("write");
- ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
+ ssize_t framesWritten = outputSink->write(buffer, frameCount);
ATRACE_END();
dumpState->mWriteSequence++;
if (framesWritten >= 0) {
@@ -461,7 +491,7 @@
void FastMixerDumpState::dump(int fd) const
{
if (mCommand == FastMixerState::INITIAL) {
- fdprintf(fd, " FastMixer not initialized\n");
+ dprintf(fd, " FastMixer not initialized\n");
return;
}
#define COMMAND_MAX 32
@@ -495,10 +525,10 @@
double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
(mMeasuredWarmupTs.tv_nsec / 1000000.0);
double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
- fdprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n"
- " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
- " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
- " mixPeriod=%.2f ms\n",
+ dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+ " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
+ " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
+ " mixPeriod=%.2f ms\n",
string, mWriteSequence, mFramesWritten,
mNumTracks, mWriteErrors, mUnderruns, mOverruns,
mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
@@ -550,26 +580,26 @@
#endif
}
if (n) {
- fdprintf(fd, " Simple moving statistics over last %.1f seconds:\n",
- wall.n() * mixPeriodSec);
- fdprintf(fd, " wall clock time in ms per mix cycle:\n"
- " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
- wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
- wall.stddev()*1e-6);
- fdprintf(fd, " raw CPU load in us per mix cycle:\n"
- " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
- loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
- loadNs.stddev()*1e-3);
+ dprintf(fd, " Simple moving statistics over last %.1f seconds:\n",
+ wall.n() * mixPeriodSec);
+ dprintf(fd, " wall clock time in ms per mix cycle:\n"
+ " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+ wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
+ wall.stddev()*1e-6);
+ dprintf(fd, " raw CPU load in us per mix cycle:\n"
+ " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+ loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
+ loadNs.stddev()*1e-3);
} else {
- fdprintf(fd, " No FastMixer statistics available currently\n");
+ dprintf(fd, " No FastMixer statistics available currently\n");
}
#ifdef CPU_FREQUENCY_STATISTICS
- fdprintf(fd, " CPU clock frequency in MHz:\n"
- " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
- kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
- fdprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
- " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
- loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
+ dprintf(fd, " CPU clock frequency in MHz:\n"
+ " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+ kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
+ dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
+ " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
+ loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
#endif
if (tail != NULL) {
qsort(tail, n, sizeof(uint32_t), compare_uint32_t);
@@ -580,12 +610,12 @@
left.sample(tail[i]);
right.sample(tail[n - (i + 1)]);
}
- fdprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
- " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
- " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
- left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
- right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
- right.stddev()*1e-6);
+ dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
+ " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
+ " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+ left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
+ right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
+ right.stddev()*1e-6);
delete[] tail;
}
#endif
@@ -595,9 +625,9 @@
// Instead we always display all tracks, with an indication
// of whether we think the track is active.
uint32_t trackMask = mTrackMask;
- fdprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
+ dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
FastMixerState::kMaxFastTracks, trackMask);
- fdprintf(fd, " Index Active Full Partial Empty Recent Ready\n");
+ dprintf(fd, " Index Active Full Partial Empty Recent Ready\n");
for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
bool isActive = trackMask & 1;
const FastTrackDump *ftDump = &mTracks[i];
@@ -617,7 +647,7 @@
mostRecent = "?";
break;
}
- fdprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
+ dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
(underruns.mBitFields.mFull) & UNDERRUN_MASK,
(underruns.mBitFields.mPartial) & UNDERRUN_MASK,
(underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 981c1a7..4671670 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -17,13 +17,11 @@
#ifndef ANDROID_AUDIO_FAST_MIXER_H
#define ANDROID_AUDIO_FAST_MIXER_H
+#include <linux/futex.h>
+#include <sys/syscall.h>
#include <utils/Debug.h>
-#if 1 // FIXME move to where used
-extern "C" {
-#include "../private/bionic_futex.h"
-}
-#endif
#include "FastThread.h"
+#include <utils/Thread.h>
#include "StateQueue.h"
#include "FastMixerState.h"
#include "FastMixerDumpState.h"
@@ -63,8 +61,16 @@
NBAIO_Sink *outputSink;
int outputSinkGen;
AudioMixer* mixer;
- short *mixBuffer;
- enum {UNDEFINED, MIXED, ZEROED} mixBufferState;
+
+ // mSinkBuffer audio format is stored in format.mFormat.
+ void* mSinkBuffer; // used for mixer output format translation
+ // if sink format is different than mixer output.
+ size_t mSinkBufferSize;
+ void* mMixerBuffer; // mixer output buffer.
+ size_t mMixerBufferSize;
+ audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT).
+
+ enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState;
NBAIO_Format format;
unsigned sampleRate;
int fastTracksGen;
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 8e6d0d4..3aa8dad 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -20,7 +20,7 @@
FastTrack::FastTrack() :
mBufferProvider(NULL), mVolumeProvider(NULL),
- mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0)
+ mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mFormat(AUDIO_FORMAT_INVALID), mGeneration(0)
{
}
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index e388fb3..661c9ca 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -45,6 +45,7 @@
ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active
VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale
audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO
+ audio_format_t mFormat; // track format
int mGeneration; // increment when any field is assigned
};
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 8a216b3..216dace 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -20,10 +20,9 @@
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
#include <utils/Log.h>
-extern "C" {
-#include "../private/bionic_futex.h"
-}
#include <utils/Trace.h>
#include "FastThread.h"
@@ -157,7 +156,7 @@
ALOG_ASSERT(coldFutexAddr != NULL);
int32_t old = android_atomic_dec(coldFutexAddr);
if (old <= 0) {
- __futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
+ syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
}
int policy = sched_getscheduler(0);
if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
new file mode 100644
index 0000000..6d84296
--- /dev/null
+++ b/services/audioflinger/PatchPanel.cpp
@@ -0,0 +1,441 @@
+/*
+**
+** Copyright 2014, 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 "AudioFlinger::PatchPanel"
+//#define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <utils/Log.h>
+#include <audio_utils/primitives.h>
+
+#include "AudioFlinger.h"
+#include "ServiceUtilities.h"
+#include <media/AudioParameter.h>
+
+// ----------------------------------------------------------------------------
+
+// Note: the following macro is used for extremely verbose logging message. In
+// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
+// 0; but one side effect of this is to turn all LOGV's as well. Some messages
+// are so verbose that we want to suppress them even when we have ALOG_ASSERT
+// turned on. Do not uncomment the #def below unless you really know what you
+// are doing and want to see all of the extremely verbose messages.
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+namespace android {
+
+/* List connected audio ports and their attributes */
+status_t AudioFlinger::listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->listAudioPorts(num_ports, ports);
+ }
+ return NO_INIT;
+}
+
+/* Get supported attributes for a given audio port */
+status_t AudioFlinger::getAudioPort(struct audio_port *port)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->getAudioPort(port);
+ }
+ return NO_INIT;
+}
+
+
+/* Connect a patch between several source and sink ports */
+status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->createAudioPatch(patch, handle);
+ }
+ return NO_INIT;
+}
+
+/* Disconnect a patch */
+status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->releaseAudioPatch(handle);
+ }
+ return NO_INIT;
+}
+
+
+/* List connected audio ports and they attributes */
+status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->listAudioPatches(num_patches, patches);
+ }
+ return NO_INIT;
+}
+
+/* Set audio port configuration */
+status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
+{
+ Mutex::Autolock _l(mLock);
+ if (mPatchPanel != 0) {
+ return mPatchPanel->setAudioPortConfig(config);
+ }
+ return NO_INIT;
+}
+
+
+AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger)
+ : mAudioFlinger(audioFlinger)
+{
+}
+
+AudioFlinger::PatchPanel::~PatchPanel()
+{
+}
+
+/* List connected audio ports and their attributes */
+status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
+ struct audio_port *ports __unused)
+{
+ ALOGV("listAudioPorts");
+ return NO_ERROR;
+}
+
+/* Get supported attributes for a given audio port */
+status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
+{
+ ALOGV("getAudioPort");
+ return NO_ERROR;
+}
+
+
+/* Connect a patch between several source and sink ports */
+status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
+ patch->num_sources, patch->num_sinks, *handle);
+ status_t status = NO_ERROR;
+
+ audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
+
+ sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+ if (audioflinger == 0) {
+ return NO_INIT;
+ }
+ if (handle == NULL || patch == NULL) {
+ return BAD_VALUE;
+ }
+ // limit number of sources to 1 for now
+ if (patch->num_sources == 0 || patch->num_sources > 1 ||
+ patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ return BAD_VALUE;
+ }
+
+ for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
+ if (*handle == mPatches[index]->mHandle) {
+ ALOGV("createAudioPatch() removing patch handle %d", *handle);
+ halHandle = mPatches[index]->mHalHandle;
+ mPatches.removeAt(index);
+ break;
+ }
+ }
+
+ switch (patch->sources[0].type) {
+ case AUDIO_PORT_TYPE_DEVICE: {
+ // limit number of sinks to 1 for now
+ if (patch->num_sinks > 1) {
+ return BAD_VALUE;
+ }
+ audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ if (index < 0) {
+ ALOGW("createAudioPatch() bad src hw module %d", src_module);
+ return BAD_VALUE;
+ }
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ // reject connection to different sink types
+ if (patch->sinks[i].type != patch->sinks[0].type) {
+ ALOGW("createAudioPatch() different sink types in same patch not supported");
+ return BAD_VALUE;
+ }
+ // limit to connections between sinks and sources on same HW module
+ if (patch->sinks[i].ext.mix.hw_module != src_module) {
+ ALOGW("createAudioPatch() cannot connect source on module %d to "
+ "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module);
+ return BAD_VALUE;
+ }
+
+ // limit to connections between devices and output streams for HAL before 3.0
+ if ((audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
+ (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
+ ALOGW("createAudioPatch() invalid sink type %d for device source",
+ patch->sinks[i].type);
+ return BAD_VALUE;
+ }
+ }
+
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+ patch->sinks[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("createAudioPatch() bad capture I/O handle %d",
+ patch->sinks[0].ext.mix.handle);
+ return BAD_VALUE;
+ }
+ status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ } else {
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ status = hwDevice->create_audio_patch(hwDevice,
+ patch->num_sources,
+ patch->sources,
+ patch->num_sinks,
+ patch->sinks,
+ &halHandle);
+ }
+ } else {
+ sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+ patch->sinks[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("createAudioPatch() bad capture I/O handle %d",
+ patch->sinks[0].ext.mix.handle);
+ return BAD_VALUE;
+ }
+ AudioParameter param;
+ param.addInt(String8(AudioParameter::keyRouting),
+ (int)patch->sources[0].ext.device.type);
+ param.addInt(String8(AudioParameter::keyInputSource),
+ (int)patch->sinks[0].ext.mix.usecase.source);
+
+ ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
+ param.toString().string());
+ status = thread->setParameters(param.toString());
+ }
+ } break;
+ case AUDIO_PORT_TYPE_MIX: {
+ audio_module_handle_t src_module = patch->sources[0].ext.mix.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ if (index < 0) {
+ ALOGW("createAudioPatch() bad src hw module %d", src_module);
+ return BAD_VALUE;
+ }
+ // limit to connections between devices and output streams
+ for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGW("createAudioPatch() invalid sink type %d for bus source",
+ patch->sinks[i].type);
+ return BAD_VALUE;
+ }
+ // limit to connections between sinks and sources on same HW module
+ if (patch->sinks[i].ext.device.hw_module != src_module) {
+ return BAD_VALUE;
+ }
+ }
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ sp<ThreadBase> thread =
+ audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("createAudioPatch() bad playback I/O handle %d",
+ patch->sources[0].ext.mix.handle);
+ return BAD_VALUE;
+ }
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+ } else {
+ audio_devices_t type = AUDIO_DEVICE_NONE;
+ for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ type |= patch->sinks[i].ext.device.type;
+ }
+ AudioParameter param;
+ param.addInt(String8(AudioParameter::keyRouting), (int)type);
+ status = thread->setParameters(param.toString());
+ }
+
+ } break;
+ default:
+ return BAD_VALUE;
+ }
+ ALOGV("createAudioPatch() status %d", status);
+ if (status == NO_ERROR) {
+ *handle = audioflinger->nextUniqueId();
+ Patch *newPatch = new Patch(patch);
+ newPatch->mHandle = *handle;
+ newPatch->mHalHandle = halHandle;
+ mPatches.add(newPatch);
+ ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
+ }
+ return status;
+}
+
+/* Disconnect a patch */
+status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
+{
+ ALOGV("releaseAudioPatch handle %d", handle);
+ status_t status = NO_ERROR;
+ size_t index;
+
+ sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+ if (audioflinger == 0) {
+ return NO_INIT;
+ }
+
+ for (index = 0; index < mPatches.size(); index++) {
+ if (handle == mPatches[index]->mHandle) {
+ break;
+ }
+ }
+ if (index == mPatches.size()) {
+ return BAD_VALUE;
+ }
+
+ struct audio_patch *patch = &mPatches[index]->mAudioPatch;
+
+ switch (patch->sources[0].type) {
+ case AUDIO_PORT_TYPE_DEVICE: {
+ audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ if (index < 0) {
+ ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+ status = BAD_VALUE;
+ break;
+ }
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+ patch->sinks[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("createAudioPatch() bad capture I/O handle %d",
+ patch->sinks[0].ext.mix.handle);
+ status = BAD_VALUE;
+ break;
+ }
+ status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
+ } else {
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ status = hwDevice->release_audio_patch(hwDevice, mPatches[index]->mHalHandle);
+ }
+ } else {
+ sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+ patch->sinks[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("releaseAudioPatch() bad capture I/O handle %d",
+ patch->sinks[0].ext.mix.handle);
+ status = BAD_VALUE;
+ break;
+ }
+ AudioParameter param;
+ param.addInt(String8(AudioParameter::keyRouting), 0);
+ ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
+ param.toString().string());
+ status = thread->setParameters(param.toString());
+ }
+ } break;
+ case AUDIO_PORT_TYPE_MIX: {
+ audio_module_handle_t src_module = patch->sources[0].ext.mix.hw_module;
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+ if (index < 0) {
+ ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+ status = BAD_VALUE;
+ break;
+ }
+ sp<ThreadBase> thread =
+ audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+ if (thread == 0) {
+ ALOGW("releaseAudioPatch() bad playback I/O handle %d",
+ patch->sources[0].ext.mix.handle);
+ status = BAD_VALUE;
+ break;
+ }
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
+ } else {
+ AudioParameter param;
+ param.addInt(String8(AudioParameter::keyRouting), (int)0);
+ status = thread->setParameters(param.toString());
+ }
+ } break;
+ default:
+ status = BAD_VALUE;
+ break;
+ }
+
+ delete (mPatches[index]);
+ mPatches.removeAt(index);
+ return status;
+}
+
+
+/* List connected audio ports and they attributes */
+status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
+ struct audio_patch *patches __unused)
+{
+ ALOGV("listAudioPatches");
+ return NO_ERROR;
+}
+
+/* Set audio port configuration */
+status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
+{
+ ALOGV("setAudioPortConfig");
+ status_t status = NO_ERROR;
+
+ sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+ if (audioflinger == 0) {
+ return NO_INIT;
+ }
+
+ audio_module_handle_t module;
+ if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+ module = config->ext.device.hw_module;
+ } else {
+ module = config->ext.mix.hw_module;
+ }
+
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
+ if (index < 0) {
+ ALOGW("setAudioPortConfig() bad hw module %d", module);
+ return BAD_VALUE;
+ }
+
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ return hwDevice->set_audio_port_config(hwDevice, config);
+ } else {
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+
+}; // namespace android
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
new file mode 100644
index 0000000..7f78621
--- /dev/null
+++ b/services/audioflinger/PatchPanel.h
@@ -0,0 +1,60 @@
+/*
+**
+** Copyright 2014, 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 INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+class PatchPanel : public RefBase {
+public:
+ PatchPanel(const sp<AudioFlinger>& audioFlinger);
+ virtual ~PatchPanel();
+
+ /* List connected audio ports and their attributes */
+ status_t listAudioPorts(unsigned int *num_ports,
+ struct audio_port *ports);
+
+ /* Get supported attributes for a given audio port */
+ status_t getAudioPort(struct audio_port *port);
+
+ /* Create a patch between several source and sink ports */
+ status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+
+ /* Release a patch */
+ status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+ /* List connected audio devices and they attributes */
+ status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches);
+
+ /* Set audio port configuration */
+ status_t setAudioPortConfig(const struct audio_port_config *config);
+
+ class Patch {
+ public:
+ Patch(const struct audio_patch *patch) :
+ mAudioPatch(*patch), mHandle(0), mHalHandle(0) {}
+
+ struct audio_patch mAudioPatch;
+ audio_patch_handle_t mHandle;
+ audio_patch_handle_t mHalHandle;
+ };
+private:
+ const wp<AudioFlinger> mAudioFlinger;
+ SortedVector <Patch *> mPatches;
+};
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 152455d..8246fef 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -59,6 +59,13 @@
return ok;
}
+bool modifyAudioRoutingAllowed() {
+ static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING");
+ bool ok = checkCallingPermission(sModifyAudioRoutingAllowed);
+ if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING");
+ return ok;
+}
+
bool dumpAllowed() {
// don't optimize for same pid, since mediaserver never dumps itself
static const String16 sDump("android.permission.DUMP");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 531bc56..df6f6f4 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -24,6 +24,7 @@
bool captureAudioOutputAllowed();
bool captureHotwordAllowed();
bool settingsAllowed();
+bool modifyAudioRoutingAllowed();
bool dumpAllowed();
}
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 48399c0..7e01c9f 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -28,12 +28,12 @@
#ifdef STATE_QUEUE_DUMP
void StateQueueObserverDump::dump(int fd)
{
- fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
+ dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
}
void StateQueueMutatorDump::dump(int fd)
{
- fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
+ dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
mPushDirty, mPushAck, mBlockedSequence);
}
#endif
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 0d5cd0c..6f4505e 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -16,12 +16,14 @@
#include "Configuration.h"
#include "FastMixerState.h"
+#include "FastCaptureState.h"
#include "StateQueue.h"
// FIXME hack for gcc
namespace android {
-template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+template class StateQueue<FastCaptureState>; // typedef FastCaptureStateQueue
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ce08ff1..d08c966 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -38,6 +38,7 @@
#include <audio_utils/minifloat.h>
// NBAIO implementations
+#include <media/nbaio/AudioStreamInSource.h>
#include <media/nbaio/AudioStreamOutSink.h>
#include <media/nbaio/MonoPipe.h>
#include <media/nbaio/MonoPipeReader.h>
@@ -53,6 +54,7 @@
#include "AudioFlinger.h"
#include "AudioMixer.h"
#include "FastMixer.h"
+#include "FastCapture.h"
#include "ServiceUtilities.h"
#include "SchedulingPolicyService.h"
@@ -131,9 +133,17 @@
// up large writes into smaller ones, and the wrapper would need to deal with scheduler.
} kUseFastMixer = FastMixer_Static;
+// Whether to use fast capture
+static const enum {
+ FastCapture_Never, // never initialize or use: for debugging only
+ FastCapture_Always, // always initialize and use, even if not needed: for debugging only
+ FastCapture_Static, // initialize if needed, then use all the time if initialized
+} kUseFastCapture = FastCapture_Static;
+
// Priorities for requestPriority
static const int kPriorityAudioApp = 2;
static const int kPriorityFastMixer = 3;
+static const int kPriorityFastCapture = 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.
@@ -142,8 +152,17 @@
// 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.
+
+// This is the default value, if not specified by property.
static const int kFastTrackMultiplier = 2;
+// The minimum and maximum allowed values
+static const int kFastTrackMultiplierMin = 1;
+static const int kFastTrackMultiplierMax = 2;
+
+// The actual value to use, which can be specified per-device via property af.fast_track_multiplier.
+static int sFastTrackMultiplier = kFastTrackMultiplier;
+
// See Thread::readOnlyHeap().
// Initially this heap is used to allocate client buffers for "fast" AudioRecord.
// Eventually it will be the single buffer that FastCapture writes into via HAL read(),
@@ -152,6 +171,22 @@
// ----------------------------------------------------------------------------
+static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT;
+
+static void sFastTrackMultiplierInit()
+{
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("af.fast_track_multiplier", value, NULL) > 0) {
+ char *endptr;
+ unsigned long ul = strtoul(value, &endptr, 0);
+ if (*endptr == '\0' && kFastTrackMultiplierMin <= ul && ul <= kFastTrackMultiplierMax) {
+ sFastTrackMultiplier = (int) ul;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
#ifdef ADD_BATTERY_DATA
// To collect the amplifier usage
static void addBatteryData(uint32_t params) {
@@ -401,6 +436,30 @@
return sendConfigEvent_l(configEvent);
}
+status_t AudioFlinger::ThreadBase::sendCreateAudioPatchConfigEvent(
+ const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ Mutex::Autolock _l(mLock);
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new CreateAudioPatchConfigEvent(*patch, *handle);
+ status_t status = sendConfigEvent_l(configEvent);
+ if (status == NO_ERROR) {
+ CreateAudioPatchConfigEventData *data =
+ (CreateAudioPatchConfigEventData *)configEvent->mData.get();
+ *handle = data->mHandle;
+ }
+ return status;
+}
+
+status_t AudioFlinger::ThreadBase::sendReleaseAudioPatchConfigEvent(
+ const audio_patch_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ sp<ConfigEvent> configEvent = (ConfigEvent *)new ReleaseAudioPatchConfigEvent(handle);
+ return sendConfigEvent_l(configEvent);
+}
+
+
// post condition: mConfigEvents.isEmpty()
void AudioFlinger::ThreadBase::processConfigEvents_l()
{
@@ -431,6 +490,16 @@
configChanged = true;
}
} break;
+ case CFG_EVENT_CREATE_AUDIO_PATCH: {
+ CreateAudioPatchConfigEventData *data =
+ (CreateAudioPatchConfigEventData *)event->mData.get();
+ event->mStatus = createAudioPatch_l(&data->mPatch, &data->mHandle);
+ } break;
+ case CFG_EVENT_RELEASE_AUDIO_PATCH: {
+ ReleaseAudioPatchConfigEventData *data =
+ (ReleaseAudioPatchConfigEventData *)event->mData.get();
+ event->mStatus = releaseAudioPatch_l(data->mHandle);
+ } break;
default:
ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
break;
@@ -505,30 +574,30 @@
bool locked = AudioFlinger::dumpTryLock(mLock);
if (!locked) {
- fdprintf(fd, "thread %p maybe dead locked\n", this);
+ dprintf(fd, "thread %p maybe dead locked\n", this);
}
- fdprintf(fd, " I/O handle: %d\n", mId);
- fdprintf(fd, " TID: %d\n", getTid());
- fdprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no");
- fdprintf(fd, " Sample rate: %u\n", mSampleRate);
- fdprintf(fd, " HAL frame count: %zu\n", mFrameCount);
- fdprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize);
- fdprintf(fd, " Channel Count: %u\n", mChannelCount);
- fdprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask,
+ dprintf(fd, " I/O handle: %d\n", mId);
+ dprintf(fd, " TID: %d\n", getTid());
+ dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no");
+ dprintf(fd, " Sample rate: %u\n", mSampleRate);
+ dprintf(fd, " HAL frame count: %zu\n", mFrameCount);
+ dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize);
+ dprintf(fd, " Channel Count: %u\n", mChannelCount);
+ dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask,
channelMaskToString(mChannelMask, mType != RECORD).string());
- fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
- fdprintf(fd, " Frame size: %zu\n", mFrameSize);
- fdprintf(fd, " Pending config events:");
+ dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
+ dprintf(fd, " Frame size: %zu\n", mFrameSize);
+ dprintf(fd, " Pending config events:");
size_t numConfig = mConfigEvents.size();
if (numConfig) {
for (size_t i = 0; i < numConfig; i++) {
mConfigEvents[i]->dump(buffer, SIZE);
- fdprintf(fd, "\n %s", buffer);
+ dprintf(fd, "\n %s", buffer);
}
- fdprintf(fd, "\n");
+ dprintf(fd, "\n");
} else {
- fdprintf(fd, " none\n");
+ dprintf(fd, " none\n");
}
if (locked) {
@@ -1191,15 +1260,15 @@
// These values are "raw"; they will wrap around. See prepareTracks_l() for a better way.
FastTrackUnderruns underruns = getFastTrackUnderruns(0);
- fdprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n",
+ dprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n",
underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
size_t numtracks = mTracks.size();
size_t numactive = mActiveTracks.size();
- fdprintf(fd, " %d Tracks", numtracks);
+ dprintf(fd, " %d Tracks", numtracks);
size_t numactiveseen = 0;
if (numtracks) {
- fdprintf(fd, " of which %d are active\n", numactive);
+ dprintf(fd, " of which %d are active\n", numactive);
Track::appendDumpHeader(result);
for (size_t i = 0; i < numtracks; ++i) {
sp<Track> track = mTracks[i];
@@ -1231,22 +1300,21 @@
}
write(fd, result.string(), result.size());
-
}
void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
- fdprintf(fd, "\nOutput thread %p:\n", this);
- fdprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount);
- fdprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
- fdprintf(fd, " Total writes: %d\n", mNumWrites);
- fdprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites);
- fdprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no");
- fdprintf(fd, " Suspend count: %d\n", mSuspended);
- fdprintf(fd, " Sink buffer : %p\n", mSinkBuffer);
- fdprintf(fd, " Mixer buffer: %p\n", mMixerBuffer);
- fdprintf(fd, " Effect buffer: %p\n", mEffectBuffer);
- fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask);
+ dprintf(fd, "\nOutput thread %p:\n", this);
+ dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount);
+ dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+ dprintf(fd, " Total writes: %d\n", mNumWrites);
+ dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites);
+ dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no");
+ dprintf(fd, " Suspend count: %d\n", mSuspended);
+ dprintf(fd, " Sink buffer : %p\n", mSinkBuffer);
+ dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer);
+ dprintf(fd, " Effect buffer: %p\n", mEffectBuffer);
+ dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask);
dumpBase(fd, args);
}
@@ -1322,7 +1390,12 @@
) {
// if frameCount not specified, then it defaults to fast mixer (HAL) frame count
if (frameCount == 0) {
- frameCount = mFrameCount * kFastTrackMultiplier;
+ // read the fast track multiplier property the first time it is needed
+ int ok = pthread_once(&sFastTrackMultiplierOnce, sFastTrackMultiplierInit);
+ if (ok != 0) {
+ ALOGE("%s pthread_once failed: %d", __func__, ok);
+ }
+ frameCount = mFrameCount * sFastTrackMultiplier;
}
ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
frameCount, mFrameCount);
@@ -2594,6 +2667,47 @@
}
return INVALID_OPERATION;
}
+
+status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ status_t status = NO_ERROR;
+ if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ // store new device and send to effects
+ audio_devices_t type = AUDIO_DEVICE_NONE;
+ for (unsigned int i = 0; i < patch->num_sinks; i++) {
+ type |= patch->sinks[i].ext.device.type;
+ }
+ mOutDevice = type;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mOutDevice);
+ }
+
+ audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
+ status = hwDevice->create_audio_patch(hwDevice,
+ patch->num_sources,
+ patch->sources,
+ patch->num_sinks,
+ patch->sinks,
+ handle);
+ } else {
+ ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+ status_t status = NO_ERROR;
+ if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
+ status = hwDevice->release_audio_patch(hwDevice, handle);
+ } else {
+ ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+ }
+ return status;
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
@@ -2640,9 +2754,27 @@
break;
}
if (initFastMixer) {
+ audio_format_t fastMixerFormat;
+ if (mMixerBufferEnabled && mEffectBufferEnabled) {
+ fastMixerFormat = AUDIO_FORMAT_PCM_FLOAT;
+ } else {
+ fastMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
+ }
+ if (mFormat != fastMixerFormat) {
+ // change our Sink format to accept our intermediate precision
+ mFormat = fastMixerFormat;
+ free(mSinkBuffer);
+ mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat);
+ const size_t sinkBufferSize = mNormalFrameCount * mFrameSize;
+ (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize);
+ }
// create a MonoPipe to connect our submix to FastMixer
NBAIO_Format format = mOutputSink->format();
+ // adjust format to match that of the Fast Mixer
+ format.mFormat = fastMixerFormat;
+ format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount;
+
// This pipe depth compensates for scheduling latency of the normal mixer thread.
// When it wakes up after a maximum latency, it runs a few cycles quickly before
// finally blocking. Note the pipe implementation rounds up the request to a power of 2.
@@ -2683,6 +2815,8 @@
// wrap the source side of the MonoPipe to make it an AudioBufferProvider
fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe));
fastTrack->mVolumeProvider = NULL;
+ fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer
+ fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
fastTrack->mGeneration++;
state->mFastTracksGen++;
state->mTrackMask = 1;
@@ -2752,7 +2886,7 @@
if (state->mCommand == FastMixerState::COLD_IDLE) {
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
- (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ (void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
}
state->mCommand = FastMixerState::EXIT;
@@ -2809,7 +2943,7 @@
if (state->mCommand == FastMixerState::COLD_IDLE) {
int32_t old = android_atomic_inc(&mFastMixerFutex);
if (old == -1) {
- (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ (void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
}
#ifdef AUDIO_WATCHDOG
if (mAudioWatchdog != 0) {
@@ -3135,6 +3269,7 @@
fastTrack->mBufferProvider = eabp;
fastTrack->mVolumeProvider = vp;
fastTrack->mChannelMask = track->mChannelMask;
+ fastTrack->mFormat = track->mFormat;
fastTrack->mGeneration++;
state->mTrackMask |= 1 << j;
didModify = true;
@@ -3244,9 +3379,11 @@
}
// compute volume for this track
- uint32_t vl, vr, va;
+ uint32_t vl, vr; // in U8.24 integer format
+ float vlf, vrf, vaf; // in [0.0, 1.0] float format
if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
- vl = vr = va = 0;
+ vl = vr = 0;
+ vlf = vrf = vaf = 0.;
if (track->isPausing()) {
track->setPaused();
}
@@ -3257,8 +3394,8 @@
float v = masterVolume * typeVolume;
AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
- float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
- float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+ vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+ vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
// track volumes come from shared memory, so can't be trusted and must be clamped
if (vlf > GAIN_FLOAT_UNITY) {
ALOGV("Track left volume out of range: %.3g", vlf);
@@ -3269,20 +3406,22 @@
vrf = GAIN_FLOAT_UNITY;
}
// now apply the master volume and stream type volume
- // FIXME we're losing the wonderful dynamic range in the minifloat representation
- float v8_24 = v * (MAX_GAIN_INT * MAX_GAIN_INT);
- vl = (uint32_t) (v8_24 * vlf);
- vr = (uint32_t) (v8_24 * vrf);
+ vlf *= v;
+ vrf *= v;
// assuming master volume and stream type volume each go up to 1.0,
- // vl and vr are now in 8.24 format
-
+ // then derive vl and vr as U8.24 versions for the effect chain
+ const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;
+ vl = (uint32_t) (scaleto8_24 * vlf);
+ vr = (uint32_t) (scaleto8_24 * vrf);
+ // vl and vr are now in U8.24 format
uint16_t sendLevel = proxy->getSendLevel_U4_12();
// send level comes from shared memory and so may be corrupt
if (sendLevel > MAX_GAIN_INT) {
ALOGV("Track send level out of range: %04X", sendLevel);
sendLevel = MAX_GAIN_INT;
}
- va = (uint32_t)(v * sendLevel);
+ // vaf is represented as [0.0, 1.0] float by rescaling sendLevel
+ vaf = v * sendLevel * (1. / MAX_GAIN_INT);
}
// Delegate volume control to effect in track effect chain if needed
@@ -3299,29 +3438,13 @@
track->mHasVolumeController = false;
}
- // FIXME Use float
- // Convert volumes from 8.24 to 4.12 format
- // This additional clamping is needed in case chain->setVolume_l() overshot
- vl = (vl + (1 << 11)) >> 12;
- if (vl > MAX_GAIN_INT) {
- vl = MAX_GAIN_INT;
- }
- vr = (vr + (1 << 11)) >> 12;
- if (vr > MAX_GAIN_INT) {
- vr = MAX_GAIN_INT;
- }
-
- if (va > MAX_GAIN_INT) {
- va = MAX_GAIN_INT; // va is uint32_t, so no need to check for -
- }
-
// XXX: these things DON'T need to be done each time
mAudioMixer->setBufferProvider(name, track);
mAudioMixer->enable(name);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)(uintptr_t)vl);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)(uintptr_t)vr);
- mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)(uintptr_t)va);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
+ mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
mAudioMixer->setParameter(
name,
AudioMixer::TRACK,
@@ -3526,9 +3649,10 @@
}
// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId)
{
- return mAudioMixer->getTrackName(channelMask, sessionId);
+ return mAudioMixer->getTrackName(channelMask, format, sessionId);
}
// deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -3641,7 +3765,8 @@
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);
+ int name = getTrackName_l(mTracks[i]->mChannelMask,
+ mTracks[i]->mFormat, mTracks[i]->mSessionId);
if (name < 0) {
break;
}
@@ -3673,7 +3798,7 @@
PlaybackThread::dumpInternals(fd, args);
- fdprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
+ dprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
// Make a non-atomic copy of fast mixer dump state so it won't change underneath us
const FastMixerDumpState copy(mFastMixerDumpState);
@@ -3932,7 +4057,7 @@
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
- int sessionId __unused)
+ audio_format_t format __unused, int sessionId __unused)
{
return 0;
}
@@ -4633,16 +4758,151 @@
#endif
, mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize,
"RecordThreadRO", MemoryHeapBase::READ_ONLY))
+ // mFastCapture below
+ , mFastCaptureFutex(0)
+ // mInputSource
+ // mPipeSink
+ // mPipeSource
+ , mPipeFramesP2(0)
+ // mPipeMemory
+ // mFastCaptureNBLogWriter
+ , mFastTrackAvail(true)
{
snprintf(mName, kNameLength, "AudioIn_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
readInputParameters_l();
+
+ // create an NBAIO source for the HAL input stream, and negotiate
+ mInputSource = new AudioStreamInSource(input->stream);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)};
+ ssize_t index = mInputSource->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+
+ // initialize fast capture depending on configuration
+ bool initFastCapture;
+ switch (kUseFastCapture) {
+ case FastCapture_Never:
+ initFastCapture = false;
+ break;
+ case FastCapture_Always:
+ initFastCapture = true;
+ break;
+ case FastCapture_Static:
+ uint32_t primaryOutputSampleRate;
+ {
+ AutoMutex _l(audioFlinger->mHardwareLock);
+ primaryOutputSampleRate = audioFlinger->mPrimaryOutputSampleRate;
+ }
+ initFastCapture =
+ // either capture sample rate is same as (a reasonable) primary output sample rate
+ (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) &&
+ (mSampleRate == primaryOutputSampleRate)) ||
+ // or primary output sample rate is unknown, and capture sample rate is reasonable
+ ((primaryOutputSampleRate == 0) &&
+ ((mSampleRate == 44100 || mSampleRate == 48000)))) &&
+ // and the buffer size is < 10 ms
+ (mFrameCount * 1000) / mSampleRate < 10;
+ break;
+ // case FastCapture_Dynamic:
+ }
+
+ if (initFastCapture) {
+ // create a Pipe for FastMixer to write to, and for us and fast tracks to read from
+ NBAIO_Format format = mInputSource->format();
+ size_t pipeFramesP2 = roundup(mFrameCount * 8);
+ size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
+ void *pipeBuffer;
+ const sp<MemoryDealer> roHeap(readOnlyHeap());
+ sp<IMemory> pipeMemory;
+ if ((roHeap == 0) ||
+ (pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
+ (pipeBuffer = pipeMemory->pointer()) == NULL) {
+ ALOGE("not enough memory for pipe buffer size=%zu", pipeSize);
+ goto failed;
+ }
+ // pipe will be shared directly with fast clients, so clear to avoid leaking old information
+ memset(pipeBuffer, 0, pipeSize);
+ Pipe *pipe = new Pipe(pipeFramesP2, format, pipeBuffer);
+ const NBAIO_Format offers[1] = {format};
+ size_t numCounterOffers = 0;
+ ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mPipeSink = pipe;
+ PipeReader *pipeReader = new PipeReader(*pipe);
+ numCounterOffers = 0;
+ index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mPipeSource = pipeReader;
+ mPipeFramesP2 = pipeFramesP2;
+ mPipeMemory = pipeMemory;
+
+ // create fast capture
+ mFastCapture = new FastCapture();
+ FastCaptureStateQueue *sq = mFastCapture->sq();
+#ifdef STATE_QUEUE_DUMP
+ // FIXME
+#endif
+ FastCaptureState *state = sq->begin();
+ state->mCblk = NULL;
+ state->mInputSource = mInputSource.get();
+ state->mInputSourceGen++;
+ state->mPipeSink = pipe;
+ state->mPipeSinkGen++;
+ state->mFrameCount = mFrameCount;
+ state->mCommand = FastCaptureState::COLD_IDLE;
+ // already done in constructor initialization list
+ //mFastCaptureFutex = 0;
+ state->mColdFutexAddr = &mFastCaptureFutex;
+ state->mColdGen++;
+ state->mDumpState = &mFastCaptureDumpState;
+#ifdef TEE_SINK
+ // FIXME
+#endif
+ mFastCaptureNBLogWriter = audioFlinger->newWriter_l(kFastCaptureLogSize, "FastCapture");
+ state->mNBLogWriter = mFastCaptureNBLogWriter.get();
+ sq->end();
+ sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+
+ // start the fast capture
+ mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
+ pid_t tid = mFastCapture->getTid();
+ int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ kPriorityFastCapture, getpid_cached, tid, err);
+ }
+
+#ifdef AUDIO_WATCHDOG
+ // FIXME
+#endif
+
+ }
+failed: ;
+
+ // FIXME mNormalSource
}
AudioFlinger::RecordThread::~RecordThread()
{
+ if (mFastCapture != 0) {
+ FastCaptureStateQueue *sq = mFastCapture->sq();
+ FastCaptureState *state = sq->begin();
+ if (state->mCommand == FastCaptureState::COLD_IDLE) {
+ int32_t old = android_atomic_inc(&mFastCaptureFutex);
+ if (old == -1) {
+ (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1);
+ }
+ }
+ state->mCommand = FastCaptureState::EXIT;
+ sq->end();
+ sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+ mFastCapture->join();
+ mFastCapture.clear();
+ }
+ mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter);
mAudioFlinger->unregisterWriter(mNBLogWriter);
delete[] mRsmpInBuffer;
}
@@ -4697,6 +4957,8 @@
// activeTracks accumulates a copy of a subset of mActiveTracks
Vector< sp<RecordTrack> > activeTracks;
+ // reference to the (first and only) fast track
+ sp<RecordTrack> fastTrack;
{ // scope for mLock
Mutex::Autolock _l(mLock);
@@ -4778,6 +5040,11 @@
activeTracks.add(activeTrack);
i++;
+ if (activeTrack->isFastTrack()) {
+ ALOG_ASSERT(!mFastTrackAvail);
+ ALOG_ASSERT(fastTrack == 0);
+ fastTrack = activeTrack;
+ }
}
if (doBroadcast) {
mStartStopCond.broadcast();
@@ -4803,6 +5070,36 @@
effectChains[i]->process_l();
}
+ // Start the fast capture if it's not already running
+ if (mFastCapture != 0) {
+ FastCaptureStateQueue *sq = mFastCapture->sq();
+ FastCaptureState *state = sq->begin();
+ if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME &&
+ (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) {
+ if (state->mCommand == FastCaptureState::COLD_IDLE) {
+ int32_t old = android_atomic_inc(&mFastCaptureFutex);
+ if (old == -1) {
+ (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1);
+ }
+ }
+ state->mCommand = FastCaptureState::READ_WRITE;
+#if 0 // FIXME
+ mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
+ FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
+#endif
+ state->mCblk = fastTrack != 0 ? fastTrack->cblk() : NULL;
+ sq->end();
+ sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+#if 0
+ if (kUseFastCapture == FastCapture_Dynamic) {
+ mNormalSource = mPipeSource;
+ }
+#endif
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+
// Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
// Only the client(s) that are too slow will overrun. But if even the fastest client is too
// slow, then this RecordThread will overrun by not calling HAL read often enough.
@@ -4810,24 +5107,45 @@
// copy to the right place. Permitted because mRsmpInBuffer was over-allocated.
int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1);
- ssize_t bytesRead = mInput->stream->read(mInput->stream,
- &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
- if (bytesRead <= 0) {
- ALOGE("read failed: bytesRead=%d < %u", bytesRead, mBufferSize);
+ ssize_t framesRead;
+
+ // If an NBAIO source is present, use it to read the normal capture's data
+ if (mPipeSource != 0) {
+ size_t framesToRead = mBufferSize / mFrameSize;
+ framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount],
+ framesToRead, AudioBufferProvider::kInvalidPTS);
+ if (framesRead == 0) {
+ // since pipe is non-blocking, simulate blocking input
+ sleepUs = (framesToRead * 1000000LL) / mSampleRate;
+ }
+ // otherwise use the HAL / AudioStreamIn directly
+ } else {
+ ssize_t bytesRead = mInput->stream->read(mInput->stream,
+ &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ if (bytesRead < 0) {
+ framesRead = bytesRead;
+ } else {
+ framesRead = bytesRead / mFrameSize;
+ }
+ }
+
+ if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) {
+ ALOGE("read failed: framesRead=%d", framesRead);
// Force input into standby so that it tries to recover at next read attempt
inputStandBy();
sleepUs = kRecordThreadSleepUs;
+ }
+ if (framesRead <= 0) {
continue;
}
- ALOG_ASSERT((size_t) bytesRead <= mBufferSize);
- size_t framesRead = bytesRead / mFrameSize;
ALOG_ASSERT(framesRead > 0);
+
if (mTeeSink != 0) {
(void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
}
// If destination is non-contiguous, we now correct for reading past end of buffer.
size_t part1 = mRsmpInFramesP2 - rear;
- if (framesRead > part1) {
+ if ((size_t) framesRead > part1) {
memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
(framesRead - part1) * mFrameSize);
}
@@ -4838,6 +5156,11 @@
for (size_t i = 0; i < size; i++) {
activeTrack = activeTracks[i];
+ // skip fast tracks, as those are handled directly by FastCapture
+ if (activeTrack->isFastTrack()) {
+ continue;
+ }
+
enum {
OVERRUN_UNKNOWN,
OVERRUN_TRUE,
@@ -5066,6 +5389,30 @@
void AudioFlinger::RecordThread::inputStandBy()
{
+ // Idle the fast capture if it's currently running
+ if (mFastCapture != 0) {
+ FastCaptureStateQueue *sq = mFastCapture->sq();
+ FastCaptureState *state = sq->begin();
+ if (!(state->mCommand & FastCaptureState::IDLE)) {
+ state->mCommand = FastCaptureState::COLD_IDLE;
+ state->mColdFutexAddr = &mFastCaptureFutex;
+ state->mColdGen++;
+ mFastCaptureFutex = 0;
+ sq->end();
+ // BLOCK_UNTIL_PUSHED would be insufficient, as we need it to stop doing I/O now
+ sq->push(FastCaptureStateQueue::BLOCK_UNTIL_ACKED);
+#if 0
+ if (kUseFastCapture == FastCapture_Dynamic) {
+ // FIXME
+ }
+#endif
+#ifdef AUDIO_WATCHDOG
+ // FIXME
+#endif
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
mInput->stream->common.standby(&mInput->stream->common);
}
@@ -5092,42 +5439,47 @@
// use case: callback handler and frame count is default or at least as large as HAL
(
(tid != -1) &&
- ((frameCount == 0) ||
+ ((frameCount == 0) /*||
+ // FIXME must be equal to pipe depth, so don't allow it to be specified by client
// FIXME not necessarily true, should be native frame count for native SR!
- (frameCount >= mFrameCount))
+ (frameCount >= mFrameCount)*/)
) &&
// PCM data
audio_is_linear_pcm(format) &&
+ // native format
+ (format == mFormat) &&
// mono or stereo
( (channelMask == AUDIO_CHANNEL_IN_MONO) ||
(channelMask == AUDIO_CHANNEL_IN_STEREO) ) &&
- // hardware sample rate
- // FIXME actually the native hardware sample rate
+ // native channel mask
+ (channelMask == mChannelMask) &&
+ // native hardware sample rate
(sampleRate == mSampleRate) &&
// record thread has an associated fast capture
- hasFastCapture()
- // fast capture does not require slots
+ hasFastCapture() &&
+ // there are sufficient fast track slots available
+ mFastTrackAvail
) {
- // if frameCount not specified, then it defaults to fast capture (HAL) frame count
+ // if frameCount not specified, then it defaults to pipe frame count
if (frameCount == 0) {
- // FIXME wrong mFrameCount
- frameCount = mFrameCount * kFastTrackMultiplier;
+ frameCount = mPipeFramesP2;
}
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 "
- "hasFastCapture=%d tid=%d",
+ "hasFastCapture=%d tid=%d mFastTrackAvail=%d",
frameCount, mFrameCount, format,
audio_is_linear_pcm(format),
- channelMask, sampleRate, mSampleRate, hasFastCapture(), tid);
+ channelMask, sampleRate, mSampleRate, hasFastCapture(), tid, mFastTrackAvail);
*flags &= ~IAudioFlinger::TRACK_FAST;
// FIXME It's not clear that we need to enforce this any more, since we have a pipe.
// 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.
+ // FIXME It's not clear how input latency actually matters. Perhaps this should be 0.
uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
size_t mNormalFrameCount = 2048; // FIXME
uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
@@ -5349,6 +5701,10 @@
{
mTracks.remove(track);
// need anything related to effects here?
+ if (track->isFastTrack()) {
+ ALOG_ASSERT(!mFastTrackAvail);
+ mFastTrackAvail = true;
+ }
}
void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
@@ -5360,13 +5716,14 @@
void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
{
- fdprintf(fd, "\nInput thread %p:\n", this);
+ dprintf(fd, "\nInput thread %p:\n", this);
if (mActiveTracks.size() > 0) {
- fdprintf(fd, " Buffer size: %zu bytes\n", mBufferSize);
+ dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize);
} else {
- fdprintf(fd, " No active record clients\n");
+ dprintf(fd, " No active record clients\n");
}
+ dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
dumpBase(fd, args);
}
@@ -5380,9 +5737,9 @@
size_t numtracks = mTracks.size();
size_t numactive = mActiveTracks.size();
size_t numactiveseen = 0;
- fdprintf(fd, " %d Tracks", numtracks);
+ dprintf(fd, " %d Tracks", numtracks);
if (numtracks) {
- fdprintf(fd, " of which %d are active\n", numactive);
+ dprintf(fd, " of which %d are active\n", numactive);
RecordTrack::appendDumpHeader(result);
for (size_t i = 0; i < numtracks ; ++i) {
sp<RecordTrack> track = mTracks[i];
@@ -5396,7 +5753,7 @@
}
}
} else {
- fdprintf(fd, "\n");
+ dprintf(fd, "\n");
}
if (numactiveseen != numactive) {
@@ -5743,4 +6100,61 @@
return 0;
}
+status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ status_t status = NO_ERROR;
+ if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ // store new device and send to effects
+ mInDevice = patch->sources[0].ext.device.type;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mInDevice);
+ }
+
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ if (mTracks.size() > 0) {
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+ }
+ }
+
+ // store new source and send to effects
+ if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
+ mAudioSource = patch->sinks[0].ext.mix.usecase.source;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setAudioSource_l(mAudioSource);
+ }
+ }
+
+ audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
+ status = hwDevice->create_audio_patch(hwDevice,
+ patch->num_sources,
+ patch->sources,
+ patch->num_sinks,
+ patch->sinks,
+ handle);
+ } else {
+ ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+ }
+ return status;
+}
+
+status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+ status_t status = NO_ERROR;
+ if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
+ status = hwDevice->release_audio_patch(hwDevice, handle);
+ } else {
+ ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+ }
+ return status;
+}
+
+
}; // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index cc2b246..07887fb 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -48,6 +48,8 @@
CFG_EVENT_IO,
CFG_EVENT_PRIO,
CFG_EVENT_SET_PARAMETER,
+ CFG_EVENT_CREATE_AUDIO_PATCH,
+ CFG_EVENT_RELEASE_AUDIO_PATCH,
};
class ConfigEventData: public RefBase {
@@ -161,6 +163,52 @@
virtual ~SetParameterConfigEvent() {}
};
+ class CreateAudioPatchConfigEventData : public ConfigEventData {
+ public:
+ CreateAudioPatchConfigEventData(const struct audio_patch patch,
+ audio_patch_handle_t handle) :
+ mPatch(patch), mHandle(handle) {}
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "Patch handle: %u\n", mHandle);
+ }
+
+ const struct audio_patch mPatch;
+ audio_patch_handle_t mHandle;
+ };
+
+ class CreateAudioPatchConfigEvent : public ConfigEvent {
+ public:
+ CreateAudioPatchConfigEvent(const struct audio_patch patch,
+ audio_patch_handle_t handle) :
+ ConfigEvent(CFG_EVENT_CREATE_AUDIO_PATCH) {
+ mData = new CreateAudioPatchConfigEventData(patch, handle);
+ mWaitStatus = true;
+ }
+ virtual ~CreateAudioPatchConfigEvent() {}
+ };
+
+ class ReleaseAudioPatchConfigEventData : public ConfigEventData {
+ public:
+ ReleaseAudioPatchConfigEventData(const audio_patch_handle_t handle) :
+ mHandle(handle) {}
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "Patch handle: %u\n", mHandle);
+ }
+
+ audio_patch_handle_t mHandle;
+ };
+
+ class ReleaseAudioPatchConfigEvent : public ConfigEvent {
+ public:
+ ReleaseAudioPatchConfigEvent(const audio_patch_handle_t handle) :
+ ConfigEvent(CFG_EVENT_RELEASE_AUDIO_PATCH) {
+ mData = new ReleaseAudioPatchConfigEventData(handle);
+ mWaitStatus = true;
+ }
+ virtual ~ReleaseAudioPatchConfigEvent() {}
+ };
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -209,8 +257,15 @@
void sendIoConfigEvent_l(int event, int param = 0);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
status_t sendSetParameterConfigEvent_l(const String8& keyValuePair);
+ status_t sendCreateAudioPatchConfigEvent(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+ status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle);
void processConfigEvents_l();
virtual void cacheParameters_l() = 0;
+ virtual status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) = 0;
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) = 0;
+
// see note at declaration of mStandby, mOutDevice and mInDevice
bool standby() const { return mStandby; }
@@ -301,6 +356,8 @@
// If a thread does not have such a heap, this method returns 0.
virtual sp<MemoryDealer> readOnlyHeap() const { return 0; }
+ virtual sp<IMemory> pipeMemory() const { return 0; }
+
mutable Mutex mLock;
protected:
@@ -619,7 +676,8 @@
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
+ virtual int getTrackName_l(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId) = 0;
virtual void deleteTrackName_l(int name) = 0;
// Time to sleep between cycles when:
@@ -641,6 +699,10 @@
virtual uint32_t correctLatency_l(uint32_t latency) const;
+ virtual status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
+
private:
friend class AudioFlinger; // for numerous
@@ -772,7 +834,8 @@
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId);
virtual void deleteTrackName_l(int name);
virtual uint32_t idleSleepTimeUs() const;
virtual uint32_t suspendSleepTimeUs() const;
@@ -825,7 +888,8 @@
status_t& status);
protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask,
+ audio_format_t format, int sessionId);
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs() const;
virtual uint32_t idleSleepTimeUs() const;
@@ -1000,6 +1064,8 @@
virtual sp<MemoryDealer> readOnlyHeap() const { return mReadOnlyHeap; }
+ virtual sp<IMemory> pipeMemory() const { return mPipeMemory; }
+
sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
@@ -1030,6 +1096,9 @@
virtual void cacheParameters_l() {}
virtual String8 getParameters(const String8& keys);
virtual void audioConfigChanged(int event, int param = 0);
+ virtual status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+ virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle);
void readInputParameters_l();
virtual uint32_t getInputFramesLost();
@@ -1048,7 +1117,7 @@
static void syncStartEventCallback(const wp<SyncEvent>& event);
virtual size_t frameCount() const { return mFrameCount; }
- bool hasFastCapture() const { return false; }
+ bool hasFastCapture() const { return mFastCapture != 0; }
private:
// Enter standby if not already in standby, and set mStandby flag
@@ -1078,4 +1147,40 @@
const sp<NBAIO_Sink> mTeeSink;
const sp<MemoryDealer> mReadOnlyHeap;
+
+ // one-time initialization, no locks required
+ sp<FastCapture> mFastCapture; // non-0 if there is also a fast capture
+ // FIXME audio watchdog thread
+
+ // contents are not guaranteed to be consistent, no locks required
+ FastCaptureDumpState mFastCaptureDumpState;
+#ifdef STATE_QUEUE_DUMP
+ // FIXME StateQueue observer and mutator dump fields
+#endif
+ // FIXME audio watchdog dump
+
+ // accessible only within the threadLoop(), no locks required
+ // mFastCapture->sq() // for mutating and pushing state
+ int32_t mFastCaptureFutex; // for cold idle
+
+ // The HAL input source is treated as non-blocking,
+ // but current implementation is blocking
+ sp<NBAIO_Source> mInputSource;
+ // The source for the normal capture thread to read from: mInputSource or mPipeSource
+ sp<NBAIO_Source> mNormalSource;
+ // If a fast capture is present, the non-blocking pipe sink written to by fast capture,
+ // otherwise clear
+ sp<NBAIO_Sink> mPipeSink;
+ // If a fast capture is present, the non-blocking pipe source read by normal thread,
+ // otherwise clear
+ sp<NBAIO_Source> mPipeSource;
+ // Depth of pipe from fast capture to normal thread and fast clients, always power of 2
+ size_t mPipeFramesP2;
+ // If a fast capture is present, the Pipe as IMemory, otherwise clear
+ sp<IMemory> mPipeMemory;
+
+ static const size_t kFastCaptureLogSize = 4 * 1024;
+ sp<NBLog::Writer> mFastCaptureNBLogWriter;
+
+ bool mFastTrackAvail; // true if fast track available
};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 5f13be3..4cba3fd 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -39,6 +39,13 @@
STARTING_2, // for RecordTrack only
};
+ // where to allocate the data buffer
+ enum alloc_type {
+ ALLOC_CBLK, // allocate immediately after control block
+ ALLOC_READONLY, // allocate from a separate read-only heap per thread
+ ALLOC_PIPE, // do not allocate; use the pipe buffer
+ };
+
TrackBase(ThreadBase *thread,
const sp<Client>& client,
uint32_t sampleRate,
@@ -50,7 +57,7 @@
int uid,
IAudioFlinger::track_flags_t flags,
bool isOut,
- bool useReadOnlyHeap = false);
+ alloc_type alloc = ALLOC_CBLK);
virtual ~TrackBase();
virtual status_t initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index de1782d..8d5dc7b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -21,6 +21,7 @@
#include "Configuration.h"
#include <math.h>
+#include <sys/syscall.h>
#include <utils/Log.h>
#include <private/media/AudioTrackShared.h>
@@ -72,7 +73,7 @@
int clientUid,
IAudioFlinger::track_flags_t flags,
bool isOut,
- bool useReadOnlyHeap)
+ alloc_type alloc)
: RefBase(),
mThread(thread),
mClient(client),
@@ -116,7 +117,7 @@
// ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
size_t size = sizeof(audio_track_cblk_t);
size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
- if (sharedBuffer == 0 && !useReadOnlyHeap) {
+ if (sharedBuffer == 0 && alloc == ALLOC_CBLK) {
size += bufferSize;
}
@@ -138,7 +139,8 @@
// construct the shared structure in-place.
if (mCblk != NULL) {
new(mCblk) audio_track_cblk_t();
- if (useReadOnlyHeap) {
+ switch (alloc) {
+ case ALLOC_READONLY: {
const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
if (roHeap == 0 ||
(mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
@@ -152,7 +154,17 @@
return;
}
memset(mBuffer, 0, bufferSize);
- } else {
+ } break;
+ case ALLOC_PIPE:
+ mBufferMemory = thread->pipeMemory();
+ // mBuffer is the virtual address as seen from current process (mediaserver),
+ // and should normally be coming from mBufferMemory->pointer().
+ // However in this case the TrackBase does not reference the buffer directly.
+ // It should references the buffer via the pipe.
+ // Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.
+ mBuffer = NULL;
+ break;
+ case ALLOC_CBLK:
// clear all buffers
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
@@ -163,6 +175,7 @@
mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
#endif
}
+ break;
}
#ifdef TEE_SINK
@@ -384,7 +397,7 @@
}
mServerProxy = mAudioTrackServerProxy;
- mName = thread->getTrackName_l(channelMask, sessionId);
+ mName = thread->getTrackName_l(channelMask, format, sessionId);
if (mName < 0) {
ALOGE("no more track names available");
return;
@@ -1008,7 +1021,7 @@
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);
+ (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, INT_MAX);
mIsInvalid = true;
}
@@ -1841,7 +1854,7 @@
: TrackBase(thread, client, sampleRate, format,
channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
flags, false /*isOut*/,
- (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/),
+ flags & IAudioFlinger::TRACK_FAST ? ALLOC_PIPE : ALLOC_CBLK),
mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
// See real initialization of mRsmpInFront at RecordThread::start()
mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
@@ -1860,9 +1873,14 @@
mResampler = AudioResampler::create(16, thread->mChannelCount, sampleRate);
// source SR
mResampler->setSampleRate(thread->mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_INT, AudioMixer::UNITY_GAIN_INT);
mResamplerBufferProvider = new ResamplerBufferProvider(this);
}
+
+ if (flags & IAudioFlinger::TRACK_FAST) {
+ ALOG_ASSERT(thread->mFastTrackAvail);
+ thread->mFastTrackAvail = false;
+ }
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1937,7 +1955,7 @@
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);
+ (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, INT_MAX);
}
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index f270bfc..a22ad9d 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -5,7 +5,6 @@
LOCAL_SRC_FILES:= \
AudioPolicyService.cpp
-USE_LEGACY_AUDIO_POLICY = 1
ifeq ($(USE_LEGACY_AUDIO_POLICY), 1)
LOCAL_SRC_FILES += \
AudioPolicyInterfaceImplLegacy.cpp \
@@ -15,8 +14,7 @@
else
LOCAL_SRC_FILES += \
AudioPolicyInterfaceImpl.cpp \
- AudioPolicyClientImpl.cpp \
- AudioPolicyManager.cpp
+ AudioPolicyClientImpl.cpp
endif
LOCAL_C_INCLUDES := \
@@ -31,14 +29,42 @@
libbinder \
libmedia \
libhardware \
- libhardware_legacy
+ libhardware_legacy \
+
+ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
+LOCAL_SHARED_LIBRARIES += \
+ libaudiopolicymanager
+endif
LOCAL_STATIC_LIBRARIES := \
libmedia_helper \
libserviceutility
-LOCAL_MODULE:= libaudiopolicy
+LOCAL_MODULE:= libaudiopolicyservice
LOCAL_CFLAGS += -fvisibility=hidden
include $(BUILD_SHARED_LIBRARY)
+
+ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
+ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioPolicyManager.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libmedia_helper
+
+LOCAL_MODULE:= libaudiopolicymanager
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
+endif
diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index 44c47c3..c322d92 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -182,6 +182,34 @@
return af->moveEffects(session, src_output, dst_output);
}
+status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs)
+{
+ return mAudioPolicyService->clientCreateAudioPatch(patch, handle, delayMs);
+}
+status_t AudioPolicyService::AudioPolicyClient::releaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs)
+{
+ return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
+}
+
+status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig(
+ const struct audio_port_config *config,
+ int delayMs)
+{
+ return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs);
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
+{
+ mAudioPolicyService->onAudioPortListUpdate();
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate()
+{
+ mAudioPolicyService->onAudioPatchListUpdate();
+}
}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 66260e3..c025a45 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -162,6 +162,24 @@
virtual status_t dump(int fd) = 0;
virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0;
+
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation) = 0;
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid) = 0;
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid) = 0;
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation) = 0;
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+ virtual void clearAudioPatches(uid_t uid) = 0;
+
};
@@ -246,6 +264,21 @@
audio_io_handle_t srcOutput,
audio_io_handle_t dstOutput) = 0;
+ /* Create a patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs) = 0;
+
+ /* Release a patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs) = 0;
+
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs) = 0;
+
+ virtual void onAudioPortListUpdate() = 0;
+
+ virtual void onAudioPatchListUpdate() = 0;
};
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
index c57c4fa..8cc386a 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
@@ -463,5 +463,90 @@
return mAudioPolicyManager->isOffloadSupported(info);
}
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation);
+}
+
+status_t AudioPolicyService::getAudioPort(struct audio_port *port)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->getAudioPort(port);
+}
+
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ return mAudioPolicyManager->createAudioPatch(patch, handle,
+ IPCThreadState::self()->getCallingUid());
+}
+
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->releaseAudioPatch(handle,
+ IPCThreadState::self()->getCallingUid());
+}
+
+status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation);
+}
+
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config)
+{
+ Mutex::Autolock _l(mLock);
+ if(!modifyAudioRoutingAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->setAudioPortConfig(config);
+}
}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
index bb62ab3..0bf4982 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
@@ -485,5 +485,43 @@
return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
}
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused,
+ audio_port_type_t type __unused,
+ unsigned int *num_ports,
+ struct audio_port *ports __unused,
+ unsigned int *generation __unused)
+{
+ *num_ports = 0;
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused)
+{
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused,
+ audio_patch_handle_t *handle __unused)
+{
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused)
+{
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches __unused,
+ unsigned int *generation __unused)
+{
+ *num_patches = 0;
+ return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused)
+{
+ return INVALID_OPERATION;
+}
}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index bd9b15a..d4c9374 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -38,9 +38,9 @@
#include <utils/Log.h>
#include <hardware/audio.h>
#include <hardware/audio_effect.h>
-#include <hardware_legacy/audio_policy_conf.h>
#include <media/AudioParameter.h>
#include "AudioPolicyManager.h"
+#include "audio_policy_conf.h"
namespace android {
@@ -100,6 +100,7 @@
STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE),
STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF),
+ STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
};
const StringToEnum sFlagNameToEnumTable[] = {
@@ -136,6 +137,12 @@
STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
};
+const StringToEnum sGainModeNameToEnumTable[] = {
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP),
+};
+
uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table,
size_t size,
@@ -188,9 +195,8 @@
if (audio_is_output_device(device)) {
SortedVector <audio_io_handle_t> outputs;
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- address,
- 0);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = address;
ssize_t index = mAvailableOutputDevices.indexOf(devDesc);
// save a copy of the opened output descriptors before any output is opened or closed
@@ -209,12 +215,19 @@
if (checkOutputsForDevice(device, state, outputs, address) != NO_ERROR) {
return INVALID_OPERATION;
}
+ // outputs should never be empty here
+ ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
+ "checkOutputsForDevice() returned no outputs but status OK");
ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
outputs.size());
// register new device as available
index = mAvailableOutputDevices.add(devDesc);
if (index >= 0) {
mAvailableOutputDevices[index]->mId = nextUniqueId();
+ sp<HwModule> module = getModuleForDevice(device);
+ ALOG_ASSERT(module != NULL, "setDeviceConnectionState():"
+ "could not find HW module for device %08x", device);
+ mAvailableOutputDevices[index]->mModule = module;
} else {
return NO_MEMORY;
}
@@ -248,7 +261,7 @@
// outputs must be closed after checkOutputForAllStrategies() is executed
if (!outputs.isEmpty()) {
for (size_t i = 0; i < outputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
// close unused outputs after device disconnection or direct outputs that have been
// opened by checkOutputsForDevice() to query dynamic parameters
if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
@@ -267,30 +280,21 @@
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
setOutputDevice(mOutputs.keyAt(i),
- getNewDevice(mOutputs.keyAt(i), true /*fromCache*/),
+ getNewOutputDevice(mOutputs.keyAt(i), true /*fromCache*/),
!mOutputs.valueAt(i)->isDuplicated(),
0);
}
- if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) {
- device = AUDIO_DEVICE_IN_WIRED_HEADSET;
- } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO ||
- device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
- device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
- device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else {
- return NO_ERROR;
- }
+ mpClientInterface->onAudioPortListUpdate();
+ return NO_ERROR;
} // end if is output device
// handle input devices
if (audio_is_input_device(device)) {
SortedVector <audio_io_handle_t> inputs;
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- address,
- 0);
-
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = address;
ssize_t index = mAvailableInputDevices.indexOf(devDesc);
switch (state)
{
@@ -300,6 +304,12 @@
ALOGW("setDeviceConnectionState() device already connected: %d", device);
return INVALID_OPERATION;
}
+ sp<HwModule> module = getModuleForDevice(device);
+ if (module == NULL) {
+ ALOGW("setDeviceConnectionState(): could not find HW module for device %08x",
+ device);
+ return INVALID_OPERATION;
+ }
if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) {
return INVALID_OPERATION;
}
@@ -307,6 +317,7 @@
index = mAvailableInputDevices.add(devDesc);
if (index >= 0) {
mAvailableInputDevices[index]->mId = nextUniqueId();
+ mAvailableInputDevices[index]->mModule = module;
} else {
return NO_MEMORY;
}
@@ -329,6 +340,7 @@
closeAllInputs();
+ mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
} // end if is input device
@@ -341,9 +353,8 @@
{
audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
String8 address = String8(device_address);
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- String8(device_address),
- 0);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = String8(device_address);
ssize_t index;
DeviceVector *deviceVector;
@@ -419,12 +430,12 @@
}
// check for device and output changes triggered by new phone state
- newDevice = getNewDevice(mPrimaryOutput, false /*fromCache*/);
+ newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
checkA2dpSuspend();
checkOutputForAllStrategies();
updateDevicesAndOutputs();
- AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
// force routing command to audio hardware when ending call
// even if no device change is needed
@@ -436,7 +447,7 @@
if (isStateInCall(state)) {
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
// mute media and sonification strategies and delay device switch by the largest
// latency of any output where either strategy is active.
// This avoid sending the ring tone or music tail into the earpiece or headset.
@@ -544,7 +555,7 @@
updateDevicesAndOutputs();
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t output = mOutputs.keyAt(i);
- audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/);
+ audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
applyStreamVolumes(output, newDevice, 0, true);
@@ -553,16 +564,7 @@
audio_io_handle_t activeInput = getActiveInput();
if (activeInput != 0) {
- AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
- audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) {
- ALOGV("setForceUse() changing device from %x to %x for input %d",
- inputDesc->mDevice, newDevice, activeInput);
- inputDesc->mDevice = newDevice;
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
- mpClientInterface->setParameters(activeInput, param.toString());
- }
+ setInputDevice(activeInput, getNewInputDevice(activeInput));
}
}
@@ -579,7 +581,7 @@
// Find a direct output profile compatible with the parameters passed, even if the input flags do
// not explicitly request a direct output
-AudioPolicyManager::IOProfile *AudioPolicyManager::getProfileForDirectOutput(
+sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput(
audio_devices_t device,
uint32_t samplingRate,
audio_format_t format,
@@ -591,7 +593,7 @@
continue;
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
- IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
+ sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
bool found = false;
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
if (profile->isCompatibleProfile(device, samplingRate, format,
@@ -635,7 +637,7 @@
if (mTestOutputs[mCurOutput] == 0) {
ALOGV("getOutput() opening test output");
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL);
+ sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
outputDesc->mDevice = mTestDevice;
outputDesc->mSamplingRate = mTestSamplingRate;
outputDesc->mFormat = mTestFormat;
@@ -676,7 +678,7 @@
// FIXME: We should check the audio session here but we do not have it in this context.
// This may prevent offloading in rare situations where effects are left active by apps
// in the background.
- IOProfile *profile = NULL;
+ sp<IOProfile> profile;
if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
!isNonOffloadableEffectEnabled()) {
profile = getProfileForDirectOutput(device,
@@ -686,11 +688,11 @@
(audio_output_flags_t)flags);
}
- if (profile != NULL) {
- AudioOutputDescriptor *outputDesc = NULL;
+ if (profile != 0) {
+ sp<AudioOutputDescriptor> outputDesc = NULL;
for (size_t i = 0; i < mOutputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && (profile == desc->mProfile)) {
outputDesc = desc;
// reuse direct output if currently open and configured with same parameters
@@ -705,7 +707,7 @@
}
// close direct output if currently open and configured with different parameters
if (outputDesc != NULL) {
- closeOutput(outputDesc->mId);
+ closeOutput(outputDesc->mIoHandle);
}
outputDesc = new AudioOutputDescriptor(profile);
outputDesc->mDevice = device;
@@ -738,7 +740,6 @@
if (output != 0) {
mpClientInterface->closeOutput(output);
}
- delete outputDesc;
return 0;
}
audio_io_handle_t srcOutput = getOutputForEffect();
@@ -749,6 +750,7 @@
}
mPreviousOutputs = mOutputs;
ALOGV("getOutput() returns new direct output %d", output);
+ mpClientInterface->onAudioPortListUpdate();
return output;
}
@@ -794,7 +796,7 @@
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
if (!outputDesc->isDuplicated()) {
int commonFlags = popcount(outputDesc->mProfile->mFlags & flags);
if (commonFlags > maxCommonFlags) {
@@ -829,7 +831,7 @@
return BAD_VALUE;
}
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
// increment usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
@@ -837,14 +839,14 @@
outputDesc->changeRefCount(stream, 1);
if (outputDesc->mRefCount[stream] == 1) {
- audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
+ audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
routing_strategy strategy = getStrategy(stream);
bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
(strategy == STRATEGY_SONIFICATION_RESPECTFUL);
uint32_t waitMs = 0;
bool force = false;
for (size_t i = 0; i < mOutputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc) {
// force a device change if any other output is managed by the same hw
// module and has a current device selection that differs from selected device.
@@ -897,7 +899,7 @@
return BAD_VALUE;
}
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
// handle special case for sonification while in call
if (isInCall()) {
@@ -910,7 +912,7 @@
// store time at which the stream was stopped - see isStreamActive()
if (outputDesc->mRefCount[stream] == 0) {
outputDesc->mStopTime[stream] = systemTime();
- audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
+ audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
// delay the device switch by twice the latency because stopOutput() is executed when
// the track stop() command is received and at that time the audio track buffer can
// still contain data that needs to be drained. The latency only covers the audio HAL
@@ -922,13 +924,13 @@
// one being selected for this output
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t curOutput = mOutputs.keyAt(i);
- AudioOutputDescriptor *desc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (curOutput != output &&
desc->isActive() &&
outputDesc->sharesHwModuleWith(desc) &&
(newDevice != desc->device())) {
setOutputDevice(curOutput,
- getNewDevice(curOutput, false /*fromCache*/),
+ getNewOutputDevice(curOutput, false /*fromCache*/),
true,
outputDesc->mLatency*2);
}
@@ -955,10 +957,9 @@
#ifdef AUDIO_POLICY_TEST
int testIndex = testOutputIndex(output);
if (testIndex != 0) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
if (outputDesc->isActive()) {
mpClientInterface->closeOutput(output);
- delete mOutputs.valueAt(index);
mOutputs.removeItem(output);
mTestOutputs[testIndex] = 0;
}
@@ -966,7 +967,7 @@
}
#endif //AUDIO_POLICY_TEST
- AudioOutputDescriptor *desc = mOutputs.valueAt(index);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index);
if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (desc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -981,6 +982,7 @@
if (dstOutput != mPrimaryOutput) {
mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
}
+ mpClientInterface->onAudioPortListUpdate();
}
}
}
@@ -1018,11 +1020,11 @@
break;
}
- IOProfile *profile = getInputProfile(device,
+ sp<IOProfile> profile = getInputProfile(device,
samplingRate,
format,
channelMask);
- if (profile == NULL) {
+ if (profile == 0) {
ALOGW("getInput() could not find profile for device %04x, samplingRate %d, format %d, "
"channelMask %04x",
device, samplingRate, format, channelMask);
@@ -1034,7 +1036,7 @@
return 0;
}
- AudioInputDescriptor *inputDesc = new AudioInputDescriptor(profile);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile);
inputDesc->mInputSource = inputSource;
inputDesc->mDevice = device;
@@ -1058,10 +1060,10 @@
if (input != 0) {
mpClientInterface->closeInput(input);
}
- delete inputDesc;
return 0;
}
addInput(input, inputDesc);
+ mpClientInterface->onAudioPortListUpdate();
return input;
}
@@ -1073,7 +1075,7 @@
ALOGW("startInput() unknown input %d", input);
return BAD_VALUE;
}
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
#ifdef AUDIO_POLICY_TEST
if (mTestInput == 0)
@@ -1083,7 +1085,7 @@
// uses AUDIO_SOURCE_HOTWORD in which case it is closed.
audio_io_handle_t activeInput = getActiveInput();
if (!isVirtualInputDevice(inputDesc->mDevice) && activeInput != 0) {
- AudioInputDescriptor *activeDesc = mInputs.valueFor(activeInput);
+ sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput);
if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) {
ALOGW("startInput() preempting already started low-priority input %d", activeInput);
stopInput(activeInput);
@@ -1095,10 +1097,7 @@
}
}
- audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) {
- inputDesc->mDevice = newDevice;
- }
+ setInputDevice(input, getNewInputDevice(input), true /* force */);
// automatically enable the remote submix output when input is started
if (audio_is_remote_submix_device(inputDesc->mDevice)) {
@@ -1106,17 +1105,8 @@
AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
}
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
-
- int aliasSource = (inputDesc->mInputSource == AUDIO_SOURCE_HOTWORD) ?
- AUDIO_SOURCE_VOICE_RECOGNITION : inputDesc->mInputSource;
-
- param.addInt(String8(AudioParameter::keyInputSource), aliasSource);
ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
- mpClientInterface->setParameters(input, param.toString());
-
inputDesc->mRefCount = 1;
return NO_ERROR;
}
@@ -1129,7 +1119,7 @@
ALOGW("stopInput() unknown input %d", input);
return BAD_VALUE;
}
- AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
if (inputDesc->mRefCount == 0) {
ALOGW("stopInput() input %d already stopped", input);
@@ -1141,9 +1131,7 @@
AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
}
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), 0);
- mpClientInterface->setParameters(input, param.toString());
+ resetInputDevice(input);
inputDesc->mRefCount = 0;
return NO_ERROR;
}
@@ -1158,8 +1146,9 @@
return;
}
mpClientInterface->closeInput(input);
- delete mInputs.valueAt(index);
mInputs.removeItem(input);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPortListUpdate();
ALOGV("releaseInput() exit");
}
@@ -1168,6 +1157,7 @@
mpClientInterface->closeInput(mInputs.keyAt(input_index));
}
mInputs.clear();
+ nextAudioPortGeneration();
}
void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream,
@@ -1264,7 +1254,7 @@
audio_io_handle_t outputDeepBuffer = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueFor(outputs[i]);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags);
if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
outputOffloaded = outputs[i];
@@ -1326,14 +1316,14 @@
desc->name, io, strategy, session, id);
ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory);
- EffectDescriptor *pDesc = new EffectDescriptor();
- memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
- pDesc->mIo = io;
- pDesc->mStrategy = (routing_strategy)strategy;
- pDesc->mSession = session;
- pDesc->mEnabled = false;
+ sp<EffectDescriptor> effectDesc = new EffectDescriptor();
+ memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t));
+ effectDesc->mIo = io;
+ effectDesc->mStrategy = (routing_strategy)strategy;
+ effectDesc->mSession = session;
+ effectDesc->mEnabled = false;
- mEffects.add(id, pDesc);
+ mEffects.add(id, effectDesc);
return NO_ERROR;
}
@@ -1346,21 +1336,20 @@
return INVALID_OPERATION;
}
- EffectDescriptor *pDesc = mEffects.valueAt(index);
+ sp<EffectDescriptor> effectDesc = mEffects.valueAt(index);
- setEffectEnabled(pDesc, false);
+ setEffectEnabled(effectDesc, false);
- if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) {
+ if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) {
ALOGW("unregisterEffect() memory %d too big for total %d",
- pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
- pDesc->mDesc.memoryUsage = mTotalEffectsMemory;
+ effectDesc->mDesc.memoryUsage, mTotalEffectsMemory);
+ effectDesc->mDesc.memoryUsage = mTotalEffectsMemory;
}
- mTotalEffectsMemory -= pDesc->mDesc.memoryUsage;
+ mTotalEffectsMemory -= effectDesc->mDesc.memoryUsage;
ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d",
- pDesc->mDesc.name, id, pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
+ effectDesc->mDesc.name, id, effectDesc->mDesc.memoryUsage, mTotalEffectsMemory);
mEffects.removeItem(id);
- delete pDesc;
return NO_ERROR;
}
@@ -1376,43 +1365,43 @@
return setEffectEnabled(mEffects.valueAt(index), enabled);
}
-status_t AudioPolicyManager::setEffectEnabled(EffectDescriptor *pDesc, bool enabled)
+status_t AudioPolicyManager::setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled)
{
- if (enabled == pDesc->mEnabled) {
+ if (enabled == effectDesc->mEnabled) {
ALOGV("setEffectEnabled(%s) effect already %s",
enabled?"true":"false", enabled?"enabled":"disabled");
return INVALID_OPERATION;
}
if (enabled) {
- if (mTotalEffectsCpuLoad + pDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) {
+ if (mTotalEffectsCpuLoad + effectDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) {
ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS",
- pDesc->mDesc.name, (float)pDesc->mDesc.cpuLoad/10);
+ effectDesc->mDesc.name, (float)effectDesc->mDesc.cpuLoad/10);
return INVALID_OPERATION;
}
- mTotalEffectsCpuLoad += pDesc->mDesc.cpuLoad;
+ mTotalEffectsCpuLoad += effectDesc->mDesc.cpuLoad;
ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad);
} else {
- if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) {
+ if (mTotalEffectsCpuLoad < effectDesc->mDesc.cpuLoad) {
ALOGW("setEffectEnabled(false) CPU load %d too high for total %d",
- pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
- pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
+ effectDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
+ effectDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
}
- mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad;
+ mTotalEffectsCpuLoad -= effectDesc->mDesc.cpuLoad;
ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad);
}
- pDesc->mEnabled = enabled;
+ effectDesc->mEnabled = enabled;
return NO_ERROR;
}
bool AudioPolicyManager::isNonOffloadableEffectEnabled()
{
for (size_t i = 0; i < mEffects.size(); i++) {
- const EffectDescriptor * const pDesc = mEffects.valueAt(i);
- if (pDesc->mEnabled && (pDesc->mStrategy == STRATEGY_MEDIA) &&
- ((pDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) {
+ sp<EffectDescriptor> effectDesc = mEffects.valueAt(i);
+ if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) &&
+ ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) {
ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d",
- pDesc->mDesc.name, pDesc->mSession);
+ effectDesc->mDesc.name, effectDesc->mSession);
return true;
}
}
@@ -1423,7 +1412,7 @@
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
- const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
+ const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
return true;
}
@@ -1436,7 +1425,7 @@
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
- const AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
+ const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
return true;
@@ -1448,7 +1437,7 @@
bool AudioPolicyManager::isSourceActive(audio_source_t source) const
{
for (size_t i = 0; i < mInputs.size(); i++) {
- const AudioInputDescriptor * inputDescriptor = mInputs.valueAt(i);
+ const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i);
if ((inputDescriptor->mInputSource == (int)source ||
(source == AUDIO_SOURCE_VOICE_RECOGNITION &&
inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD))
@@ -1488,15 +1477,13 @@
snprintf(buffer, SIZE, " Available output devices:\n");
result.append(buffer);
write(fd, result.string(), result.size());
- DeviceDescriptor::dumpHeader(fd, 2);
for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
- mAvailableOutputDevices[i]->dump(fd, 2);
+ mAvailableOutputDevices[i]->dump(fd, 2, i);
}
snprintf(buffer, SIZE, "\n Available input devices:\n");
write(fd, buffer, strlen(buffer));
- DeviceDescriptor::dumpHeader(fd, 2);
for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
- mAvailableInputDevices[i]->dump(fd, 2);
+ mAvailableInputDevices[i]->dump(fd, 2, i);
}
snprintf(buffer, SIZE, "\nHW Modules dump:\n");
@@ -1608,13 +1595,533 @@
// See if there is a profile to support this.
// AUDIO_DEVICE_NONE
- IOProfile *profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */,
+ sp<IOProfile> profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */,
offloadInfo.sample_rate,
offloadInfo.format,
offloadInfo.channel_mask,
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
- ALOGV("isOffloadSupported() profile %sfound", profile != NULL ? "" : "NOT ");
- return (profile != NULL);
+ ALOGV("isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT ");
+ return (profile != 0);
+}
+
+status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation)
+{
+ if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
+ if (ports == NULL) {
+ *num_ports = 0;
+ }
+
+ size_t portsWritten = 0;
+ size_t portsMax = *num_ports;
+ *num_ports = 0;
+ if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_DEVICE) {
+ if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0;
+ i < mAvailableOutputDevices.size() && portsWritten < portsMax; i++) {
+ mAvailableOutputDevices[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mAvailableOutputDevices.size();
+ }
+ if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0;
+ i < mAvailableInputDevices.size() && portsWritten < portsMax; i++) {
+ mAvailableInputDevices[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mAvailableInputDevices.size();
+ }
+ }
+ if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_MIX) {
+ if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0; i < mInputs.size() && portsWritten < portsMax; i++) {
+ mInputs[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mInputs.size();
+ }
+ if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0; i < mOutputs.size() && portsWritten < portsMax; i++) {
+ mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mOutputs.size();
+ }
+ }
+ *generation = curAudioPortGeneration();
+ ALOGV("listAudioPorts() got %d ports needed %d", portsWritten, *num_ports);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused)
+{
+ return NO_ERROR;
+}
+
+sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromId(
+ audio_port_handle_t id) const
+{
+ sp<AudioOutputDescriptor> outputDesc = NULL;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ outputDesc = mOutputs.valueAt(i);
+ if (outputDesc->mId == id) {
+ break;
+ }
+ }
+ return outputDesc;
+}
+
+sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId(
+ audio_port_handle_t id) const
+{
+ sp<AudioInputDescriptor> inputDesc = NULL;
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ inputDesc = mInputs.valueAt(i);
+ if (inputDesc->mId == id) {
+ break;
+ }
+ }
+ return inputDesc;
+}
+
+sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice(
+ audio_devices_t device) const
+{
+ sp <HwModule> module;
+
+ for (size_t i = 0; i < mHwModules.size(); i++) {
+ if (mHwModules[i]->mHandle == 0) {
+ continue;
+ }
+ if (audio_is_output_device(device)) {
+ for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
+ {
+ if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) {
+ return mHwModules[i];
+ }
+ }
+ } else {
+ for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) {
+ if (mHwModules[i]->mInputProfiles[j]->mSupportedDevices.types() &
+ device & ~AUDIO_DEVICE_BIT_IN) {
+ return mHwModules[i];
+ }
+ }
+ }
+ }
+ return module;
+}
+
+sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleFromName(const char *name) const
+{
+ sp <HwModule> module;
+
+ for (size_t i = 0; i < mHwModules.size(); i++)
+ {
+ if (strcmp(mHwModules[i]->mName, name) == 0) {
+ return mHwModules[i];
+ }
+ }
+ return module;
+}
+
+
+status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid)
+{
+ ALOGV("createAudioPatch()");
+
+ if (handle == NULL || patch == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
+
+ if (patch->num_sources > 1 || patch->num_sinks > 1) {
+ return INVALID_OPERATION;
+ }
+ if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
+ patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AudioPatch> patchDesc;
+ ssize_t index = mAudioPatches.indexOfKey(*handle);
+
+ ALOGV("createAudioPatch sink id %d role %d type %d", patch->sinks[0].id, patch->sinks[0].role,
+ patch->sinks[0].type);
+ ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
+ patch->sources[0].role,
+ patch->sources[0].type);
+
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ ALOGV("createAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+ mUidCached, patchDesc->mUid, uid);
+ if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ return INVALID_OPERATION;
+ }
+ } else {
+ *handle = 0;
+ }
+
+ if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ // TODO add support for mix to mix connection
+ if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGV("createAudioPatch() source mix sink not device");
+ return BAD_VALUE;
+ }
+ // output mix to output device connection
+ sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id);
+ if (outputDesc == NULL) {
+ ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
+ ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
+ patchDesc->mPatch.sources[0].id, patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ }
+ sp<DeviceDescriptor> devDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+ if (devDesc == 0) {
+ ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[0].id);
+ return BAD_VALUE;
+ }
+
+ if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+ patch->sources[0].sample_rate,
+ patch->sources[0].format,
+ patch->sources[0].channel_mask,
+ AUDIO_OUTPUT_FLAG_NONE)) {
+ return INVALID_OPERATION;
+ }
+ // TODO: reconfigure output format and channels here
+ ALOGV("createAudioPatch() setting device %08x on output %d",
+ devDesc->mType, outputDesc->mIoHandle);
+ setOutputDevice(outputDesc->mIoHandle,
+ devDesc->mType,
+ true,
+ 0,
+ handle);
+ index = mAudioPatches.indexOfKey(*handle);
+ if (index >= 0) {
+ if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+ ALOGW("createAudioPatch() setOutputDevice() did not reuse the patch provided");
+ }
+ patchDesc = mAudioPatches.valueAt(index);
+ patchDesc->mUid = uid;
+ ALOGV("createAudioPatch() success");
+ } else {
+ ALOGW("createAudioPatch() setOutputDevice() failed to create a patch");
+ return INVALID_OPERATION;
+ }
+ } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ // input device to input mix connection
+ sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id);
+ if (inputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+ return BAD_VALUE;
+ }
+ }
+ sp<DeviceDescriptor> devDesc =
+ mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+ if (devDesc == 0) {
+ return BAD_VALUE;
+ }
+
+ if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+ patch->sinks[0].sample_rate,
+ patch->sinks[0].format,
+ patch->sinks[0].channel_mask,
+ AUDIO_OUTPUT_FLAG_NONE)) {
+ return INVALID_OPERATION;
+ }
+ // TODO: reconfigure output format and channels here
+ ALOGV("createAudioPatch() setting device %08x on output %d",
+ devDesc->mType, inputDesc->mIoHandle);
+ setInputDevice(inputDesc->mIoHandle,
+ devDesc->mType,
+ true,
+ handle);
+ index = mAudioPatches.indexOfKey(*handle);
+ if (index >= 0) {
+ if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+ ALOGW("createAudioPatch() setInputDevice() did not reuse the patch provided");
+ }
+ patchDesc = mAudioPatches.valueAt(index);
+ patchDesc->mUid = uid;
+ ALOGV("createAudioPatch() success");
+ } else {
+ ALOGW("createAudioPatch() setInputDevice() failed to create a patch");
+ return INVALID_OPERATION;
+ }
+ } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ // device to device connection
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sources[0].id != patch->sources[0].id &&
+ patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+ return BAD_VALUE;
+ }
+ }
+
+ sp<DeviceDescriptor> srcDeviceDesc =
+ mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+ sp<DeviceDescriptor> sinkDeviceDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+ if (srcDeviceDesc == 0 || sinkDeviceDesc == 0) {
+ return BAD_VALUE;
+ }
+ //update source and sink with our own data as the data passed in the patch may
+ // be incomplete.
+ struct audio_patch newPatch = *patch;
+ srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
+ sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]);
+
+ // TODO: add support for devices on different HW modules
+ if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+ return INVALID_OPERATION;
+ }
+ // TODO: check from routing capabilities in config file and other conflicting patches
+
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
+ status_t status = mpClientInterface->createAudioPatch(&newPatch,
+ &afPatchHandle,
+ 0);
+ ALOGV("createAudioPatch() patch panel returned %d patchHandle %d",
+ status, afPatchHandle);
+ if (status == NO_ERROR) {
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &newPatch, uid);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = newPatch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ *handle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ } else {
+ ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
+ status);
+ return INVALID_OPERATION;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid)
+{
+ ALOGV("releaseAudioPatch() patch %d", handle);
+
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index < 0) {
+ return BAD_VALUE;
+ }
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ ALOGV("releaseAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+ mUidCached, patchDesc->mUid, uid);
+ if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ return INVALID_OPERATION;
+ }
+
+ struct audio_patch *patch = &patchDesc->mPatch;
+ patchDesc->mUid = mUidCached;
+ if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id);
+ if (outputDesc == NULL) {
+ ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
+ return BAD_VALUE;
+ }
+
+ setOutputDevice(outputDesc->mIoHandle,
+ getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+ true,
+ 0,
+ NULL);
+ } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id);
+ if (inputDesc == NULL) {
+ ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id);
+ return BAD_VALUE;
+ }
+ setInputDevice(inputDesc->mIoHandle,
+ getNewInputDevice(inputDesc->mIoHandle),
+ true,
+ NULL);
+ } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
+ status, patchDesc->mAfPatchHandle);
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ } else {
+ return BAD_VALUE;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation)
+{
+ if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("listAudioPatches() num_patches %d patches %p available patches %d",
+ *num_patches, patches, mAudioPatches.size());
+ if (patches == NULL) {
+ *num_patches = 0;
+ }
+
+ size_t patchesWritten = 0;
+ size_t patchesMax = *num_patches;
+ for (size_t i = 0;
+ i < mAudioPatches.size() && patchesWritten < patchesMax; i++) {
+ patches[patchesWritten] = mAudioPatches[i]->mPatch;
+ patches[patchesWritten++].id = mAudioPatches[i]->mHandle;
+ ALOGV("listAudioPatches() patch %d num_sources %d num_sinks %d",
+ i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks);
+ }
+ *num_patches = mAudioPatches.size();
+
+ *generation = curAudioPortGeneration();
+ ALOGV("listAudioPatches() got %d patches needed %d", patchesWritten, *num_patches);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config)
+{
+ ALOGV("setAudioPortConfig()");
+
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("setAudioPortConfig() on port handle %d", config->id);
+ // Only support gain configuration for now
+ if (config->config_mask != AUDIO_PORT_CONFIG_GAIN) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AudioPortConfig> audioPortConfig;
+ if (config->type == AUDIO_PORT_TYPE_MIX) {
+ if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+ sp<AudioOutputDescriptor> outputDesc = getOutputFromId(config->id);
+ if (outputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ audioPortConfig = outputDesc;
+ } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+ sp<AudioInputDescriptor> inputDesc = getInputFromId(config->id);
+ if (inputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ audioPortConfig = inputDesc;
+ } else {
+ return BAD_VALUE;
+ }
+ } else if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+ sp<DeviceDescriptor> deviceDesc;
+ if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+ deviceDesc = mAvailableInputDevices.getDeviceFromId(config->id);
+ } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+ deviceDesc = mAvailableOutputDevices.getDeviceFromId(config->id);
+ } else {
+ return BAD_VALUE;
+ }
+ if (deviceDesc == NULL) {
+ return BAD_VALUE;
+ }
+ audioPortConfig = deviceDesc;
+ } else {
+ return BAD_VALUE;
+ }
+
+ struct audio_port_config backupConfig;
+ status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig);
+ if (status == NO_ERROR) {
+ struct audio_port_config newConfig;
+ audioPortConfig->toAudioPortConfig(&newConfig, config);
+ status = mpClientInterface->setAudioPortConfig(&newConfig, 0);
+ }
+ if (status != NO_ERROR) {
+ audioPortConfig->applyAudioPortConfig(&backupConfig);
+ }
+
+ return status;
+}
+
+void AudioPolicyManager::clearAudioPatches(uid_t uid)
+{
+ for (ssize_t i = 0; i < (ssize_t)mAudioPatches.size(); i++) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i);
+ if (patchDesc->mUid == uid) {
+ // releaseAudioPatch() removes the patch from mAudioPatches
+ if (releaseAudioPatch(mAudioPatches.keyAt(i), uid) == NO_ERROR) {
+ i--;
+ }
+ }
+ }
+}
+
+status_t AudioPolicyManager::addAudioPatch(audio_patch_handle_t handle,
+ const sp<AudioPatch>& patch)
+{
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index >= 0) {
+ ALOGW("addAudioPatch() patch %d already in", handle);
+ return ALREADY_EXISTS;
+ }
+ mAudioPatches.add(handle, patch);
+ ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d"
+ "sink handle %d",
+ handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks,
+ patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeAudioPatch(audio_patch_handle_t handle)
+{
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index < 0) {
+ ALOGW("removeAudioPatch() patch %d not in", handle);
+ return ALREADY_EXISTS;
+ }
+ ALOGV("removeAudioPatch() handle %d af handle %d", handle,
+ mAudioPatches.valueAt(index)->mAfPatchHandle);
+ mAudioPatches.removeItemsAt(index);
+ return NO_ERROR;
}
// ----------------------------------------------------------------------------
@@ -1626,6 +2133,11 @@
return android_atomic_inc(&mNextUniqueId);
}
+uint32_t AudioPolicyManager::nextAudioPortGeneration()
+{
+ return android_atomic_inc(&mAudioPortGeneration);
+}
+
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
:
#ifdef AUDIO_POLICY_TEST
@@ -1636,15 +2148,17 @@
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
mA2dpSuspended(false),
- mSpeakerDrcEnabled(false), mNextUniqueId(0)
+ mSpeakerDrcEnabled(false), mNextUniqueId(1),
+ mAudioPortGeneration(1)
{
+ mUidCached = getuid();
mpClientInterface = clientInterface;
for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) {
mForceUse[i] = AUDIO_POLICY_FORCE_NONE;
}
- mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
+ mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER);
if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) {
if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
@@ -1671,7 +2185,7 @@
// This also validates mAvailableOutputDevices list
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
- const IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j];
+ const sp<IOProfile> outProfile = mHwModules[i]->mOutputProfiles[j];
if (outProfile->mSupportedDevices.isEmpty()) {
ALOGW("Output profile contains no device on module %s", mHwModules[i]->mName);
@@ -1681,9 +2195,9 @@
audio_devices_t profileTypes = outProfile->mSupportedDevices.types();
if ((profileTypes & outputDeviceTypes) &&
((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile);
+ sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
- outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mType & profileTypes);
+ outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mDeviceType & profileTypes);
audio_io_handle_t output = mpClientInterface->openOutput(
outProfile->mModule->mHandle,
&outputDesc->mDevice,
@@ -1696,15 +2210,15 @@
ALOGW("Cannot open output stream for device %08x on hw module %s",
outputDesc->mDevice,
mHwModules[i]->mName);
- delete outputDesc;
} else {
for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) {
- audio_devices_t type = outProfile->mSupportedDevices[k]->mType;
+ audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType;
ssize_t index =
mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]);
// give a valid ID to an attached device once confirmed it is reachable
if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
mAvailableOutputDevices[index]->mId = nextUniqueId();
+ mAvailableOutputDevices[index]->mModule = mHwModules[i];
}
}
if (mPrimaryOutput == 0 &&
@@ -1712,6 +2226,7 @@
mPrimaryOutput = output;
}
addOutput(output, outputDesc);
+ ALOGI("CSTOR setOutputDevice %08x", outputDesc->mDevice);
setOutputDevice(output,
outputDesc->mDevice,
true);
@@ -1722,7 +2237,7 @@
// mAvailableInputDevices list
for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
{
- const IOProfile *inProfile = mHwModules[i]->mInputProfiles[j];
+ const sp<IOProfile> inProfile = mHwModules[i]->mInputProfiles[j];
if (inProfile->mSupportedDevices.isEmpty()) {
ALOGW("Input profile contains no device on module %s", mHwModules[i]->mName);
@@ -1731,10 +2246,10 @@
audio_devices_t profileTypes = inProfile->mSupportedDevices.types();
if (profileTypes & inputDeviceTypes) {
- AudioInputDescriptor *inputDesc = new AudioInputDescriptor(inProfile);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile);
inputDesc->mInputSource = AUDIO_SOURCE_MIC;
- inputDesc->mDevice = inProfile->mSupportedDevices[0]->mType;
+ inputDesc->mDevice = inProfile->mSupportedDevices[0]->mDeviceType;
audio_io_handle_t input = mpClientInterface->openInput(
inProfile->mModule->mHandle,
&inputDesc->mDevice,
@@ -1744,12 +2259,13 @@
if (input != 0) {
for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) {
- audio_devices_t type = inProfile->mSupportedDevices[k]->mType;
+ audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType;
ssize_t index =
mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]);
// give a valid ID to an attached device once confirmed it is reachable
if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
mAvailableInputDevices[index]->mId = nextUniqueId();
+ mAvailableInputDevices[index]->mModule = mHwModules[i];
}
}
mpClientInterface->closeInput(input);
@@ -1758,14 +2274,13 @@
inputDesc->mDevice,
mHwModules[i]->mName);
}
- delete inputDesc;
}
}
}
// make sure all attached devices have been allocated a unique ID
for (size_t i = 0; i < mAvailableOutputDevices.size();) {
if (mAvailableOutputDevices[i]->mId == 0) {
- ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mType);
+ ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mDeviceType);
mAvailableOutputDevices.remove(mAvailableOutputDevices[i]);
continue;
}
@@ -1773,7 +2288,7 @@
}
for (size_t i = 0; i < mAvailableInputDevices.size();) {
if (mAvailableInputDevices[i]->mId == 0) {
- ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mType);
+ ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mDeviceType);
mAvailableInputDevices.remove(mAvailableInputDevices[i]);
continue;
}
@@ -1781,7 +2296,7 @@
}
// make sure default device is reachable
if (mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) {
- ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mType);
+ ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mDeviceType);
}
ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output");
@@ -1820,17 +2335,15 @@
#endif //AUDIO_POLICY_TEST
for (size_t i = 0; i < mOutputs.size(); i++) {
mpClientInterface->closeOutput(mOutputs.keyAt(i));
- delete mOutputs.valueAt(i);
}
for (size_t i = 0; i < mInputs.size(); i++) {
mpClientInterface->closeInput(mInputs.keyAt(i));
- delete mInputs.valueAt(i);
- }
- for (size_t i = 0; i < mHwModules.size(); i++) {
- delete mHwModules[i];
}
mAvailableOutputDevices.clear();
mAvailableInputDevices.clear();
+ mOutputs.clear();
+ mInputs.clear();
+ mHwModules.clear();
}
status_t AudioPolicyManager::initCheck()
@@ -1934,15 +2447,14 @@
if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
param.remove(String8("test_cmd_policy_reopen"));
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
mpClientInterface->closeOutput(mPrimaryOutput);
audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle;
- delete mOutputs.valueFor(mPrimaryOutput);
mOutputs.removeItem(mPrimaryOutput);
- AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(NULL);
+ sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
mPrimaryOutput = mpClientInterface->openOutput(moduleHandle,
&outputDesc->mDevice,
@@ -1990,16 +2502,20 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc)
{
- outputDesc->mId = id;
- mOutputs.add(id, outputDesc);
+ outputDesc->mIoHandle = output;
+ outputDesc->mId = nextUniqueId();
+ mOutputs.add(output, outputDesc);
+ nextAudioPortGeneration();
}
-void AudioPolicyManager::addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc)
{
- inputDesc->mId = id;
- mInputs.add(id, inputDesc);
+ inputDesc->mIoHandle = input;
+ inputDesc->mId = nextUniqueId();
+ mInputs.add(input, inputDesc);
+ nextAudioPortGeneration();
}
String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address)
@@ -2015,7 +2531,7 @@
SortedVector<audio_io_handle_t>& outputs,
const String8 address)
{
- AudioOutputDescriptor *desc;
+ sp<AudioOutputDescriptor> desc;
if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
// first list already open outputs that can be routed to this device
@@ -2027,7 +2543,7 @@
}
}
// then look for output profiles that can be routed to this device
- SortedVector<IOProfile *> profiles;
+ SortedVector< sp<IOProfile> > profiles;
for (size_t i = 0; i < mHwModules.size(); i++)
{
if (mHwModules[i]->mHandle == 0) {
@@ -2050,7 +2566,7 @@
// open outputs for matching profiles if needed. Direct outputs are also opened to
// query for dynamic parameters and will be closed later by setDeviceConnectionState()
for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
- IOProfile *profile = profiles[profile_index];
+ sp<IOProfile> profile = profiles[profile_index];
// nothing to do if one output is already opened for this profile
size_t j;
@@ -2096,7 +2612,7 @@
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadSamplingRates(value + 1, profile);
+ profile->loadSamplingRates(value + 1);
}
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
@@ -2106,7 +2622,7 @@
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadFormats(value + 1, profile);
+ profile->loadFormats(value + 1);
}
}
if (profile->mChannelMasks[0] == 0) {
@@ -2116,7 +2632,7 @@
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadOutChannels(value + 1, profile);
+ profile->loadOutChannels(value + 1);
}
}
if (((profile->mSamplingRates[0] == 0) &&
@@ -2158,7 +2674,7 @@
mPrimaryOutput);
if (duplicatedOutput != 0) {
// add duplicated output descriptor
- AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(NULL);
+ sp<AudioOutputDescriptor> dupOutputDesc = new AudioOutputDescriptor(NULL);
dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
dupOutputDesc->mSamplingRate = desc->mSamplingRate;
@@ -2172,6 +2688,7 @@
mPrimaryOutput, output);
mpClientInterface->closeOutput(output);
mOutputs.removeItem(output);
+ nextAudioPortGeneration();
output = 0;
}
}
@@ -2179,7 +2696,6 @@
}
if (output == 0) {
ALOGW("checkOutputsForDevice() could not open output for device %x", device);
- delete desc;
profiles.removeAt(profile_index);
profile_index--;
} else {
@@ -2211,7 +2727,7 @@
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
{
- IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
+ sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
if (profile->mSupportedDevices.types() & device) {
ALOGV("checkOutputsForDevice(): "
"clearing direct output profile %zu on module %zu", j, i);
@@ -2239,7 +2755,7 @@
SortedVector<audio_io_handle_t>& inputs,
const String8 address)
{
- AudioInputDescriptor *desc;
+ sp<AudioInputDescriptor> desc;
if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
// first list already open inputs that can be routed to this device
for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
@@ -2251,7 +2767,7 @@
}
// then look for input profiles that can be routed to this device
- SortedVector<IOProfile *> profiles;
+ SortedVector< sp<IOProfile> > profiles;
for (size_t module_idx = 0; module_idx < mHwModules.size(); module_idx++)
{
if (mHwModules[module_idx]->mHandle == 0) {
@@ -2279,7 +2795,7 @@
// query for dynamic parameters and will be closed later by setDeviceConnectionState()
for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
- IOProfile *profile = profiles[profile_index];
+ sp<IOProfile> profile = profiles[profile_index];
// nothing to do if one input is already opened for this profile
size_t input_index;
for (input_index = 0; input_index < mInputs.size(); input_index++) {
@@ -2317,7 +2833,7 @@
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadSamplingRates(value + 1, profile);
+ profile->loadSamplingRates(value + 1);
}
}
if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
@@ -2326,7 +2842,7 @@
ALOGV("checkInputsForDevice() direct input sup formats %s", reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadFormats(value + 1, profile);
+ profile->loadFormats(value + 1);
}
}
if (profile->mChannelMasks[0] == 0) {
@@ -2336,7 +2852,7 @@
reply.string());
value = strpbrk((char *)reply.string(), "=");
if (value != NULL) {
- loadInChannels(value + 1, profile);
+ profile->loadInChannels(value + 1);
}
}
if (((profile->mSamplingRates[0] == 0) && (profile->mSamplingRates.size() < 2)) ||
@@ -2354,7 +2870,6 @@
if (input == 0) {
ALOGW("checkInputsForDevice() could not open input for device 0x%X", device);
- delete desc;
profiles.removeAt(profile_index);
profile_index--;
} else {
@@ -2386,7 +2901,7 @@
for (size_t profile_index = 0;
profile_index < mHwModules[module_index]->mInputProfiles.size();
profile_index++) {
- IOProfile *profile = mHwModules[module_index]->mInputProfiles[profile_index];
+ sp<IOProfile> profile = mHwModules[module_index]->mInputProfiles[profile_index];
if (profile->mSupportedDevices.types() & device) {
ALOGV("checkInputsForDevice(): clearing direct input profile %d on module %d",
profile_index, module_index);
@@ -2415,7 +2930,7 @@
{
ALOGV("closeOutput(%d)", output);
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (outputDesc == NULL) {
ALOGW("closeOutput() unknown output %d", output);
return;
@@ -2423,11 +2938,11 @@
// look for duplicated outputs connected to the output being removed.
for (size_t i = 0; i < mOutputs.size(); i++) {
- AudioOutputDescriptor *dupOutputDesc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
if (dupOutputDesc->isDuplicated() &&
(dupOutputDesc->mOutput1 == outputDesc ||
dupOutputDesc->mOutput2 == outputDesc)) {
- AudioOutputDescriptor *outputDesc2;
+ sp<AudioOutputDescriptor> outputDesc2;
if (dupOutputDesc->mOutput1 == outputDesc) {
outputDesc2 = dupOutputDesc->mOutput2;
} else {
@@ -2445,7 +2960,6 @@
ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput);
mpClientInterface->closeOutput(duplicatedOutput);
- delete mOutputs.valueFor(duplicatedOutput);
mOutputs.removeItem(duplicatedOutput);
}
}
@@ -2455,13 +2969,13 @@
mpClientInterface->setParameters(output, param.toString());
mpClientInterface->closeOutput(output);
- delete outputDesc;
mOutputs.removeItem(output);
mPreviousOutputs = mOutputs;
+ nextAudioPortGeneration();
}
SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
- DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs)
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
@@ -2503,7 +3017,7 @@
strategy, srcOutputs[0], dstOutputs[0]);
// mute strategy while moving tracks from one output to another
for (size_t i = 0; i < srcOutputs.size(); i++) {
- AudioOutputDescriptor *desc = mOutputs.valueFor(srcOutputs[i]);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
if (desc->isStrategyActive(strategy)) {
setStrategyMute(strategy, true, srcOutputs[i]);
setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
@@ -2515,17 +3029,17 @@
audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs);
SortedVector<audio_io_handle_t> moved;
for (size_t i = 0; i < mEffects.size(); i++) {
- EffectDescriptor *desc = mEffects.valueAt(i);
- if (desc->mSession == AUDIO_SESSION_OUTPUT_MIX &&
- desc->mIo != fxOutput) {
- if (moved.indexOf(desc->mIo) < 0) {
+ sp<EffectDescriptor> effectDesc = mEffects.valueAt(i);
+ if (effectDesc->mSession == AUDIO_SESSION_OUTPUT_MIX &&
+ effectDesc->mIo != fxOutput) {
+ if (moved.indexOf(effectDesc->mIo) < 0) {
ALOGV("checkOutputForStrategy() moving effect %d to output %d",
mEffects.keyAt(i), fxOutput);
- mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, desc->mIo,
+ mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, effectDesc->mIo,
fxOutput);
- moved.add(desc->mIo);
+ moved.add(effectDesc->mIo);
}
- desc->mIo = fxOutput;
+ effectDesc->mIo = fxOutput;
}
}
}
@@ -2551,7 +3065,7 @@
audio_io_handle_t AudioPolicyManager::getA2dpOutput()
{
for (size_t i = 0; i < mOutputs.size(); i++) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) {
return mOutputs.keyAt(i);
}
@@ -2605,11 +3119,22 @@
}
}
-audio_devices_t AudioPolicyManager::getNewDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
{
audio_devices_t device = AUDIO_DEVICE_NONE;
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+
+ ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ if (index >= 0) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ if (patchDesc->mUid != mUidCached) {
+ ALOGV("getNewOutputDevice() device %08x forced by patch %d",
+ outputDesc->device(), outputDesc->mPatchHandle);
+ return outputDesc->device();
+ }
+ }
+
// check the following by order of priority to request a routing change if necessary:
// 1: the strategy enforced audible is active on the output:
// use device for strategy enforced audible
@@ -2638,7 +3163,27 @@
device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
}
- ALOGV("getNewDevice() selected device %x", device);
+ ALOGV("getNewOutputDevice() selected device %x", device);
+ return device;
+}
+
+audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input)
+{
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input);
+
+ ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ if (index >= 0) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ if (patchDesc->mUid != mUidCached) {
+ ALOGV("getNewInputDevice() device %08x forced by patch %d",
+ inputDesc->mDevice, inputDesc->mPatchHandle);
+ return inputDesc->mDevice;
+ }
+ }
+
+ audio_devices_t device = getDeviceForInputSource(inputDesc->mInputSource);
+
+ ALOGV("getNewInputDevice() selected device %x", device);
return device;
}
@@ -2647,15 +3192,22 @@
}
audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) {
- audio_devices_t devices;
// By checking the range of stream before calling getStrategy, we avoid
// getStrategy's behavior for invalid streams. getStrategy would do a ALOGE
// and then return STRATEGY_MEDIA, but we want to return the empty set.
if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_CNT) {
- devices = AUDIO_DEVICE_NONE;
- } else {
- AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
- devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+ return AUDIO_DEVICE_NONE;
+ }
+ audio_devices_t devices;
+ AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
+ devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+ SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs);
+ for (size_t i = 0; i < outputs.size(); i++) {
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
+ if (outputDesc->isStrategyActive(strategy)) {
+ devices = outputDesc->device();
+ break;
+ }
}
return devices;
}
@@ -2784,7 +3336,7 @@
}
device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE;
if (device) break;
- device = mDefaultOutputDevice->mType;
+ device = mDefaultOutputDevice->mDeviceType;
if (device == AUDIO_DEVICE_NONE) {
ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE");
}
@@ -2813,7 +3365,7 @@
}
device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER;
if (device) break;
- device = mDefaultOutputDevice->mType;
+ device = mDefaultOutputDevice->mDeviceType;
if (device == AUDIO_DEVICE_NONE) {
ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER");
}
@@ -2895,7 +3447,7 @@
// STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
device |= device2;
if (device) break;
- device = mDefaultOutputDevice->mType;
+ device = mDefaultOutputDevice->mDeviceType;
if (device == AUDIO_DEVICE_NONE) {
ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA");
}
@@ -2918,7 +3470,7 @@
mPreviousOutputs = mOutputs;
}
-uint32_t AudioPolicyManager::checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc,
+uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc,
audio_devices_t prevDevice,
uint32_t delayMs)
{
@@ -2947,7 +3499,7 @@
}
if (doMute) {
for (size_t j = 0; j < mOutputs.size(); j++) {
- AudioOutputDescriptor *desc = mOutputs.valueAt(j);
+ sp<AudioOutputDescriptor> desc = mOutputs.valueAt(j);
// skip output if it does not share any device with current output
if ((desc->supportedDevices() & outputDesc->supportedDevices())
== AUDIO_DEVICE_NONE) {
@@ -2981,9 +3533,9 @@
}
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
if (outputDesc->isStrategyActive((routing_strategy)i)) {
- setStrategyMute((routing_strategy)i, true, outputDesc->mId);
+ setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
// do tempMute unmute after twice the mute wait time
- setStrategyMute((routing_strategy)i, false, outputDesc->mId,
+ setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
muteWaitMs *2, device);
}
}
@@ -3001,16 +3553,17 @@
uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
audio_devices_t device,
bool force,
- int delayMs)
+ int delayMs,
+ audio_patch_handle_t *patchHandle)
{
ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
AudioParameter param;
uint32_t muteWaitMs;
if (outputDesc->isDuplicated()) {
- muteWaitMs = setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
- muteWaitMs += setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
+ muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
+ muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
return muteWaitMs;
}
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
@@ -3042,9 +3595,59 @@
}
ALOGV("setOutputDevice() changing device");
+
// do the routing
- param.addInt(String8(AudioParameter::keyRouting), (int)device);
- mpClientInterface->setParameters(output, param.toString(), delayMs);
+ if (device == AUDIO_DEVICE_NONE) {
+ resetOutputDevice(output, delayMs, NULL);
+ } else {
+ DeviceVector deviceList = mAvailableOutputDevices.getDevicesFromType(device);
+ if (!deviceList.isEmpty()) {
+ struct audio_patch patch;
+ outputDesc->toAudioPortConfig(&patch.sources[0]);
+ patch.num_sources = 1;
+ patch.num_sinks = 0;
+ for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {
+ deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);
+ patch.num_sinks++;
+ }
+ ssize_t index;
+ if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ }
+ sp< AudioPatch> patchDesc;
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
+ status_t status = mpClientInterface->createAudioPatch(&patch,
+ &afPatchHandle,
+ delayMs);
+ ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d"
+ "num_sources %d num_sinks %d",
+ status, afPatchHandle, patch.num_sources, patch.num_sinks);
+ if (status == NO_ERROR) {
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &patch, mUidCached);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = patch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ patchDesc->mUid = mUidCached;
+ if (patchHandle) {
+ *patchHandle = patchDesc->mHandle;
+ }
+ outputDesc->mPatchHandle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ }
+ }
+ }
// update stream volumes according to new device
applyStreamVolumes(output, device, delayMs);
@@ -3052,7 +3655,113 @@
return muteWaitMs;
}
-AudioPolicyManager::IOProfile *AudioPolicyManager::getInputProfile(audio_devices_t device,
+status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+ int delayMs,
+ audio_patch_handle_t *patchHandle)
+{
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ ssize_t index;
+ if (patchHandle) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ }
+ if (index < 0) {
+ return INVALID_OPERATION;
+ }
+ sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, delayMs);
+ ALOGV("resetOutputDevice() releaseAudioPatch returned %d", status);
+ outputDesc->mPatchHandle = 0;
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ return status;
+}
+
+status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input,
+ audio_devices_t device,
+ bool force,
+ audio_patch_handle_t *patchHandle)
+{
+ status_t status = NO_ERROR;
+
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input);
+ if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) {
+ inputDesc->mDevice = device;
+
+ DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device);
+ if (!deviceList.isEmpty()) {
+ struct audio_patch patch;
+ inputDesc->toAudioPortConfig(&patch.sinks[0]);
+ patch.num_sinks = 1;
+ //only one input device for now
+ deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]);
+ patch.num_sources = 1;
+ ssize_t index;
+ if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ }
+ sp< AudioPatch> patchDesc;
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
+ status_t status = mpClientInterface->createAudioPatch(&patch,
+ &afPatchHandle,
+ 0);
+ ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d",
+ status, afPatchHandle);
+ if (status == NO_ERROR) {
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &patch, mUidCached);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = patch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ patchDesc->mUid = mUidCached;
+ if (patchHandle) {
+ *patchHandle = patchDesc->mHandle;
+ }
+ inputDesc->mPatchHandle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
+ audio_patch_handle_t *patchHandle)
+{
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input);
+ ssize_t index;
+ if (patchHandle) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ }
+ if (index < 0) {
+ return INVALID_OPERATION;
+ }
+ sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
+ inputDesc->mPatchHandle = 0;
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ return status;
+}
+
+sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask)
@@ -3067,7 +3776,7 @@
}
for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
{
- IOProfile *profile = mHwModules[i]->mInputProfiles[j];
+ sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j];
// profile->log();
if (profile->isCompatibleProfile(device, samplingRate, format,
channelMask, AUDIO_OUTPUT_FLAG_NONE)) {
@@ -3093,6 +3802,12 @@
case AUDIO_SOURCE_DEFAULT:
case AUDIO_SOURCE_MIC:
+ if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
+ device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+ break;
+ }
+ // FALL THROUGH
+
case AUDIO_SOURCE_VOICE_RECOGNITION:
case AUDIO_SOURCE_HOTWORD:
case AUDIO_SOURCE_VOICE_COMMUNICATION:
@@ -3146,7 +3861,7 @@
audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs)
{
for (size_t i = 0; i < mInputs.size(); i++) {
- const AudioInputDescriptor * input_descriptor = mInputs.valueAt(i);
+ const sp<AudioInputDescriptor> input_descriptor = mInputs.valueAt(i);
if ((input_descriptor->mRefCount > 0)
&& (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) {
return mInputs.keyAt(i);
@@ -3269,6 +3984,11 @@
};
const AudioPolicyManager::VolumeCurvePoint
+ AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = {
+ {1, -56.0f}, {20, -34.0f}, {86, -10.0f}, {100, 0.0f}
+};
+
+const AudioPolicyManager::VolumeCurvePoint
AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = {
{1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f}
};
@@ -3382,6 +4102,8 @@
sSpeakerSonificationVolumeCurveDrc;
mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] =
sSpeakerSonificationVolumeCurveDrc;
+ mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] =
+ sSpeakerMediaVolumeCurveDrc;
}
}
@@ -3391,7 +4113,7 @@
audio_devices_t device)
{
float volume = 1.0;
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
StreamDescriptor &streamDesc = mStreams[stream];
if (device == AUDIO_DEVICE_NONE) {
@@ -3547,7 +4269,7 @@
audio_devices_t device)
{
StreamDescriptor &streamDesc = mStreams[stream];
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->device();
}
@@ -3592,7 +4314,7 @@
const routing_strategy stream_strategy = getStrategy(stream);
if ((stream_strategy == STRATEGY_SONIFICATION) ||
((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) {
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
stream, starting, outputDesc->mDevice, stateChange);
if (outputDesc->mRefCount[stream]) {
@@ -3645,13 +4367,13 @@
return MAX_EFFECTS_MEMORY;
}
+
// --- AudioOutputDescriptor class implementation
AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor(
- const IOProfile *profile)
- : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT),
- mChannelMask(0), mLatency(0),
- mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE),
+ const sp<IOProfile>& profile)
+ : mId(0), mIoHandle(0), mLatency(0),
+ mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0),
mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0)
{
// clear usage count for all stream types
@@ -3665,9 +4387,13 @@
mStrategyMutedByDevice[i] = false;
}
if (profile != NULL) {
+ mAudioPort = profile;
mSamplingRate = profile->mSamplingRates[0];
mFormat = profile->mFormats[0];
mChannelMask = profile->mChannelMasks[0];
+ if (profile->mGains.size() > 0) {
+ profile->mGains[0]->getDefaultConfig(&mGain);
+ }
mFlags = profile->mFlags;
}
}
@@ -3691,7 +4417,7 @@
}
bool AudioPolicyManager::AudioOutputDescriptor::sharesHwModuleWith(
- const AudioOutputDescriptor *outputDesc)
+ const sp<AudioOutputDescriptor> outputDesc)
{
if (isDuplicated()) {
return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
@@ -3770,6 +4496,36 @@
return false;
}
+void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+ AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
+ if (srcConfig != NULL) {
+ dstConfig->config_mask &= srcConfig->config_mask;
+ }
+ AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
+
+ dstConfig->id = mId;
+ dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
+ dstConfig->type = AUDIO_PORT_TYPE_MIX;
+ dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+ dstConfig->ext.mix.handle = mIoHandle;
+ dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
+}
+
+void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
+ struct audio_port *port) const
+{
+ mProfile->toAudioPort(port);
+ port->id = mId;
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.hw_module = mProfile->mModule->mHandle;
+ port->ext.mix.handle = mIoHandle;
+ port->ext.mix.latency_class =
+ mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+}
status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd)
{
@@ -3803,18 +4559,57 @@
// --- AudioInputDescriptor class implementation
-AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const IOProfile *profile)
- : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0),
- mDevice(AUDIO_DEVICE_NONE), mRefCount(0),
+AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
+ : mId(0), mIoHandle(0),
+ mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mRefCount(0),
mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile)
{
if (profile != NULL) {
+ mAudioPort = profile;
mSamplingRate = profile->mSamplingRates[0];
mFormat = profile->mFormats[0];
mChannelMask = profile->mChannelMasks[0];
+ if (profile->mGains.size() > 0) {
+ profile->mGains[0]->getDefaultConfig(&mGain);
+ }
+ } else {
+ mSamplingRate = 0;
+ mFormat = AUDIO_FORMAT_DEFAULT;
+ mChannelMask = 0;
}
}
+void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+ AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
+ if (srcConfig != NULL) {
+ dstConfig->config_mask &= srcConfig->config_mask;
+ }
+
+ AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
+
+ dstConfig->id = mId;
+ dstConfig->role = AUDIO_PORT_ROLE_SINK;
+ dstConfig->type = AUDIO_PORT_TYPE_MIX;
+ dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+ dstConfig->ext.mix.handle = mIoHandle;
+ dstConfig->ext.mix.usecase.source = mInputSource;
+}
+
+void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
+ struct audio_port *port) const
+{
+ mProfile->toAudioPort(port);
+ port->id = mId;
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.hw_module = mProfile->mModule->mHandle;
+ port->ext.mix.handle = mIoHandle;
+ port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
+}
+
status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd)
{
const size_t SIZE = 256;
@@ -3897,10 +4692,11 @@
return NO_ERROR;
}
-// --- IOProfile class implementation
+// --- HwModule class implementation
AudioPolicyManager::HwModule::HwModule(const char *name)
- : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), mHandle(0)
+ : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)),
+ mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0)
{
}
@@ -3908,15 +4704,147 @@
{
for (size_t i = 0; i < mOutputProfiles.size(); i++) {
mOutputProfiles[i]->mSupportedDevices.clear();
- delete mOutputProfiles[i];
}
for (size_t i = 0; i < mInputProfiles.size(); i++) {
mInputProfiles[i]->mSupportedDevices.clear();
- delete mInputProfiles[i];
}
free((void *)mName);
}
+status_t AudioPolicyManager::HwModule::loadInput(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+
+ while (node) {
+ if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+ profile->loadSamplingRates((char *)node->value);
+ } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+ profile->loadFormats((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ profile->loadInChannels((char *)node->value);
+ } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+ profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ mDeclaredDevices);
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ profile->loadGains(node);
+ }
+ node = node->next;
+ }
+ ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+ "loadInput() invalid supported devices");
+ ALOGW_IF(profile->mChannelMasks.size() == 0,
+ "loadInput() invalid supported channel masks");
+ ALOGW_IF(profile->mSamplingRates.size() == 0,
+ "loadInput() invalid supported sampling rates");
+ ALOGW_IF(profile->mFormats.size() == 0,
+ "loadInput() invalid supported formats");
+ if (!profile->mSupportedDevices.isEmpty() &&
+ (profile->mChannelMasks.size() != 0) &&
+ (profile->mSamplingRates.size() != 0) &&
+ (profile->mFormats.size() != 0)) {
+
+ ALOGV("loadInput() adding input Supported Devices %04x",
+ profile->mSupportedDevices.types());
+
+ mInputProfiles.add(profile);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioPolicyManager::HwModule::loadOutput(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+
+ while (node) {
+ if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+ profile->loadSamplingRates((char *)node->value);
+ } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+ profile->loadFormats((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ profile->loadOutChannels((char *)node->value);
+ } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+ profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ mDeclaredDevices);
+ } else if (strcmp(node->name, FLAGS_TAG) == 0) {
+ profile->mFlags = parseFlagNames((char *)node->value);
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ profile->loadGains(node);
+ }
+ node = node->next;
+ }
+ ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+ "loadOutput() invalid supported devices");
+ ALOGW_IF(profile->mChannelMasks.size() == 0,
+ "loadOutput() invalid supported channel masks");
+ ALOGW_IF(profile->mSamplingRates.size() == 0,
+ "loadOutput() invalid supported sampling rates");
+ ALOGW_IF(profile->mFormats.size() == 0,
+ "loadOutput() invalid supported formats");
+ if (!profile->mSupportedDevices.isEmpty() &&
+ (profile->mChannelMasks.size() != 0) &&
+ (profile->mSamplingRates.size() != 0) &&
+ (profile->mFormats.size() != 0)) {
+
+ ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
+ profile->mSupportedDevices.types(), profile->mFlags);
+
+ mOutputProfiles.add(profile);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioPolicyManager::HwModule::loadDevice(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ audio_devices_t type = AUDIO_DEVICE_NONE;
+ while (node) {
+ if (strcmp(node->name, DEVICE_TYPE) == 0) {
+ type = parseDeviceNames((char *)node->value);
+ break;
+ }
+ node = node->next;
+ }
+ if (type == AUDIO_DEVICE_NONE ||
+ (!audio_is_input_device(type) && !audio_is_output_device(type))) {
+ ALOGW("loadDevice() bad type %08x", type);
+ return BAD_VALUE;
+ }
+ sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
+ deviceDesc->mModule = this;
+
+ node = root->first_child;
+ while (node) {
+ if (strcmp(node->name, DEVICE_ADDRESS) == 0) {
+ deviceDesc->mAddress = String8((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ if (audio_is_input_device(type)) {
+ deviceDesc->loadInChannels((char *)node->value);
+ } else {
+ deviceDesc->loadOutChannels((char *)node->value);
+ }
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ deviceDesc->loadGains(node);
+ }
+ node = node->next;
+ }
+
+ ALOGV("loadDevice() adding device name %s type %08x address %s",
+ deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+
+ mDeclaredDevices.add(deviceDesc);
+
+ return NO_ERROR;
+}
+
void AudioPolicyManager::HwModule::dump(int fd)
{
const size_t SIZE = 256;
@@ -3927,6 +4855,8 @@
result.append(buffer);
snprintf(buffer, SIZE, " - handle: %d\n", mHandle);
result.append(buffer);
+ snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF);
+ result.append(buffer);
write(fd, result.string(), result.size());
if (mOutputProfiles.size()) {
write(fd, " - outputs:\n", strlen(" - outputs:\n"));
@@ -3944,10 +4874,519 @@
mInputProfiles[i]->dump(fd);
}
}
+ if (mDeclaredDevices.size()) {
+ write(fd, " - devices:\n", strlen(" - devices:\n"));
+ for (size_t i = 0; i < mDeclaredDevices.size(); i++) {
+ mDeclaredDevices[i]->dump(fd, 4, i);
+ }
+ }
}
-AudioPolicyManager::IOProfile::IOProfile(HwModule *module)
- : mFlags((audio_output_flags_t)0), mModule(module)
+// --- AudioPort class implementation
+
+
+AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type,
+ audio_port_role_t role, const sp<HwModule>& module) :
+ mName(name), mType(type), mRole(role), mModule(module)
+{
+ mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
+ ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
+}
+
+void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const
+{
+ port->role = mRole;
+ port->type = mType;
+ unsigned int i;
+ for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) {
+ port->sample_rates[i] = mSamplingRates[i];
+ }
+ port->num_sample_rates = i;
+ for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) {
+ port->channel_masks[i] = mChannelMasks[i];
+ }
+ port->num_channel_masks = i;
+ for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) {
+ port->formats[i] = mFormats[i];
+ }
+ port->num_formats = i;
+
+ ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+
+ for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
+ port->gains[i] = mGains[i]->mGain;
+ }
+ port->num_gains = i;
+}
+
+
+void AudioPolicyManager::AudioPort::loadSamplingRates(char *name)
+{
+ char *str = strtok(name, "|");
+
+ // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling
+ // rates should be read from the output stream after it is opened for the first time
+ if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+ mSamplingRates.add(0);
+ return;
+ }
+
+ while (str != NULL) {
+ uint32_t rate = atoi(str);
+ if (rate != 0) {
+ ALOGV("loadSamplingRates() adding rate %d", rate);
+ mSamplingRates.add(rate);
+ }
+ str = strtok(NULL, "|");
+ }
+}
+
+void AudioPolicyManager::AudioPort::loadFormats(char *name)
+{
+ char *str = strtok(name, "|");
+
+ // by convention, "0' in the first entry in mFormats indicates the supported formats
+ // should be read from the output stream after it is opened for the first time
+ if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+ mFormats.add(AUDIO_FORMAT_DEFAULT);
+ return;
+ }
+
+ while (str != NULL) {
+ audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable,
+ ARRAY_SIZE(sFormatNameToEnumTable),
+ str);
+ if (format != AUDIO_FORMAT_DEFAULT) {
+ mFormats.add(format);
+ }
+ str = strtok(NULL, "|");
+ }
+}
+
+void AudioPolicyManager::AudioPort::loadInChannels(char *name)
+{
+ const char *str = strtok(name, "|");
+
+ ALOGV("loadInChannels() %s", name);
+
+ if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+ mChannelMasks.add(0);
+ return;
+ }
+
+ while (str != NULL) {
+ audio_channel_mask_t channelMask =
+ (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+ ARRAY_SIZE(sInChannelsNameToEnumTable),
+ str);
+ if (channelMask != 0) {
+ ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+ mChannelMasks.add(channelMask);
+ }
+ str = strtok(NULL, "|");
+ }
+}
+
+void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
+{
+ const char *str = strtok(name, "|");
+
+ ALOGV("loadOutChannels() %s", name);
+
+ // by convention, "0' in the first entry in mChannelMasks indicates the supported channel
+ // masks should be read from the output stream after it is opened for the first time
+ if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+ mChannelMasks.add(0);
+ return;
+ }
+
+ while (str != NULL) {
+ audio_channel_mask_t channelMask =
+ (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+ ARRAY_SIZE(sOutChannelsNameToEnumTable),
+ str);
+ if (channelMask != 0) {
+ mChannelMasks.add(channelMask);
+ }
+ str = strtok(NULL, "|");
+ }
+ return;
+}
+
+audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name)
+{
+ const char *str = strtok(name, "|");
+
+ ALOGV("loadGainMode() %s", name);
+ audio_gain_mode_t mode = 0;
+ while (str != NULL) {
+ mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable,
+ ARRAY_SIZE(sGainModeNameToEnumTable),
+ str);
+ str = strtok(NULL, "|");
+ }
+ return mode;
+}
+
+void AudioPolicyManager::AudioPort::loadGain(cnode *root, int index)
+{
+ cnode *node = root->first_child;
+
+ sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask);
+
+ while (node) {
+ if (strcmp(node->name, GAIN_MODE) == 0) {
+ gain->mGain.mode = loadGainMode((char *)node->value);
+ } else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
+ if (mUseInChannelMask) {
+ gain->mGain.channel_mask =
+ (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+ ARRAY_SIZE(sInChannelsNameToEnumTable),
+ (char *)node->value);
+ } else {
+ gain->mGain.channel_mask =
+ (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+ ARRAY_SIZE(sOutChannelsNameToEnumTable),
+ (char *)node->value);
+ }
+ } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
+ gain->mGain.min_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
+ gain->mGain.max_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
+ gain->mGain.default_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
+ gain->mGain.step_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
+ gain->mGain.min_ramp_ms = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
+ gain->mGain.max_ramp_ms = atoi((char *)node->value);
+ }
+ node = node->next;
+ }
+
+ ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
+ gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value);
+
+ if (gain->mGain.mode == 0) {
+ return;
+ }
+ mGains.add(gain);
+}
+
+void AudioPolicyManager::AudioPort::loadGains(cnode *root)
+{
+ cnode *node = root->first_child;
+ int index = 0;
+ while (node) {
+ ALOGV("loadGains() loading gain %s", node->name);
+ loadGain(node, index++);
+ node = node->next;
+ }
+}
+
+status_t AudioPolicyManager::AudioPort::checkSamplingRate(uint32_t samplingRate) const
+{
+ for (size_t i = 0; i < mSamplingRates.size(); i ++) {
+ if (mSamplingRates[i] == samplingRate) {
+ return NO_ERROR;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t AudioPolicyManager::AudioPort::checkChannelMask(audio_channel_mask_t channelMask) const
+{
+ for (size_t i = 0; i < mChannelMasks.size(); i ++) {
+ if (mChannelMasks[i] == channelMask) {
+ return NO_ERROR;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t AudioPolicyManager::AudioPort::checkFormat(audio_format_t format) const
+{
+ for (size_t i = 0; i < mFormats.size(); i ++) {
+ if (mFormats[i] == format) {
+ return NO_ERROR;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t AudioPolicyManager::AudioPort::checkGain(const struct audio_gain_config *gainConfig,
+ int index) const
+{
+ if (index < 0 || (size_t)index >= mGains.size()) {
+ return BAD_VALUE;
+ }
+ return mGains[index]->checkConfig(gainConfig);
+}
+
+void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ if (mName.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
+ result.append(buffer);
+ }
+
+ if (mSamplingRates.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mSamplingRates.size(); i++) {
+ snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
+ result.append(buffer);
+ result.append(i == (mSamplingRates.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+
+ if (mChannelMasks.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mChannelMasks.size(); i++) {
+ snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
+ result.append(buffer);
+ result.append(i == (mChannelMasks.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+
+ if (mFormats.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- formats: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mFormats.size(); i++) {
+ snprintf(buffer, SIZE, "%-48s", enumToString(sFormatNameToEnumTable,
+ ARRAY_SIZE(sFormatNameToEnumTable),
+ mFormats[i]));
+ result.append(buffer);
+ result.append(i == (mFormats.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+ write(fd, result.string(), result.size());
+ if (mGains.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
+ write(fd, buffer, strlen(buffer) + 1);
+ result.append(buffer);
+ for (size_t i = 0; i < mGains.size(); i++) {
+ mGains[i]->dump(fd, spaces + 2, i);
+ }
+ }
+}
+
+// --- AudioGain class implementation
+
+AudioPolicyManager::AudioGain::AudioGain(int index, bool useInChannelMask)
+{
+ mIndex = index;
+ mUseInChannelMask = useInChannelMask;
+ memset(&mGain, 0, sizeof(struct audio_gain));
+}
+
+void AudioPolicyManager::AudioGain::getDefaultConfig(struct audio_gain_config *config)
+{
+ config->index = mIndex;
+ config->mode = mGain.mode;
+ config->channel_mask = mGain.channel_mask;
+ if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+ config->values[0] = mGain.default_value;
+ } else {
+ uint32_t numValues;
+ if (mUseInChannelMask) {
+ numValues = audio_channel_count_from_in_mask(mGain.channel_mask);
+ } else {
+ numValues = audio_channel_count_from_out_mask(mGain.channel_mask);
+ }
+ for (size_t i = 0; i < numValues; i++) {
+ config->values[i] = mGain.default_value;
+ }
+ }
+ if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+ config->ramp_duration_ms = mGain.min_ramp_ms;
+ }
+}
+
+status_t AudioPolicyManager::AudioGain::checkConfig(const struct audio_gain_config *config)
+{
+ if ((config->mode & ~mGain.mode) != 0) {
+ return BAD_VALUE;
+ }
+ if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+ if ((config->values[0] < mGain.min_value) ||
+ (config->values[0] > mGain.max_value)) {
+ return BAD_VALUE;
+ }
+ } else {
+ if ((config->channel_mask & ~mGain.channel_mask) != 0) {
+ return BAD_VALUE;
+ }
+ uint32_t numValues;
+ if (mUseInChannelMask) {
+ numValues = audio_channel_count_from_in_mask(config->channel_mask);
+ } else {
+ numValues = audio_channel_count_from_out_mask(config->channel_mask);
+ }
+ for (size_t i = 0; i < numValues; i++) {
+ if ((config->values[i] < mGain.min_value) ||
+ (config->values[i] > mGain.max_value)) {
+ return BAD_VALUE;
+ }
+ }
+ }
+ if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+ if ((config->ramp_duration_ms < mGain.min_ramp_ms) ||
+ (config->ramp_duration_ms > mGain.max_ramp_ms)) {
+ return BAD_VALUE;
+ }
+ }
+ return NO_ERROR;
+}
+
+void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms);
+ result.append(buffer);
+
+ write(fd, result.string(), result.size());
+}
+
+// --- AudioPortConfig class implementation
+
+AudioPolicyManager::AudioPortConfig::AudioPortConfig()
+{
+ mSamplingRate = 0;
+ mChannelMask = AUDIO_CHANNEL_NONE;
+ mFormat = AUDIO_FORMAT_INVALID;
+ mGain.index = -1;
+}
+
+status_t AudioPolicyManager::AudioPortConfig::applyAudioPortConfig(
+ const struct audio_port_config *config,
+ struct audio_port_config *backupConfig)
+{
+ struct audio_port_config localBackupConfig;
+ status_t status = NO_ERROR;
+
+ localBackupConfig.config_mask = config->config_mask;
+ toAudioPortConfig(&localBackupConfig);
+
+ if (mAudioPort == 0) {
+ status = NO_INIT;
+ goto exit;
+ }
+ if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ status = mAudioPort->checkSamplingRate(config->sample_rate);
+ if (status != NO_ERROR) {
+ goto exit;
+ }
+ mSamplingRate = config->sample_rate;
+ }
+ if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ status = mAudioPort->checkChannelMask(config->channel_mask);
+ if (status != NO_ERROR) {
+ goto exit;
+ }
+ mChannelMask = config->channel_mask;
+ }
+ if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ status = mAudioPort->checkFormat(config->format);
+ if (status != NO_ERROR) {
+ goto exit;
+ }
+ mFormat = config->format;
+ }
+ if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ status = mAudioPort->checkGain(&config->gain, config->gain.index);
+ if (status != NO_ERROR) {
+ goto exit;
+ }
+ mGain = config->gain;
+ }
+
+exit:
+ if (status != NO_ERROR) {
+ applyAudioPortConfig(&localBackupConfig);
+ }
+ if (backupConfig != NULL) {
+ *backupConfig = localBackupConfig;
+ }
+ return status;
+}
+
+void AudioPolicyManager::AudioPortConfig::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ dstConfig->sample_rate = mSamplingRate;
+ if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) {
+ dstConfig->sample_rate = srcConfig->sample_rate;
+ }
+ } else {
+ dstConfig->sample_rate = 0;
+ }
+ if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ dstConfig->channel_mask = mChannelMask;
+ if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) {
+ dstConfig->channel_mask = srcConfig->channel_mask;
+ }
+ } else {
+ dstConfig->channel_mask = AUDIO_CHANNEL_NONE;
+ }
+ if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ dstConfig->format = mFormat;
+ if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) {
+ dstConfig->format = srcConfig->format;
+ }
+ } else {
+ dstConfig->format = AUDIO_FORMAT_INVALID;
+ }
+ if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ dstConfig->gain = mGain;
+ if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) {
+ dstConfig->gain = srcConfig->gain;
+ }
+ } else {
+ dstConfig->gain.index = -1;
+ }
+ if (dstConfig->gain.index != -1) {
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+ } else {
+ dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN;
+ }
+}
+
+// --- IOProfile class implementation
+
+AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role,
+ const sp<HwModule>& module)
+ : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
{
}
@@ -3974,32 +5413,13 @@
if ((mFlags & flags) != flags) {
return false;
}
- size_t i;
- for (i = 0; i < mSamplingRates.size(); i++)
- {
- if (mSamplingRates[i] == samplingRate) {
- break;
- }
- }
- if (i == mSamplingRates.size()) {
+ if (checkSamplingRate(samplingRate) != NO_ERROR) {
return false;
}
- for (i = 0; i < mFormats.size(); i++)
- {
- if (mFormats[i] == format) {
- break;
- }
- }
- if (i == mFormats.size()) {
+ if (checkChannelMask(channelMask) != NO_ERROR) {
return false;
}
- for (i = 0; i < mChannelMasks.size(); i++)
- {
- if (mChannelMasks[i] == channelMask) {
- break;
- }
- }
- if (i == mChannelMasks.size()) {
+ if (checkFormat(format) != NO_ERROR) {
return false;
}
return true;
@@ -4011,42 +5431,16 @@
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, " - sampling rates: ");
- result.append(buffer);
- for (size_t i = 0; i < mSamplingRates.size(); i++) {
- snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
- result.append(buffer);
- result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", ");
- }
-
- snprintf(buffer, SIZE, " - channel masks: ");
- result.append(buffer);
- for (size_t i = 0; i < mChannelMasks.size(); i++) {
- snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
- result.append(buffer);
- result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", ");
- }
-
- snprintf(buffer, SIZE, " - formats: ");
- result.append(buffer);
- for (size_t i = 0; i < mFormats.size(); i++) {
- snprintf(buffer, SIZE, "0x%08x", mFormats[i]);
- result.append(buffer);
- result.append(i == (mFormats.size() - 1) ? "\n" : ", ");
- }
-
- snprintf(buffer, SIZE, " - devices:\n");
- result.append(buffer);
- write(fd, result.string(), result.size());
- DeviceDescriptor::dumpHeader(fd, 6);
- for (size_t i = 0; i < mSupportedDevices.size(); i++) {
- mSupportedDevices[i]->dump(fd, 6);
- }
+ AudioPort::dump(fd, 4);
snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags);
result.append(buffer);
-
+ snprintf(buffer, SIZE, " - devices:\n");
+ result.append(buffer);
write(fd, result.string(), result.size());
+ for (size_t i = 0; i < mSupportedDevices.size(); i++) {
+ mSupportedDevices[i]->dump(fd, 6, i);
+ }
}
void AudioPolicyManager::IOProfile::log()
@@ -4077,13 +5471,28 @@
// --- DeviceDescriptor implementation
+
+AudioPolicyManager::DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) :
+ AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
+ audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
+ AUDIO_PORT_ROLE_SOURCE,
+ NULL),
+ mDeviceType(type), mAddress(""),
+ mChannelMask(AUDIO_CHANNEL_NONE), mId(0)
+{
+ mAudioPort = this;
+ if (mGains.size() > 0) {
+ mGains[0]->getDefaultConfig(&mGain);
+ }
+}
+
bool AudioPolicyManager::DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const
{
// Devices are considered equal if they:
// - are of the same type (a device type cannot be AUDIO_DEVICE_NONE)
// - have the same address or one device does not specify the address
// - have the same channel mask or one device does not specify the channel mask
- return (mType == other->mType) &&
+ return (mDeviceType == other->mDeviceType) &&
(mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) &&
(mChannelMask == 0 || other->mChannelMask == 0 ||
mChannelMask == other->mChannelMask);
@@ -4091,11 +5500,11 @@
void AudioPolicyManager::DeviceVector::refreshTypes()
{
- mTypes = AUDIO_DEVICE_NONE;
+ mDeviceTypes = AUDIO_DEVICE_NONE;
for(size_t i = 0; i < size(); i++) {
- mTypes |= itemAt(i)->mType;
+ mDeviceTypes |= itemAt(i)->mDeviceType;
}
- ALOGV("DeviceVector::refreshTypes() mTypes %08x", mTypes);
+ ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes);
}
ssize_t AudioPolicyManager::DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const
@@ -4118,7 +5527,7 @@
refreshTypes();
}
} else {
- ALOGW("DeviceVector::add device %08x already in", item->mType);
+ ALOGW("DeviceVector::add device %08x already in", item->mDeviceType);
ret = -1;
}
return ret;
@@ -4130,7 +5539,7 @@
ssize_t ret = indexOf(item);
if (ret < 0) {
- ALOGW("DeviceVector::remove device %08x not in", item->mType);
+ ALOGW("DeviceVector::remove device %08x not in", item->mDeviceType);
} else {
ret = SortedVector::removeAt(ret);
if (ret >= 0) {
@@ -4151,32 +5560,150 @@
uint32_t i = 31 - __builtin_clz(types);
uint32_t type = 1 << i;
types &= ~type;
- add(new DeviceDescriptor(type | role_bit));
+ add(new DeviceDescriptor(String8(""), type | role_bit));
}
}
-void AudioPolicyManager::DeviceDescriptor::dumpHeader(int fd, int spaces)
+void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name,
+ const DeviceVector& declaredDevices)
{
- const size_t SIZE = 256;
- char buffer[SIZE];
-
- snprintf(buffer, SIZE, "%*s%-48s %-2s %-8s %-32s \n",
- spaces, "", "Type", "ID", "Cnl Mask", "Address");
- write(fd, buffer, strlen(buffer));
+ char *devName = strtok(name, "|");
+ while (devName != NULL) {
+ if (strlen(devName) != 0) {
+ audio_devices_t type = stringToEnum(sDeviceNameToEnumTable,
+ ARRAY_SIZE(sDeviceNameToEnumTable),
+ devName);
+ if (type != AUDIO_DEVICE_NONE) {
+ add(new DeviceDescriptor(String8(""), type));
+ } else {
+ sp<DeviceDescriptor> deviceDesc =
+ declaredDevices.getDeviceFromName(String8(devName));
+ if (deviceDesc != 0) {
+ add(deviceDesc);
+ }
+ }
+ }
+ devName = strtok(NULL, "|");
+ }
}
-status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces) const
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice(
+ audio_devices_t type, String8 address) const
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < size(); i++) {
+ if (itemAt(i)->mDeviceType == type) {
+ device = itemAt(i);
+ if (itemAt(i)->mAddress = address) {
+ break;
+ }
+ }
+ }
+ ALOGV("DeviceVector::getDevice() for type %d address %s found %p",
+ type, address.string(), device.get());
+ return device;
+}
+
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId(
+ audio_port_handle_t id) const
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < size(); i++) {
+ ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%d)->mId %d", id, i, itemAt(i)->mId);
+ if (itemAt(i)->mId == id) {
+ device = itemAt(i);
+ break;
+ }
+ }
+ return device;
+}
+
+AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType(
+ audio_devices_t type) const
+{
+ DeviceVector devices;
+ for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) {
+ if (itemAt(i)->mDeviceType & type & ~AUDIO_DEVICE_BIT_IN) {
+ devices.add(itemAt(i));
+ type &= ~itemAt(i)->mDeviceType;
+ ALOGV("DeviceVector::getDevicesFromType() for type %x found %p",
+ itemAt(i)->mDeviceType, itemAt(i).get());
+ }
+ }
+ return devices;
+}
+
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName(
+ const String8& name) const
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < size(); i++) {
+ if (itemAt(i)->mName == name) {
+ device = itemAt(i);
+ break;
+ }
+ }
+ return device;
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN;
+ if (srcConfig != NULL) {
+ dstConfig->config_mask &= srcConfig->config_mask;
+ }
+
+ AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig);
+
+ dstConfig->id = mId;
+ dstConfig->role = audio_is_output_device(mDeviceType) ?
+ AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
+ dstConfig->type = AUDIO_PORT_TYPE_DEVICE;
+ dstConfig->ext.device.type = mDeviceType;
+ dstConfig->ext.device.hw_module = mModule->mHandle;
+ strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const
+{
+ ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType);
+ AudioPort::toAudioPort(port);
+ port->id = mId;
+ toAudioPortConfig(&port->active_config);
+ port->ext.device.type = mDeviceType;
+ port->ext.device.hw_module = mModule->mHandle;
+ strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+}
+
+status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const
{
const size_t SIZE = 256;
char buffer[SIZE];
+ String8 result;
- snprintf(buffer, SIZE, "%*s%-48s %2d %08x %-32s \n",
- spaces, "",
- enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
- mType),
- mId, mChannelMask, mAddress.string());
- write(fd, buffer, strlen(buffer));
+ snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1);
+ result.append(buffer);
+ if (mId != 0) {
+ snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId);
+ result.append(buffer);
+ }
+ snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
+ enumToString(sDeviceNameToEnumTable,
+ ARRAY_SIZE(sDeviceNameToEnumTable),
+ mDeviceType));
+ result.append(buffer);
+ if (mAddress.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string());
+ result.append(buffer);
+ }
+ if (mChannelMask != AUDIO_CHANNEL_NONE) {
+ snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", spaces, "", mChannelMask);
+ result.append(buffer);
+ }
+ write(fd, result.string(), result.size());
+ AudioPort::dump(fd, spaces);
return NO_ERROR;
}
@@ -4225,200 +5752,30 @@
return device;
}
-void AudioPolicyManager::loadSamplingRates(char *name, IOProfile *profile)
-{
- char *str = strtok(name, "|");
-
- // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling
- // rates should be read from the output stream after it is opened for the first time
- if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
- profile->mSamplingRates.add(0);
- return;
- }
-
- while (str != NULL) {
- uint32_t rate = atoi(str);
- if (rate != 0) {
- ALOGV("loadSamplingRates() adding rate %d", rate);
- profile->mSamplingRates.add(rate);
- }
- str = strtok(NULL, "|");
- }
- return;
-}
-
-void AudioPolicyManager::loadFormats(char *name, IOProfile *profile)
-{
- char *str = strtok(name, "|");
-
- // by convention, "0' in the first entry in mFormats indicates the supported formats
- // should be read from the output stream after it is opened for the first time
- if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
- profile->mFormats.add(AUDIO_FORMAT_DEFAULT);
- return;
- }
-
- while (str != NULL) {
- audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable,
- ARRAY_SIZE(sFormatNameToEnumTable),
- str);
- if (format != AUDIO_FORMAT_DEFAULT) {
- profile->mFormats.add(format);
- }
- str = strtok(NULL, "|");
- }
- return;
-}
-
-void AudioPolicyManager::loadInChannels(char *name, IOProfile *profile)
-{
- const char *str = strtok(name, "|");
-
- ALOGV("loadInChannels() %s", name);
-
- if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
- profile->mChannelMasks.add(0);
- return;
- }
-
- while (str != NULL) {
- audio_channel_mask_t channelMask =
- (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
- ARRAY_SIZE(sInChannelsNameToEnumTable),
- str);
- if (channelMask != 0) {
- ALOGV("loadInChannels() adding channelMask %04x", channelMask);
- profile->mChannelMasks.add(channelMask);
- }
- str = strtok(NULL, "|");
- }
- return;
-}
-
-void AudioPolicyManager::loadOutChannels(char *name, IOProfile *profile)
-{
- const char *str = strtok(name, "|");
-
- ALOGV("loadOutChannels() %s", name);
-
- // by convention, "0' in the first entry in mChannelMasks indicates the supported channel
- // masks should be read from the output stream after it is opened for the first time
- if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
- profile->mChannelMasks.add(0);
- return;
- }
-
- while (str != NULL) {
- audio_channel_mask_t channelMask =
- (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
- ARRAY_SIZE(sOutChannelsNameToEnumTable),
- str);
- if (channelMask != 0) {
- profile->mChannelMasks.add(channelMask);
- }
- str = strtok(NULL, "|");
- }
- return;
-}
-
-status_t AudioPolicyManager::loadInput(cnode *root, HwModule *module)
-{
- cnode *node = root->first_child;
-
- IOProfile *profile = new IOProfile(module);
-
- while (node) {
- if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
- loadSamplingRates((char *)node->value, profile);
- } else if (strcmp(node->name, FORMATS_TAG) == 0) {
- loadFormats((char *)node->value, profile);
- } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
- loadInChannels((char *)node->value, profile);
- } else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
- }
- node = node->next;
- }
- ALOGW_IF(profile->mSupportedDevices.isEmpty(),
- "loadInput() invalid supported devices");
- ALOGW_IF(profile->mChannelMasks.size() == 0,
- "loadInput() invalid supported channel masks");
- ALOGW_IF(profile->mSamplingRates.size() == 0,
- "loadInput() invalid supported sampling rates");
- ALOGW_IF(profile->mFormats.size() == 0,
- "loadInput() invalid supported formats");
- if (!profile->mSupportedDevices.isEmpty() &&
- (profile->mChannelMasks.size() != 0) &&
- (profile->mSamplingRates.size() != 0) &&
- (profile->mFormats.size() != 0)) {
-
- ALOGV("loadInput() adding input Supported Devices %04x",
- profile->mSupportedDevices.types());
-
- module->mInputProfiles.add(profile);
- return NO_ERROR;
- } else {
- delete profile;
- return BAD_VALUE;
- }
-}
-
-status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
-{
- cnode *node = root->first_child;
-
- IOProfile *profile = new IOProfile(module);
-
- while (node) {
- if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
- loadSamplingRates((char *)node->value, profile);
- } else if (strcmp(node->name, FORMATS_TAG) == 0) {
- loadFormats((char *)node->value, profile);
- } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
- loadOutChannels((char *)node->value, profile);
- } else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
- } else if (strcmp(node->name, FLAGS_TAG) == 0) {
- profile->mFlags = parseFlagNames((char *)node->value);
- }
- node = node->next;
- }
- ALOGW_IF(profile->mSupportedDevices.isEmpty(),
- "loadOutput() invalid supported devices");
- ALOGW_IF(profile->mChannelMasks.size() == 0,
- "loadOutput() invalid supported channel masks");
- ALOGW_IF(profile->mSamplingRates.size() == 0,
- "loadOutput() invalid supported sampling rates");
- ALOGW_IF(profile->mFormats.size() == 0,
- "loadOutput() invalid supported formats");
- if (!profile->mSupportedDevices.isEmpty() &&
- (profile->mChannelMasks.size() != 0) &&
- (profile->mSamplingRates.size() != 0) &&
- (profile->mFormats.size() != 0)) {
-
- ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
- profile->mSupportedDevices.types(), profile->mFlags);
-
- module->mOutputProfiles.add(profile);
- return NO_ERROR;
- } else {
- delete profile;
- return BAD_VALUE;
- }
-}
-
void AudioPolicyManager::loadHwModule(cnode *root)
{
- cnode *node = config_find(root, OUTPUTS_TAG);
status_t status = NAME_NOT_FOUND;
+ cnode *node;
+ sp<HwModule> module = new HwModule(root->name);
- HwModule *module = new HwModule(root->name);
-
+ node = config_find(root, DEVICES_TAG);
+ if (node != NULL) {
+ node = node->first_child;
+ while (node) {
+ ALOGV("loadHwModule() loading device %s", node->name);
+ status_t tmpStatus = module->loadDevice(node);
+ if (status == NAME_NOT_FOUND || status == NO_ERROR) {
+ status = tmpStatus;
+ }
+ node = node->next;
+ }
+ }
+ node = config_find(root, OUTPUTS_TAG);
if (node != NULL) {
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading output %s", node->name);
- status_t tmpStatus = loadOutput(node, module);
+ status_t tmpStatus = module->loadOutput(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
@@ -4430,17 +5787,17 @@
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading input %s", node->name);
- status_t tmpStatus = loadInput(node, module);
+ status_t tmpStatus = module->loadInput(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
+ loadGlobalConfig(root, module);
+
if (status == NO_ERROR) {
mHwModules.add(module);
- } else {
- delete module;
}
}
@@ -4459,16 +5816,23 @@
}
}
-void AudioPolicyManager::loadGlobalConfig(cnode *root)
+void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& module)
{
cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
+
if (node == NULL) {
return;
}
+ DeviceVector declaredDevices;
+ if (module != NULL) {
+ declaredDevices = module->mDeclaredDevices;
+ }
+
node = node->first_child;
while (node) {
if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
- mAvailableOutputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+ mAvailableOutputDevices.loadDevicesFromName((char *)node->value,
+ declaredDevices);
ALOGV("loadGlobalConfig() Attached Output Devices %08x",
mAvailableOutputDevices.types());
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
@@ -4476,17 +5840,24 @@
ARRAY_SIZE(sDeviceNameToEnumTable),
(char *)node->value);
if (device != AUDIO_DEVICE_NONE) {
- mDefaultOutputDevice = new DeviceDescriptor(device);
+ mDefaultOutputDevice = new DeviceDescriptor(String8(""), device);
} else {
ALOGW("loadGlobalConfig() default device not specified");
}
- ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mType);
+ ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mDeviceType);
} else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
- mAvailableInputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+ mAvailableInputDevices.loadDevicesFromName((char *)node->value,
+ declaredDevices);
ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types());
} else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
mSpeakerDrcEnabled = stringToBool((char *)node->value);
ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled);
+ } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) {
+ uint32_t major, minor;
+ sscanf((char *)node->value, "%u.%u", &major, &minor);
+ module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor);
+ ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u",
+ module->mHalVersion, major, minor);
}
node = node->next;
}
@@ -4504,9 +5875,9 @@
root = config_node("", "");
config_load(root, data);
- loadGlobalConfig(root);
loadHwModules(root);
-
+ // legacy audio_policy.conf files have one global_configuration section
+ loadGlobalConfig(root, getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
config_free(root);
free(root);
free(data);
@@ -4518,15 +5889,16 @@
void AudioPolicyManager::defaultAudioPolicyConfig(void)
{
- HwModule *module;
- IOProfile *profile;
- sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
+ sp<HwModule> module;
+ sp<IOProfile> profile;
+ sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""),
+ AUDIO_DEVICE_IN_BUILTIN_MIC);
mAvailableOutputDevices.add(mDefaultOutputDevice);
mAvailableInputDevices.add(defaultInputDevice);
module = new HwModule("primary");
- profile = new IOProfile(module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
profile->mSamplingRates.add(44100);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4534,7 +5906,7 @@
profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
module->mOutputProfiles.add(profile);
- profile = new IOProfile(module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
profile->mSamplingRates.add(8000);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index f00fa8a..1abeb6a 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -140,6 +140,23 @@
virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation);
+ virtual status_t getAudioPort(struct audio_port *port);
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid);
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid);
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation);
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+ virtual void clearAudioPatches(uid_t uid);
+
protected:
enum routing_strategy {
@@ -173,60 +190,136 @@
DEVICE_CATEGORY_CNT
};
- class IOProfile;
+ class HwModule;
- class DeviceDescriptor: public RefBase
+ class AudioGain: public RefBase
{
public:
- DeviceDescriptor(audio_devices_t type, String8 address,
- audio_channel_mask_t channelMask) :
- mType(type), mAddress(address),
- mChannelMask(channelMask), mId(0) {}
+ AudioGain(int index, bool useInChannelMask);
+ virtual ~AudioGain() {}
- DeviceDescriptor(audio_devices_t type) :
- mType(type), mAddress(""),
- mChannelMask(0), mId(0) {}
+ void dump(int fd, int spaces, int index) const;
- status_t dump(int fd, int spaces) const;
- static void dumpHeader(int fd, int spaces);
+ void getDefaultConfig(struct audio_gain_config *config);
+ status_t checkConfig(const struct audio_gain_config *config);
+ int mIndex;
+ struct audio_gain mGain;
+ bool mUseInChannelMask;
+ };
+
+ class AudioPort: public virtual RefBase
+ {
+ public:
+ AudioPort(const String8& name, audio_port_type_t type,
+ audio_port_role_t role, const sp<HwModule>& module);
+ virtual ~AudioPort() {}
+
+ virtual void toAudioPort(struct audio_port *port) const;
+
+ void loadSamplingRates(char *name);
+ void loadFormats(char *name);
+ void loadOutChannels(char *name);
+ void loadInChannels(char *name);
+
+ audio_gain_mode_t loadGainMode(char *name);
+ void loadGain(cnode *root, int index);
+ void loadGains(cnode *root);
+
+ status_t checkSamplingRate(uint32_t samplingRate) const;
+ status_t checkChannelMask(audio_channel_mask_t channelMask) const;
+ status_t checkFormat(audio_format_t format) const;
+ status_t checkGain(const struct audio_gain_config *gainConfig, int index) const;
+
+ void dump(int fd, int spaces) const;
+
+ String8 mName;
+ audio_port_type_t mType;
+ audio_port_role_t mRole;
+ bool mUseInChannelMask;
+ // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
+ // indicates the supported parameters should be read from the output stream
+ // after it is opened for the first time
+ Vector <uint32_t> mSamplingRates; // supported sampling rates
+ Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
+ Vector <audio_format_t> mFormats; // supported audio formats
+ Vector < sp<AudioGain> > mGains; // gain controllers
+ sp<HwModule> mModule; // audio HW module exposing this I/O stream
+ };
+
+ class AudioPortConfig: public virtual RefBase
+ {
+ public:
+ AudioPortConfig();
+ virtual ~AudioPortConfig() {}
+
+ status_t applyAudioPortConfig(const struct audio_port_config *config,
+ struct audio_port_config *backupConfig = NULL);
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const = 0;
+ sp<AudioPort> mAudioPort;
+ uint32_t mSamplingRate;
+ audio_format_t mFormat;
+ audio_channel_mask_t mChannelMask;
+ struct audio_gain_config mGain;
+ };
+
+
+ class AudioPatch: public RefBase
+ {
+ public:
+ AudioPatch(audio_patch_handle_t handle,
+ const struct audio_patch *patch, uid_t uid) :
+ mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {}
+
+ audio_patch_handle_t mHandle;
+ struct audio_patch mPatch;
+ uid_t mUid;
+ audio_patch_handle_t mAfPatchHandle;
+ };
+
+ class DeviceDescriptor: public AudioPort, public AudioPortConfig
+ {
+ public:
+ DeviceDescriptor(const String8& name, audio_devices_t type);
+
+ virtual ~DeviceDescriptor() {}
bool equals(const sp<DeviceDescriptor>& other) const;
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
- audio_devices_t mType;
+ virtual void toAudioPort(struct audio_port *port) const;
+
+ status_t dump(int fd, int spaces, int index) const;
+
+ audio_devices_t mDeviceType;
String8 mAddress;
audio_channel_mask_t mChannelMask;
- uint32_t mId;
+ audio_port_handle_t mId;
};
class DeviceVector : public SortedVector< sp<DeviceDescriptor> >
{
public:
- DeviceVector() : SortedVector(), mTypes(AUDIO_DEVICE_NONE) {}
+ DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {}
ssize_t add(const sp<DeviceDescriptor>& item);
ssize_t remove(const sp<DeviceDescriptor>& item);
ssize_t indexOf(const sp<DeviceDescriptor>& item) const;
- audio_devices_t types() const { return mTypes; }
+ audio_devices_t types() const { return mDeviceTypes; }
void loadDevicesFromType(audio_devices_t types);
+ void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+
+ sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
+ DeviceVector getDevicesFromType(audio_devices_t types) const;
+ sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
+ sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
private:
void refreshTypes();
- audio_devices_t mTypes;
- };
-
- class HwModule {
- public:
- HwModule(const char *name);
- ~HwModule();
-
- void dump(int fd);
-
- const char *const mName; // base name of the audio HW module (primary, a2dp ...)
- audio_module_handle_t mHandle;
- Vector <IOProfile *> mOutputProfiles; // output profiles exposed by this module
- Vector <IOProfile *> mInputProfiles; // input profiles exposed by this module
+ audio_devices_t mDeviceTypes;
};
// the IOProfile class describes the capabilities of an output or input stream.
@@ -234,11 +327,11 @@
// It is used by the policy manager to determine if an output or input is suitable for
// a given use case, open/close it accordingly and connect/disconnect audio tracks
// to/from it.
- class IOProfile
+ class IOProfile : public AudioPort
{
public:
- IOProfile(HwModule *module);
- ~IOProfile();
+ IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module);
+ virtual ~IOProfile();
bool isCompatibleProfile(audio_devices_t device,
uint32_t samplingRate,
@@ -249,17 +342,30 @@
void dump(int fd);
void log();
- // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
- // indicates the supported parameters should be read from the output stream
- // after it is opened for the first time
- Vector <uint32_t> mSamplingRates; // supported sampling rates
- Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
- Vector <audio_format_t> mFormats; // supported audio formats
DeviceVector mSupportedDevices; // supported devices
// (devices this output can be routed to)
audio_output_flags_t mFlags; // attribute flags (e.g primary output,
// direct output...). For outputs only.
- HwModule *mModule; // audio HW module exposing this I/O stream
+ };
+
+ class HwModule : public RefBase{
+ public:
+ HwModule(const char *name);
+ ~HwModule();
+
+ status_t loadOutput(cnode *root);
+ status_t loadInput(cnode *root);
+ status_t loadDevice(cnode *root);
+
+ void dump(int fd);
+
+ const char *const mName; // base name of the audio HW module (primary, a2dp ...)
+ uint32_t mHalVersion; // audio HAL API version
+ audio_module_handle_t mHandle;
+ Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
+ Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module
+ DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf
+
};
// default volume curve
@@ -268,6 +374,7 @@
static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT];
// volume curve for media strategy on speakers
static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT];
+ static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT];
// volume curve for sonification strategy on speakers
static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT];
static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT];
@@ -281,10 +388,10 @@
// descriptor for audio outputs. Used to maintain current configuration of each opened audio output
// and keep track of the usage of this output by each audio stream type.
- class AudioOutputDescriptor
+ class AudioOutputDescriptor: public AudioPortConfig
{
public:
- AudioOutputDescriptor(const IOProfile *profile);
+ AudioOutputDescriptor(const sp<IOProfile>& profile);
status_t dump(int fd);
@@ -294,7 +401,7 @@
bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
audio_devices_t supportedDevices();
uint32_t latency();
- bool sharesHwModuleWith(const AudioOutputDescriptor *outputDesc);
+ bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
bool isActive(uint32_t inPastMs = 0) const;
bool isStreamActive(audio_stream_type_t stream,
uint32_t inPastMs = 0,
@@ -303,20 +410,23 @@
uint32_t inPastMs = 0,
nsecs_t sysTime = 0) const;
- audio_io_handle_t mId; // output handle
- uint32_t mSamplingRate; //
- audio_format_t mFormat; //
- audio_channel_mask_t mChannelMask; // output configuration
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
+ void toAudioPort(struct audio_port *port) const;
+
+ audio_port_handle_t mId;
+ audio_io_handle_t mIoHandle; // output handle
uint32_t mLatency; //
audio_output_flags_t mFlags; //
audio_devices_t mDevice; // current device this output is routed to
+ audio_patch_handle_t mPatchHandle;
uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
nsecs_t mStopTime[AUDIO_STREAM_CNT];
- AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output
- AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output
+ sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output
+ sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume
int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter
- const IOProfile *mProfile; // I/O profile this output derives from
+ const sp<IOProfile> mProfile; // I/O profile this output derives from
bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
// device selection. See checkDeviceMuteStrategies()
uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
@@ -324,21 +434,24 @@
// descriptor for audio inputs. Used to maintain current configuration of each opened audio input
// and keep track of the usage of this input.
- class AudioInputDescriptor
+ class AudioInputDescriptor: public AudioPortConfig
{
public:
- AudioInputDescriptor(const IOProfile *profile);
+ AudioInputDescriptor(const sp<IOProfile>& profile);
status_t dump(int fd);
- audio_io_handle_t mId; // input handle
- uint32_t mSamplingRate; //
- audio_format_t mFormat; // input configuration
- audio_channel_mask_t mChannelMask; //
+ audio_port_handle_t mId;
+ audio_io_handle_t mIoHandle; // input handle
audio_devices_t mDevice; // current device this input is routed to
+ audio_patch_handle_t mPatchHandle;
uint32_t mRefCount; // number of AudioRecord clients using this output
audio_source_t mInputSource; // input source selected by application (mediarecorder.h)
- const IOProfile *mProfile; // I/O profile this output derives from
+ const sp<IOProfile> mProfile; // I/O profile this output derives from
+
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
+ void toAudioPort(struct audio_port *port) const;
};
// stream descriptor used for volume control
@@ -359,7 +472,7 @@
};
// stream descriptor used for volume control
- class EffectDescriptor
+ class EffectDescriptor : public RefBase
{
public:
@@ -372,8 +485,8 @@
bool mEnabled; // enabled state: CPU load being used or not
};
- void addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc);
- void addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc);
+ void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc);
+ void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc);
// return the strategy corresponding to a given stream type
static routing_strategy getStrategy(audio_stream_type_t stream);
@@ -397,7 +510,17 @@
uint32_t setOutputDevice(audio_io_handle_t output,
audio_devices_t device,
bool force = false,
- int delayMs = 0);
+ int delayMs = 0,
+ audio_patch_handle_t *patchHandle = NULL);
+ status_t resetOutputDevice(audio_io_handle_t output,
+ int delayMs = 0,
+ audio_patch_handle_t *patchHandle = NULL);
+ status_t setInputDevice(audio_io_handle_t input,
+ audio_devices_t device,
+ bool force = false,
+ audio_patch_handle_t *patchHandle = NULL);
+ status_t resetInputDevice(audio_io_handle_t input,
+ audio_patch_handle_t *patchHandle = NULL);
// select input device corresponding to requested audio source
virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource);
@@ -484,16 +607,18 @@
// must be called every time a condition that affects the device choice for a given output is
// changed: connected device, phone state, force use, output start, output stop..
// see getDeviceForStrategy() for the use of fromCache parameter
+ audio_devices_t getNewOutputDevice(audio_io_handle_t output, bool fromCache);
- audio_devices_t getNewDevice(audio_io_handle_t output, bool fromCache);
// updates cache of device used by all strategies (mDeviceForStrategy[])
// must be called every time a condition that affects the device choice for a given strategy is
// changed: connected device, phone state, force use...
// cached values are used by getDeviceForStrategy() if parameter fromCache is true.
// Must be called after checkOutputForAllStrategies()
-
void updateDevicesAndOutputs();
+ // selects the most appropriate device on input for current state
+ audio_devices_t getNewInputDevice(audio_io_handle_t input);
+
virtual uint32_t getMaxEffectsCpuLoad();
virtual uint32_t getMaxEffectsMemory();
#ifdef AUDIO_POLICY_TEST
@@ -502,7 +627,7 @@
int testOutputIndex(audio_io_handle_t output);
#endif //AUDIO_POLICY_TEST
- status_t setEffectEnabled(EffectDescriptor *pDesc, bool enabled);
+ status_t setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled);
// returns the category the device belongs to with regard to volume curve management
static device_category getDeviceCategory(audio_devices_t device);
@@ -511,7 +636,7 @@
static audio_devices_t getDeviceForVolume(audio_devices_t device);
SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device,
- DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> openOutputs);
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs);
bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1,
SortedVector<audio_io_handle_t>& outputs2);
@@ -519,17 +644,17 @@
// if muting, wait for the audio in pcm buffer to be drained before proceeding
// if unmuting, unmute only after the specified delay
// Returns the number of ms waited
- uint32_t checkDeviceMuteStrategies(AudioOutputDescriptor *outputDesc,
+ uint32_t checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc,
audio_devices_t prevDevice,
uint32_t delayMs);
audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs,
audio_output_flags_t flags);
- IOProfile *getInputProfile(audio_devices_t device,
+ sp<IOProfile> getInputProfile(audio_devices_t device,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask);
- IOProfile *getProfileForDirectOutput(audio_devices_t device,
+ sp<IOProfile> getProfileForDirectOutput(audio_devices_t device,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
@@ -539,6 +664,14 @@
bool isNonOffloadableEffectEnabled();
+ status_t addAudioPatch(audio_patch_handle_t handle,
+ const sp<AudioPatch>& patch);
+ status_t removeAudioPatch(audio_patch_handle_t handle);
+
+ sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
+ sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const;
+ sp<HwModule> getModuleForDevice(audio_devices_t device) const;
+ sp<HwModule> getModuleFromName(const char *name) const;
//
// Audio policy configuration file parsing (audio_policy.conf)
//
@@ -551,31 +684,24 @@
static bool stringToBool(const char *value);
static audio_output_flags_t parseFlagNames(char *name);
static audio_devices_t parseDeviceNames(char *name);
- void loadSamplingRates(char *name, IOProfile *profile);
- void loadFormats(char *name, IOProfile *profile);
- void loadOutChannels(char *name, IOProfile *profile);
- void loadInChannels(char *name, IOProfile *profile);
- status_t loadOutput(cnode *root, HwModule *module);
- status_t loadInput(cnode *root, HwModule *module);
void loadHwModule(cnode *root);
void loadHwModules(cnode *root);
- void loadGlobalConfig(cnode *root);
+ void loadGlobalConfig(cnode *root, const sp<HwModule>& module);
status_t loadAudioPolicyConfig(const char *path);
void defaultAudioPolicyConfig(void);
+ uid_t mUidCached;
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
audio_io_handle_t mPrimaryOutput; // primary output handle
// list of descriptors for outputs currently opened
- DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs;
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mOutputs;
// copy of mOutputs before setDeviceConnectionState() opens new outputs
// reset to mOutputs when updateDevicesAndOutputs() is called.
- DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs;
- DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors
- DeviceVector mAvailableOutputDevices; // bit field of all available output devices
- DeviceVector mAvailableInputDevices; // bit field of all available input devices
- // without AUDIO_DEVICE_BIT_IN to allow direct bit
- // field comparisons
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mPreviousOutputs;
+ DefaultKeyedVector<audio_io_handle_t, sp<AudioInputDescriptor> > mInputs; // list of input descriptors
+ DeviceVector mAvailableOutputDevices; // all available output devices
+ DeviceVector mAvailableInputDevices; // all available input devices
int mPhoneState; // current phone state
audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; // current forced use configuration
@@ -590,14 +716,17 @@
static const uint32_t MAX_EFFECTS_MEMORY = 512;
uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects
uint32_t mTotalEffectsMemory; // current memory used by effects
- KeyedVector<int, EffectDescriptor *> mEffects; // list of registered audio effects
+ KeyedVector<int, sp<EffectDescriptor> > mEffects; // list of registered audio effects
bool mA2dpSuspended; // true if A2DP output is suspended
sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time
bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path
// to boost soft sounds, used to adjust volume curves accordingly
- Vector <HwModule *> mHwModules;
+ Vector < sp<HwModule> > mHwModules;
volatile int32_t mNextUniqueId;
+ volatile int32_t mAudioPortGeneration;
+
+ DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches;
#ifdef AUDIO_POLICY_TEST
Mutex mLock;
@@ -622,6 +751,8 @@
void handleNotificationRoutingForStream(audio_stream_type_t stream);
static bool isVirtualInputDevice(audio_devices_t device);
uint32_t nextUniqueId();
+ uint32_t nextAudioPortGeneration();
+ uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; }
// converts device address to string sent to audio HAL via setParameters
static String8 addressToParameter(audio_devices_t device, const String8 address);
};
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index 4e9a2f0..e86f4a2 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -148,8 +148,123 @@
delete mAudioPolicyManager;
delete mAudioPolicyClient;
#endif
+
+ mNotificationClients.clear();
}
+// A notification client is always registered by AudioSystem when the client process
+// connects to AudioPolicyService.
+void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+{
+
+ Mutex::Autolock _l(mLock);
+
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (mNotificationClients.indexOfKey(uid) < 0) {
+ sp<NotificationClient> notificationClient = new NotificationClient(this,
+ client,
+ uid);
+ ALOGV("registerClient() client %p, uid %d", client.get(), uid);
+
+ mNotificationClients.add(uid, notificationClient);
+
+ sp<IBinder> binder = client->asBinder();
+ binder->linkToDeath(notificationClient);
+ }
+}
+
+// removeNotificationClient() is called when the client process dies.
+void AudioPolicyService::removeNotificationClient(uid_t uid)
+{
+ Mutex::Autolock _l(mLock);
+
+ mNotificationClients.removeItem(uid);
+
+#ifndef USE_LEGACY_AUDIO_POLICY
+ if (mAudioPolicyManager) {
+ mAudioPolicyManager->clearAudioPatches(uid);
+ }
+#endif
+}
+
+void AudioPolicyService::onAudioPortListUpdate()
+{
+ mOutputCommandThread->updateAudioPortListCommand();
+}
+
+void AudioPolicyService::doOnAudioPortListUpdate()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onAudioPortListUpdate();
+ }
+}
+
+void AudioPolicyService::onAudioPatchListUpdate()
+{
+ mOutputCommandThread->updateAudioPatchListCommand();
+}
+
+status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs)
+{
+ return mAudioCommandThread->createAudioPatchCommand(patch, handle, delayMs);
+}
+
+status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs)
+{
+ return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs);
+}
+
+void AudioPolicyService::doOnAudioPatchListUpdate()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onAudioPatchListUpdate();
+ }
+}
+
+status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
+ int delayMs)
+{
+ return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
+}
+
+AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
+ const sp<IAudioPolicyServiceClient>& client,
+ uid_t uid)
+ : mService(service), mUid(uid), mAudioPolicyServiceClient(client)
+{
+}
+
+AudioPolicyService::NotificationClient::~NotificationClient()
+{
+}
+
+void AudioPolicyService::NotificationClient::binderDied(const wp<IBinder>& who __unused)
+{
+ sp<NotificationClient> keep(this);
+ sp<AudioPolicyService> service = mService.promote();
+ if (service != 0) {
+ service->removeNotificationClient(mUid);
+ }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPortListUpdate()
+{
+ if (mAudioPolicyServiceClient != 0) {
+ mAudioPolicyServiceClient->onAudioPortListUpdate();
+ }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPatchListUpdate()
+{
+ if (mAudioPolicyServiceClient != 0) {
+ mAudioPolicyServiceClient->onAudioPatchListUpdate();
+ }
+}
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
@@ -284,7 +399,8 @@
mLock.lock();
while (!exitPending())
{
- while (!mAudioCommands.isEmpty()) {
+ sp<AudioPolicyService> svc;
+ while (!mAudioCommands.isEmpty() && !exitPending()) {
nsecs_t curTime = systemTime();
// commands are sorted by increasing time stamp: execute them from index 0 and up
if (mAudioCommands[0]->mTime <= curTime) {
@@ -337,7 +453,7 @@
StopOutputData *data = (StopOutputData *)command->mParam.get();
ALOGV("AudioCommandThread() processing stop output %d",
data->mIO);
- sp<AudioPolicyService> svc = mService.promote();
+ svc = mService.promote();
if (svc == 0) {
break;
}
@@ -349,7 +465,7 @@
ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get();
ALOGV("AudioCommandThread() processing release output %d",
data->mIO);
- sp<AudioPolicyService> svc = mService.promote();
+ svc = mService.promote();
if (svc == 0) {
break;
}
@@ -357,6 +473,56 @@
svc->doReleaseOutput(data->mIO);
mLock.lock();
}break;
+ case CREATE_AUDIO_PATCH: {
+ CreateAudioPatchData *data = (CreateAudioPatchData *)command->mParam.get();
+ ALOGV("AudioCommandThread() processing create audio patch");
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ command->mStatus = PERMISSION_DENIED;
+ } else {
+ command->mStatus = af->createAudioPatch(&data->mPatch, &data->mHandle);
+ }
+ } break;
+ case RELEASE_AUDIO_PATCH: {
+ ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mParam.get();
+ ALOGV("AudioCommandThread() processing release audio patch");
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ command->mStatus = PERMISSION_DENIED;
+ } else {
+ command->mStatus = af->releaseAudioPatch(data->mHandle);
+ }
+ } break;
+ case UPDATE_AUDIOPORT_LIST: {
+ ALOGV("AudioCommandThread() processing update audio port list");
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnAudioPortListUpdate();
+ mLock.lock();
+ }break;
+ case UPDATE_AUDIOPATCH_LIST: {
+ ALOGV("AudioCommandThread() processing update audio patch list");
+ svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnAudioPatchListUpdate();
+ mLock.lock();
+ }break;
+ case SET_AUDIOPORT_CONFIG: {
+ SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get();
+ ALOGV("AudioCommandThread() processing set port config");
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ command->mStatus = PERMISSION_DENIED;
+ } else {
+ command->mStatus = af->setAudioPortConfig(&data->mConfig);
+ }
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
@@ -377,9 +543,16 @@
if (mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
- ALOGV("AudioCommandThread() going to sleep");
- mWaitWorkCV.waitRelative(mLock, waitTime);
- ALOGV("AudioCommandThread() waking up");
+ // release mLock before releasing strong reference on the service as
+ // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock.
+ mLock.unlock();
+ svc.clear();
+ mLock.lock();
+ if (!exitPending()) {
+ ALOGV("AudioCommandThread() going to sleep");
+ mWaitWorkCV.waitRelative(mLock, waitTime);
+ ALOGV("AudioCommandThread() waking up");
+ }
}
mLock.unlock();
return false;
@@ -516,6 +689,70 @@
sendCommand(command);
}
+status_t AudioPolicyService::AudioCommandThread::createAudioPatchCommand(
+ const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = CREATE_AUDIO_PATCH;
+ CreateAudioPatchData *data = new CreateAudioPatchData();
+ data->mPatch = *patch;
+ data->mHandle = *handle;
+ command->mParam = data;
+ command->mWaitStatus = true;
+ ALOGV("AudioCommandThread() adding create patch delay %d", delayMs);
+ status = sendCommand(command, delayMs);
+ if (status == NO_ERROR) {
+ *handle = data->mHandle;
+ }
+ return status;
+}
+
+status_t AudioPolicyService::AudioCommandThread::releaseAudioPatchCommand(audio_patch_handle_t handle,
+ int delayMs)
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = RELEASE_AUDIO_PATCH;
+ ReleaseAudioPatchData *data = new ReleaseAudioPatchData();
+ data->mHandle = handle;
+ command->mParam = data;
+ command->mWaitStatus = true;
+ ALOGV("AudioCommandThread() adding release patch delay %d", delayMs);
+ return sendCommand(command, delayMs);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = UPDATE_AUDIOPORT_LIST;
+ ALOGV("AudioCommandThread() adding update audio port list");
+ sendCommand(command);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
+{
+ sp<AudioCommand>command = new AudioCommand();
+ command->mCommand = UPDATE_AUDIOPATCH_LIST;
+ ALOGV("AudioCommandThread() adding update audio patch list");
+ sendCommand(command);
+}
+
+status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand(
+ const struct audio_port_config *config, int delayMs)
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = SET_AUDIOPORT_CONFIG;
+ SetAudioPortConfigData *data = new SetAudioPortConfigData();
+ data->mConfig = *config;
+ command->mParam = data;
+ command->mWaitStatus = true;
+ ALOGV("AudioCommandThread() adding set port config delay %d", delayMs);
+ return sendCommand(command, delayMs);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 26037e4..40f589b 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -140,11 +140,41 @@
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
virtual bool isOffloadSupported(const audio_offload_info_t &config);
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation);
+ virtual status_t getAudioPort(struct audio_port *port);
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle);
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle);
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation);
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+
status_t doStopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
void doReleaseOutput(audio_io_handle_t output);
+ status_t clientCreateAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs);
+ status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs);
+ virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+ int delayMs);
+
+ void removeNotificationClient(uid_t uid);
+ void onAudioPortListUpdate();
+ void doOnAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+ void doOnAudioPatchListUpdate();
+
private:
AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
@@ -169,7 +199,12 @@
SET_PARAMETERS,
SET_VOICE_VOLUME,
STOP_OUTPUT,
- RELEASE_OUTPUT
+ RELEASE_OUTPUT,
+ CREATE_AUDIO_PATCH,
+ RELEASE_AUDIO_PATCH,
+ UPDATE_AUDIOPORT_LIST,
+ UPDATE_AUDIOPATCH_LIST,
+ SET_AUDIOPORT_CONFIG,
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -196,6 +231,16 @@
void releaseOutputCommand(audio_io_handle_t output);
status_t sendCommand(sp<AudioCommand>& command, int delayMs = 0);
void insertCommand_l(sp<AudioCommand>& command, int delayMs = 0);
+ status_t createAudioPatchCommand(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs);
+ status_t releaseAudioPatchCommand(audio_patch_handle_t handle,
+ int delayMs);
+ void updateAudioPortListCommand();
+ void updateAudioPatchListCommand();
+ status_t setAudioPortConfigCommand(const struct audio_port_config *config,
+ int delayMs);
+ void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
class AudioCommandData;
@@ -261,6 +306,22 @@
audio_io_handle_t mIO;
};
+ class CreateAudioPatchData : public AudioCommandData {
+ public:
+ struct audio_patch mPatch;
+ audio_patch_handle_t mHandle;
+ };
+
+ class ReleaseAudioPatchData : public AudioCommandData {
+ public:
+ audio_patch_handle_t mHandle;
+ };
+
+ class SetAudioPortConfigData : public AudioCommandData {
+ public:
+ struct audio_port_config mConfig;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -405,10 +466,48 @@
audio_io_handle_t srcOutput,
audio_io_handle_t dstOutput);
+ /* Create a patch between several source and sink ports */
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ int delayMs);
+
+ /* Release a patch */
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ int delayMs);
+
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs);
+
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
+
private:
AudioPolicyService *mAudioPolicyService;
};
+ // --- Notification Client ---
+ class NotificationClient : public IBinder::DeathRecipient {
+ public:
+ NotificationClient(const sp<AudioPolicyService>& service,
+ const sp<IAudioPolicyServiceClient>& client,
+ uid_t uid);
+ virtual ~NotificationClient();
+
+ void onAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ NotificationClient(const NotificationClient&);
+ NotificationClient& operator = (const NotificationClient&);
+
+ const wp<AudioPolicyService> mService;
+ const uid_t mUid;
+ const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+ };
+
static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
@@ -445,6 +544,8 @@
KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
+
+ DefaultKeyedVector< uid_t, sp<NotificationClient> > mNotificationClients;
};
}; // namespace android
diff --git a/services/audiopolicy/audio_policy.conf b/services/audiopolicy/audio_policy.conf
new file mode 100644
index 0000000..9b83fef
--- /dev/null
+++ b/services/audiopolicy/audio_policy.conf
@@ -0,0 +1,145 @@
+#
+# Template audio policy configuration file
+#
+
+# Global configuration section:
+# - before audio HAL version 3.0:
+# lists input and output devices always present on the device
+# as well as the output device selected by default.
+# Devices are designated by a string that corresponds to the enum in audio.h
+#
+# global_configuration {
+# attached_output_devices AUDIO_DEVICE_OUT_SPEAKER
+# default_output_device AUDIO_DEVICE_OUT_SPEAKER
+# attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX
+# }
+#
+# - after and including audio HAL 3.0 the global_configuration section is included in each
+# hardware module section.
+# it also includes the audio HAL version of this hw module:
+# global_configuration {
+# ...
+# audio_hal_version <major.minor> # audio HAL version in e.g. 3.0
+# }
+# other attributes (attached devices, default device) have to be included in the
+# global_configuration section of each hardware module
+
+
+# audio hardware module section: contains descriptors for all audio hw modules present on the
+# device. Each hw module node is named after the corresponding hw module library base name.
+# For instance, "primary" corresponds to audio.primary.<device>.so.
+# The "primary" module is mandatory and must include at least one output with
+# AUDIO_OUTPUT_FLAG_PRIMARY flag.
+# Each module descriptor contains one or more output profile descriptors and zero or more
+# input profile descriptors. Each profile lists all the parameters supported by a given output
+# or input stream category.
+# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding
+# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n".
+#
+# For audio HAL version posterior to 3.0 the following sections or sub sections can be present in
+# a hw module section:
+# - A "global_configuration" section: see above
+# - Optionally a "devices" section:
+# This section contains descriptors for audio devices with attributes like an address or a
+# gain controller. The syntax for the devices section and device descriptor is as follows:
+# devices {
+# <device name> { # <device name>: any string without space
+# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER
+# address <address> # optional: device address, char string less than 64 in length
+# }
+# }
+# - one or more "gains" sections can be present in a device descriptor section.
+# If present, they describe the capabilities of gain controllers attached to this input or
+# output device. e.g. :
+# <device name> { # <device name>: any string without space
+# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER
+# address <address> # optional: device address, char string less than 64 in length
+# gains {
+# <gain name> {
+# mode <gain modes supported> # e.g. AUDIO_GAIN_MODE_CHANNELS
+# channel_mask <controlled channels> # needed if mode AUDIO_GAIN_MODE_CHANNELS
+# min_value_mB <min value in millibel>
+# max_value_mB <max value in millibel>
+# default_value_mB <default value in millibel>
+# step_value_mB <step value in millibel>
+# min_ramp_ms <min duration in ms> # needed if mode AUDIO_GAIN_MODE_RAMP
+# max_ramp_ms <max duration ms> # needed if mode AUDIO_GAIN_MODE_RAMP
+# }
+# }
+# }
+# - when a device descriptor is present, output and input profiles can refer to this device by
+# its name in their "devices" section instead of specifying a device type. e.g. :
+# outputs {
+# primary {
+# sampling_rates 44100
+# channel_masks AUDIO_CHANNEL_OUT_STEREO
+# formats AUDIO_FORMAT_PCM_16_BIT
+# devices <device name>
+# flags AUDIO_OUTPUT_FLAG_PRIMARY
+# }
+# }
+# sample audio_policy.conf file below
+
+audio_hw_modules {
+ primary {
+ global_configuration {
+ attached_output_devices AUDIO_DEVICE_OUT_SPEAKER
+ default_output_device AUDIO_DEVICE_OUT_SPEAKER
+ attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC
+ audio_hal_version 3.0
+ }
+ devices {
+ speaker {
+ type AUDIO_DEVICE_OUT_SPEAKER
+ gains {
+ gain_1 {
+ mode AUDIO_GAIN_MODE_JOINT
+ min_value_mB -8400
+ max_value_mB 4000
+ default_value_mB 0
+ step_value_mB 100
+ }
+ }
+ }
+ }
+ outputs {
+ primary {
+ sampling_rates 48000
+ channel_masks AUDIO_CHANNEL_OUT_STEREO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices speaker
+ flags AUDIO_OUTPUT_FLAG_PRIMARY
+ }
+ }
+ inputs {
+ primary {
+ sampling_rates 8000|16000
+ channel_masks AUDIO_CHANNEL_IN_MONO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_IN_BUILTIN_MIC
+ }
+ }
+ }
+ r_submix {
+ global_configuration {
+ attached_input_devices AUDIO_DEVICE_IN_REMOTE_SUBMIX
+ audio_hal_version 2.0
+ }
+ outputs {
+ submix {
+ sampling_rates 48000
+ channel_masks AUDIO_CHANNEL_OUT_STEREO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+ }
+ }
+ inputs {
+ submix {
+ sampling_rates 48000
+ channel_masks AUDIO_CHANNEL_IN_STEREO
+ formats AUDIO_FORMAT_PCM_16_BIT
+ devices AUDIO_DEVICE_IN_REMOTE_SUBMIX
+ }
+ }
+ }
+}
diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h
new file mode 100644
index 0000000..2535a67
--- /dev/null
+++ b/services/audiopolicy/audio_policy_conf.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_POLICY_CONF_H
+#define ANDROID_AUDIO_POLICY_CONF_H
+
+
+/////////////////////////////////////////////////
+// Definitions for audio policy configuration file (audio_policy.conf)
+/////////////////////////////////////////////////
+
+#define AUDIO_HARDWARE_MODULE_ID_MAX_LEN 32
+
+#define AUDIO_POLICY_CONFIG_FILE "/system/etc/audio_policy.conf"
+#define AUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf"
+
+// global configuration
+#define GLOBAL_CONFIG_TAG "global_configuration"
+
+#define ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices"
+#define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device"
+#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices"
+#define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled"
+#define AUDIO_HAL_VERSION_TAG "audio_hal_version"
+
+// hw modules descriptions
+#define AUDIO_HW_MODULE_TAG "audio_hw_modules"
+
+#define OUTPUTS_TAG "outputs"
+#define INPUTS_TAG "inputs"
+
+#define SAMPLING_RATES_TAG "sampling_rates"
+#define FORMATS_TAG "formats"
+#define CHANNELS_TAG "channel_masks"
+#define DEVICES_TAG "devices"
+#define FLAGS_TAG "flags"
+
+#define DYNAMIC_VALUE_TAG "dynamic" // special value for "channel_masks", "sampling_rates" and
+ // "formats" in outputs descriptors indicating that supported
+ // values should be queried after opening the output.
+
+#define DEVICES_TAG "devices"
+#define DEVICE_TYPE "type"
+#define DEVICE_ADDRESS "address"
+
+#define MIXERS_TAG "mixers"
+#define MIXER_TYPE "type"
+#define MIXER_TYPE_MUX "mux"
+#define MIXER_TYPE_MIX "mix"
+
+#define GAINS_TAG "gains"
+#define GAIN_MODE "mode"
+#define GAIN_CHANNELS "channel_mask"
+#define GAIN_MIN_VALUE "min_value_mB"
+#define GAIN_MAX_VALUE "max_value_mB"
+#define GAIN_DEFAULT_VALUE "default_value_mB"
+#define GAIN_STEP_VALUE "step_value_mB"
+#define GAIN_MIN_RAMP_MS "min_ramp_ms"
+#define GAIN_MAX_RAMP_MS "max_ramp_ms"
+
+
+
+#endif // ANDROID_AUDIO_POLICY_CONF_H
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fe1e707..9fd35e1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -39,6 +39,8 @@
#include <utils/String16.h>
#include <utils/Trace.h>
#include <system/camera_vendor_tags.h>
+#include <system/camera_metadata.h>
+#include <system/camera.h>
#include "CameraService.h"
#include "api1/CameraClient.h"
@@ -178,6 +180,9 @@
{
Mutex::Autolock al(mServiceLock);
+ /* Remove cached parameters from shim cache */
+ mShimParams.removeItem(cameraId);
+
/* Find all clients that we need to disconnect */
sp<BasicClient> client = mClient[cameraId].promote();
if (client.get() != NULL) {
@@ -236,6 +241,96 @@
return rc;
}
+
+status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
+ status_t ret = OK;
+ struct CameraInfo info;
+ if ((ret = getCameraInfo(cameraId, &info)) != OK) {
+ return ret;
+ }
+
+ CameraMetadata shimInfo;
+ int32_t orientation = static_cast<int32_t>(info.orientation);
+ if ((ret = shimInfo.update(ANDROID_SENSOR_ORIENTATION, &orientation, 1)) != OK) {
+ return ret;
+ }
+
+ uint8_t facing = (info.facing == CAMERA_FACING_FRONT) ?
+ ANDROID_LENS_FACING_FRONT : ANDROID_LENS_FACING_BACK;
+ if ((ret = shimInfo.update(ANDROID_LENS_FACING, &facing, 1)) != OK) {
+ return ret;
+ }
+
+ ssize_t index = -1;
+ { // Scope for service lock
+ Mutex::Autolock lock(mServiceLock);
+ index = mShimParams.indexOfKey(cameraId);
+ // Release service lock so initializeShimMetadata can be called correctly.
+ }
+
+ if (index < 0) {
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ ret = initializeShimMetadata(cameraId);
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ if (ret != OK) {
+ return ret;
+ }
+ }
+
+ Vector<Size> sizes;
+ Vector<int32_t> formats;
+ const char* supportedPreviewFormats;
+ { // Scope for service lock
+ Mutex::Autolock lock(mServiceLock);
+ index = mShimParams.indexOfKey(cameraId);
+
+ mShimParams[index].getSupportedPreviewSizes(/*out*/sizes);
+
+ mShimParams[index].getSupportedPreviewFormats(/*out*/formats);
+ }
+
+ // Always include IMPLEMENTATION_DEFINED
+ formats.add(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+ const size_t INTS_PER_CONFIG = 4;
+
+ // Build available stream configurations metadata
+ size_t streamConfigSize = sizes.size() * formats.size() * INTS_PER_CONFIG;
+ int32_t streamConfigs[streamConfigSize];
+ size_t configIndex = 0;
+ for (size_t i = 0; i < formats.size(); ++i) {
+ for (size_t j = 0; j < sizes.size(); ++j) {
+ streamConfigs[configIndex++] = formats[i];
+ streamConfigs[configIndex++] = sizes[j].width;
+ streamConfigs[configIndex++] = sizes[j].height;
+ streamConfigs[configIndex++] =
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
+ }
+ }
+
+ if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+ streamConfigs, streamConfigSize)) != OK) {
+ return ret;
+ }
+
+ int64_t fakeMinFrames[0];
+ // TODO: Fixme, don't fake min frame durations.
+ if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+ fakeMinFrames, 0)) != OK) {
+ return ret;
+ }
+
+ int64_t fakeStalls[0];
+ // TODO: Fixme, don't fake stall durations.
+ if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+ fakeStalls, 0)) != OK) {
+ return ret;
+ }
+
+ *cameraInfo = shimInfo;
+ return OK;
+}
+
status_t CameraService::getCameraCharacteristics(int cameraId,
CameraMetadata* cameraInfo) {
if (!cameraInfo) {
@@ -248,33 +343,37 @@
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;
- }
+ status_t ret = OK;
+ if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0 ||
+ getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1 ) {
+ /**
+ * Backwards compatibility mode for old HALs:
+ * - Convert CameraInfo into static CameraMetadata properties.
+ * - Retrieve cached CameraParameters for this camera. If none exist,
+ * attempt to open CameraClient and retrieve the CameraParameters.
+ * - Convert cached CameraParameters into static CameraMetadata
+ * properties.
+ */
+ ALOGI("%s: Switching to HAL1 shim implementation...", __FUNCTION__);
- 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;
- }
+ if ((ret = generateShimMetadata(cameraId, cameraInfo)) != OK) {
+ return ret;
+ }
- struct camera_info info;
- status_t ret = mModule->get_camera_info(cameraId, &info);
- *cameraInfo = info.static_camera_characteristics;
+ } else {
+ /**
+ * Normal HAL 2.1+ codepath.
+ */
+ struct camera_info info;
+ ret = mModule->get_camera_info(cameraId, &info);
+ *cameraInfo = info.static_camera_characteristics;
+ }
return ret;
}
@@ -285,12 +384,6 @@
return -ENODEV;
}
- if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_2) {
- // TODO: Remove this check once HAL1 shim is in place.
- ALOGW("%s: Only HAL module version V2.2 or higher supports vendor tags", __FUNCTION__);
- return -EOPNOTSUPP;
- }
-
desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
return OK;
}
@@ -372,6 +465,54 @@
return true;
}
+status_t CameraService::initializeShimMetadata(int cameraId) {
+ int pid = getCallingPid();
+ int uid = getCallingUid();
+ status_t ret = validateConnect(cameraId, uid);
+ if (ret != OK) {
+ return ret;
+ }
+
+ bool needsNewClient = false;
+ sp<Client> client;
+
+ String16 internalPackageName("media");
+ { // Scope for service lock
+ Mutex::Autolock lock(mServiceLock);
+ if (mClient[cameraId] != NULL) {
+ client = static_cast<Client*>(mClient[cameraId].promote().get());
+ }
+ if (client == NULL) {
+ needsNewClient = true;
+ ret = connectHelperLocked(/*cameraClient*/NULL, // Empty binder callbacks
+ cameraId,
+ internalPackageName,
+ uid,
+ pid,
+ client);
+
+ if (ret != OK) {
+ return ret;
+ }
+ }
+
+ if (client == NULL) {
+ ALOGE("%s: Could not connect to client camera device.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ String8 rawParams = client->getParameters();
+ CameraParameters params(rawParams);
+ mShimParams.add(cameraId, params);
+ }
+
+ // Close client if one was opened solely for this call
+ if (needsNewClient) {
+ client->disconnect();
+ }
+ return OK;
+}
+
status_t CameraService::validateConnect(int cameraId,
/*inout*/
int& clientUid) const {
@@ -468,6 +609,64 @@
return true;
}
+status_t CameraService::connectHelperLocked(const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ int callingPid,
+ /*out*/
+ sp<Client>& client) {
+
+ 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:
+ client = new CameraClient(this, cameraClient,
+ clientPackageName, cameraId,
+ facing, callingPid, clientUid, getpid());
+ break;
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ case CAMERA_DEVICE_API_VERSION_3_0:
+ case CAMERA_DEVICE_API_VERSION_3_1:
+ case CAMERA_DEVICE_API_VERSION_3_2:
+ client = new Camera2Client(this, cameraClient,
+ clientPackageName, cameraId,
+ facing, callingPid, clientUid, getpid(),
+ deviceVersion);
+ 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;
+ }
+
+ mClient[cameraId] = client;
+ LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
+ getpid());
+
+ return OK;
+}
+
status_t CameraService::connect(
const sp<ICameraClient>& cameraClient,
int cameraId,
@@ -501,52 +700,16 @@
return OK;
}
- 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:
- client = new CameraClient(this, cameraClient,
- clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid());
- break;
- case CAMERA_DEVICE_API_VERSION_2_0:
- case CAMERA_DEVICE_API_VERSION_2_1:
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- client = new Camera2Client(this, cameraClient,
- clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid(),
- deviceVersion);
- 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());
+ status = connectHelperLocked(cameraClient,
+ cameraId,
+ clientPackageName,
+ clientUid,
+ callingPid,
+ client);
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;
}
- mClient[cameraId] = client;
- LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
- getpid());
}
// 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)
@@ -561,8 +724,9 @@
if (status != OK) {
return status;
}
-
- remoteCallback->linkToDeath(this);
+ if (remoteCallback != NULL) {
+ remoteCallback->linkToDeath(this);
+ }
return OK;
}
@@ -800,9 +964,13 @@
if (client != 0) {
// Found our camera, clear and leave.
LOG1("removeClient: clear camera %d", outIndex);
- mClient[outIndex].clear();
- client->getRemote()->unlinkToDeath(this);
+ sp<IBinder> remote = client->getRemote();
+ if (remote != NULL) {
+ remote->unlinkToDeath(this);
+ }
+
+ mClient[outIndex].clear();
} else {
sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 76ea7be..ee39d52 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -18,6 +18,7 @@
#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
#include <binder/AppOpsManager.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
@@ -32,6 +33,7 @@
#include <camera/camera2/ICameraDeviceCallbacks.h>
#include <camera/VendorTagDescriptor.h>
#include <camera/CaptureResult.h>
+#include <camera/CameraParameters.h>
#include <camera/ICameraServiceListener.h>
@@ -395,6 +397,43 @@
bool isValidCameraId(int cameraId);
bool setUpVendorTags();
+
+ /**
+ * A mapping of camera ids to CameraParameters returned by that camera device.
+ *
+ * This cache is used to generate CameraCharacteristic metadata when using
+ * the HAL1 shim.
+ */
+ KeyedVector<int, CameraParameters> mShimParams;
+
+ /**
+ * Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t initializeShimMetadata(int cameraId);
+
+ /**
+ * Generate the CameraCharacteristics metadata required by the Camera2 API
+ * from the available HAL1 CameraParameters and CameraInfo.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo);
+
+ /**
+ * Connect a new camera client. This should only be used while holding the
+ * mutex for mServiceLock.
+ *
+ * Returns OK on success, or a negative error code.
+ */
+ status_t connectHelperLocked(const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ int callingPid,
+ /*out*/
+ sp<Client>& client);
};
} // namespace android
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 65592d3..dece764 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2028,24 +2028,7 @@
}
int Parameters::formatStringToEnum(const char *format) {
- return
- !format ?
- HAL_PIXEL_FORMAT_YCrCb_420_SP :
- !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ?
- HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
- !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) ?
- HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
- !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) ?
- HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2
- !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420P) ?
- HAL_PIXEL_FORMAT_YV12 : // YV12
- !strcmp(format, CameraParameters::PIXEL_FORMAT_RGB565) ?
- HAL_PIXEL_FORMAT_RGB_565 : // RGB565
- !strcmp(format, CameraParameters::PIXEL_FORMAT_RGBA8888) ?
- HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888
- !strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) ?
- HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data
- -1;
+ return CameraParameters::previewFormatToEnum(format);
}
const char* Parameters::formatEnumToString(int format) {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 5a48a62..4fce1b3 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -246,6 +246,18 @@
return res;
}
+status_t CameraDeviceClient::beginConfigure() {
+ // TODO: Implement this.
+ ALOGE("%s: Not implemented yet.", __FUNCTION__);
+ return OK;
+}
+
+status_t CameraDeviceClient::endConfigure() {
+ // TODO: Implement this.
+ ALOGE("%s: Not implemented yet.", __FUNCTION__);
+ return OK;
+}
+
status_t CameraDeviceClient::deleteStream(int streamId) {
ATRACE_CALL();
ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 0b37784..9981dfe 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -76,6 +76,10 @@
/*out*/
int64_t* lastFrameNumber = NULL);
+ virtual status_t beginConfigure();
+
+ virtual status_t endConfigure();
+
// Returns -EBUSY if device is not idle
virtual status_t deleteStream(int streamId);
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
index 346e15f..374dc5e 100644
--- a/services/camera/libcameraservice/utils/CameraTraces.cpp
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -74,10 +74,10 @@
return BAD_VALUE;
}
- fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+ dprintf(fd, "Camera traces (%zu):\n", pcsList.size());
if (pcsList.empty()) {
- fdprintf(fd, " No camera traces collected.\n");
+ dprintf(fd, " No camera traces collected.\n");
}
// Print newest items first
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 0c7fbbd..41dab1f 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -60,7 +60,7 @@
static const String16 sDump("android.permission.DUMP");
if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA ||
PermissionCache::checkCallingPermission(sDump))) {
- fdprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
+ dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
return NO_ERROR;
@@ -74,7 +74,7 @@
for (size_t i = 0; i < namedReaders.size(); i++) {
const NamedReader& namedReader = namedReaders[i];
if (fd >= 0) {
- fdprintf(fd, "\n%s:\n", namedReader.name());
+ dprintf(fd, "\n%s:\n", namedReader.name());
} else {
ALOGI("%s:", namedReader.name());
}
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
new file mode 100644
index 0000000..b7ccaab
--- /dev/null
+++ b/services/soundtrigger/Android.mk
@@ -0,0 +1,41 @@
+# Copyright 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
+ LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE
+endif
+
+LOCAL_SRC_FILES:= \
+ SoundTriggerHwService.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+ libui \
+ liblog \
+ libutils \
+ libbinder \
+ libcutils \
+ libhardware \
+ libsoundtrigger
+
+#LOCAL_C_INCLUDES += \
+
+
+LOCAL_MODULE:= libsoundtriggerservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
new file mode 100644
index 0000000..f09e79e
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2014 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 "SoundTriggerHwService"
+//#define LOG_NDEBUG 0
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include "SoundTriggerHwService.h"
+#include <system/sound_trigger.h>
+#include <hardware/sound_trigger.h>
+
+namespace android {
+
+#ifdef SOUND_TRIGGER_USE_STUB_MODULE
+#define HW_MODULE_PREFIX "stub"
+#else
+#define HW_MODULE_PREFIX "primary"
+#endif
+
+SoundTriggerHwService::SoundTriggerHwService()
+ : BnSoundTriggerHwService(),
+ mNextUniqueId(1)
+{
+}
+
+void SoundTriggerHwService::onFirstRef()
+{
+ const hw_module_t *mod;
+ int rc;
+ sound_trigger_hw_device *dev;
+
+ rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod);
+ if (rc != 0) {
+ ALOGE("couldn't load sound trigger module %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+ return;
+ }
+ rc = sound_trigger_hw_device_open(mod, &dev);
+ if (rc != 0) {
+ ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
+ SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+ return;
+ }
+ if (dev->common.version != SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("wrong sound trigger hw device version %04x", dev->common.version);
+ return;
+ }
+
+ sound_trigger_module_descriptor descriptor;
+ rc = dev->get_properties(dev, &descriptor.properties);
+ if (rc != 0) {
+ ALOGE("could not read implementation properties");
+ return;
+ }
+ descriptor.handle =
+ (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId);
+ ALOGI("loaded default module %s, handle %d", descriptor.properties.description,
+ descriptor.handle);
+
+ sp<ISoundTriggerClient> client;
+ sp<Module> module = new Module(this, dev, descriptor, client);
+ mModules.add(descriptor.handle, module);
+ mCallbackThread = new CallbackThread(this);
+}
+
+SoundTriggerHwService::~SoundTriggerHwService()
+{
+ if (mCallbackThread != 0) {
+ mCallbackThread->exit();
+ }
+ for (size_t i = 0; i < mModules.size(); i++) {
+ sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice());
+ }
+}
+
+status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules)
+{
+ ALOGV("listModules");
+ AutoMutex lock(mServiceLock);
+ if (numModules == NULL || (*numModules != 0 && modules == NULL)) {
+ return BAD_VALUE;
+ }
+ size_t maxModules = *numModules;
+ *numModules = mModules.size();
+ for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
+ modules[i] = mModules.valueAt(i)->descriptor();
+ }
+ return NO_ERROR;
+}
+
+status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle,
+ const sp<ISoundTriggerClient>& client,
+ sp<ISoundTrigger>& moduleInterface)
+{
+ ALOGV("attach module %d", handle);
+ AutoMutex lock(mServiceLock);
+ moduleInterface.clear();
+ if (client == 0) {
+ return BAD_VALUE;
+ }
+ ssize_t index = mModules.indexOfKey(handle);
+ if (index < 0) {
+ return BAD_VALUE;
+ }
+ sp<Module> module = mModules.valueAt(index);
+
+ module->setClient(client);
+ client->asBinder()->linkToDeath(module);
+ moduleInterface = module;
+
+ return NO_ERROR;
+}
+
+void SoundTriggerHwService::detachModule(sp<Module> module) {
+ AutoMutex lock(mServiceLock);
+ ALOGV("detachModule");
+ module->clearClient();
+}
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 60000;
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t SoundTriggerHwService::dump(int fd, const Vector<String16>& args __unused) {
+ String8 result;
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ result.appendFormat("Permission Denial: can't dump SoundTriggerHwService");
+ write(fd, result.string(), result.size());
+ } else {
+ bool locked = tryLock(mServiceLock);
+ // failed to lock - SoundTriggerHwService is probably deadlocked
+ if (!locked) {
+ result.append("SoundTriggerHwService may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ if (locked) mServiceLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+status_t SoundTriggerHwService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ return BnSoundTriggerHwService::onTransact(code, data, reply, flags);
+}
+
+
+// static
+void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event,
+ void *cookie)
+{
+ Module *module = (Module *)cookie;
+ if (module == NULL) {
+ return;
+ }
+ module->sendRecognitionEvent(event);
+}
+
+
+void SoundTriggerHwService::sendRecognitionEvent(const sp<RecognitionEvent>& event)
+{
+ mCallbackThread->sendRecognitionEvent(event);
+}
+
+void SoundTriggerHwService::onRecognitionEvent(const sp<RecognitionEvent>& event)
+{
+ ALOGV("onRecognitionEvent");
+ sp<Module> module;
+ {
+ AutoMutex lock(mServiceLock);
+ module = event->mModule.promote();
+ if (module == 0) {
+ return;
+ }
+ }
+ module->onRecognitionEvent(event->mEventMemory);
+}
+
+// static
+void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event __unused,
+ void *cookie)
+{
+ Module *module = (Module *)cookie;
+
+}
+
+#undef LOG_TAG
+#define LOG_TAG "SoundTriggerHwService::CallbackThread"
+
+SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service)
+ : mService(service)
+{
+}
+
+SoundTriggerHwService::CallbackThread::~CallbackThread()
+{
+ mEventQueue.clear();
+}
+
+void SoundTriggerHwService::CallbackThread::onFirstRef()
+{
+ run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool SoundTriggerHwService::CallbackThread::threadLoop()
+{
+ while (!exitPending()) {
+ sp<RecognitionEvent> event;
+ sp<SoundTriggerHwService> service;
+ {
+ Mutex::Autolock _l(mCallbackLock);
+ while (mEventQueue.isEmpty() && !exitPending()) {
+ ALOGV("CallbackThread::threadLoop() sleep");
+ mCallbackCond.wait(mCallbackLock);
+ ALOGV("CallbackThread::threadLoop() wake up");
+ }
+ if (exitPending()) {
+ break;
+ }
+ event = mEventQueue[0];
+ mEventQueue.removeAt(0);
+ service = mService.promote();
+ }
+ if (service != 0) {
+ service->onRecognitionEvent(event);
+ }
+ }
+ return false;
+}
+
+void SoundTriggerHwService::CallbackThread::exit()
+{
+ Mutex::Autolock _l(mCallbackLock);
+ requestExit();
+ mCallbackCond.broadcast();
+}
+
+void SoundTriggerHwService::CallbackThread::sendRecognitionEvent(
+ const sp<SoundTriggerHwService::RecognitionEvent>& event)
+{
+ AutoMutex lock(mCallbackLock);
+ mEventQueue.add(event);
+ mCallbackCond.signal();
+}
+
+SoundTriggerHwService::RecognitionEvent::RecognitionEvent(
+ sp<IMemory> eventMemory,
+ wp<Module> module)
+ : mEventMemory(eventMemory), mModule(module)
+{
+}
+
+SoundTriggerHwService::RecognitionEvent::~RecognitionEvent()
+{
+}
+
+#undef LOG_TAG
+#define LOG_TAG "SoundTriggerHwService::Module"
+
+SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
+ sound_trigger_hw_device* hwDevice,
+ sound_trigger_module_descriptor descriptor,
+ const sp<ISoundTriggerClient>& client)
+ : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor),
+ mClient(client)
+{
+}
+
+SoundTriggerHwService::Module::~Module() {
+}
+
+void SoundTriggerHwService::Module::detach() {
+ ALOGV("detach()");
+ {
+ AutoMutex lock(mLock);
+ for (size_t i = 0; i < mModels.size(); i++) {
+ sp<Model> model = mModels.valueAt(i);
+ ALOGV("detach() unloading model %d", model->mHandle);
+ if (model->mState == Model::STATE_ACTIVE) {
+ mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+ model->deallocateMemory();
+ }
+ mHwDevice->unload_sound_model(mHwDevice, model->mHandle);
+ }
+ mModels.clear();
+ }
+ if (mClient != 0) {
+ mClient->asBinder()->unlinkToDeath(this);
+ }
+ sp<SoundTriggerHwService> service = mService.promote();
+ if (service == 0) {
+ return;
+ }
+ service->detachModule(this);
+}
+
+status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle)
+{
+ ALOGV("loadSoundModel() handle");
+
+ if (modelMemory == 0 || modelMemory->pointer() == NULL) {
+ ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
+ return BAD_VALUE;
+ }
+ struct sound_trigger_sound_model *sound_model =
+ (struct sound_trigger_sound_model *)modelMemory->pointer();
+
+ AutoMutex lock(mLock);
+ status_t status = mHwDevice->load_sound_model(mHwDevice,
+ sound_model,
+ SoundTriggerHwService::soundModelCallback,
+ this,
+ handle);
+ if (status == NO_ERROR) {
+ mModels.replaceValueFor(*handle, new Model(*handle));
+ }
+
+ return status;
+}
+
+status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle)
+{
+ ALOGV("unloadSoundModel() model handle %d", handle);
+
+ AutoMutex lock(mLock);
+ ssize_t index = mModels.indexOfKey(handle);
+ if (index < 0) {
+ return BAD_VALUE;
+ }
+ mModels.removeItem(handle);
+
+ return mHwDevice->unload_sound_model(mHwDevice, handle);
+}
+
+status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory)
+{
+ ALOGV("startRecognition() model handle %d", handle);
+
+ if (dataMemory != 0 && dataMemory->pointer() == NULL) {
+ ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()");
+ return BAD_VALUE;
+
+ }
+ AutoMutex lock(mLock);
+ sp<Model> model = getModel(handle);
+ if (model == 0) {
+ return BAD_VALUE;
+ }
+
+ if (model->mState == Model::STATE_ACTIVE) {
+ return INVALID_OPERATION;
+ }
+ model->mState = Model::STATE_ACTIVE;
+
+ char *data = NULL;
+ unsigned int data_size = 0;
+ if (dataMemory != 0 && dataMemory->size() != 0) {
+ data_size = (unsigned int)dataMemory->size();
+ data = (char *)dataMemory->pointer();
+ ALOGV("startRecognition() data size %d data %d - %d",
+ data_size, data[0], data[data_size - 1]);
+ }
+
+ //TODO: get capture handle and device from audio policy service
+ audio_io_handle_t capture_handle = 0;
+ return mHwDevice->start_recognition(mHwDevice, handle, capture_handle, AUDIO_DEVICE_NONE,
+ SoundTriggerHwService::recognitionCallback,
+ this,
+ data_size,
+ data);
+}
+
+status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle)
+{
+ ALOGV("stopRecognition() model handle %d", handle);
+
+ AutoMutex lock(mLock);
+ sp<Model> model = getModel(handle);
+ if (model == 0) {
+ return BAD_VALUE;
+ }
+
+ if (model->mState != Model::STATE_ACTIVE) {
+ return INVALID_OPERATION;
+ }
+ mHwDevice->stop_recognition(mHwDevice, handle);
+ model->deallocateMemory();
+ model->mState = Model::STATE_IDLE;
+ return NO_ERROR;
+}
+
+void SoundTriggerHwService::Module::sendRecognitionEvent(
+ struct sound_trigger_recognition_event *event)
+{
+ sp<SoundTriggerHwService> service;
+ sp<IMemory> eventMemory;
+ ALOGV("sendRecognitionEvent for model %d", event->model);
+ {
+ AutoMutex lock(mLock);
+ sp<Model> model = getModel(event->model);
+ if (model == 0) {
+ return;
+ }
+ if (model->mState != Model::STATE_ACTIVE) {
+ ALOGV("sendRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState);
+ return;
+ }
+ if (mClient == 0) {
+ return;
+ }
+ service = mService.promote();
+ if (service == 0) {
+ return;
+ }
+
+ //sanitize event
+ switch (event->type) {
+ case SOUND_MODEL_TYPE_KEYPHRASE:
+ ALOGW_IF(event->data_offset !=
+ sizeof(struct sound_trigger_phrase_recognition_event),
+ "sendRecognitionEvent(): invalid data offset %u for keyphrase event type",
+ event->data_offset);
+ event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event);
+ break;
+ case SOUND_MODEL_TYPE_UNKNOWN:
+ ALOGW_IF(event->data_offset !=
+ sizeof(struct sound_trigger_recognition_event),
+ "sendRecognitionEvent(): invalid data offset %u for unknown event type",
+ event->data_offset);
+ event->data_offset = sizeof(struct sound_trigger_recognition_event);
+ break;
+ default:
+ return;
+ }
+
+ size_t size = event->data_offset + event->data_size;
+ eventMemory = model->allocateMemory(size);
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+ memcpy(eventMemory->pointer(), event, size);
+ }
+ service->sendRecognitionEvent(new RecognitionEvent(eventMemory, this));
+}
+
+void SoundTriggerHwService::Module::onRecognitionEvent(sp<IMemory> eventMemory)
+{
+ ALOGV("Module::onRecognitionEvent");
+
+ AutoMutex lock(mLock);
+
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+ struct sound_trigger_recognition_event *event =
+ (struct sound_trigger_recognition_event *)eventMemory->pointer();
+
+ sp<Model> model = getModel(event->model);
+ if (model == 0) {
+ ALOGI("%s model == 0", __func__);
+ return;
+ }
+ if (model->mState != Model::STATE_ACTIVE) {
+ ALOGV("onRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState);
+ return;
+ }
+ if (mClient == 0) {
+ ALOGI("%s mClient == 0", __func__);
+ return;
+ }
+ mClient->onRecognitionEvent(eventMemory);
+ model->mState = Model::STATE_IDLE;
+ model->deallocateMemory();
+}
+
+sp<SoundTriggerHwService::Model> SoundTriggerHwService::Module::getModel(
+ sound_model_handle_t handle)
+{
+ sp<Model> model;
+ ssize_t index = mModels.indexOfKey(handle);
+ if (index >= 0) {
+ model = mModels.valueAt(index);
+ }
+ return model;
+}
+
+void SoundTriggerHwService::Module::binderDied(
+ const wp<IBinder> &who __unused) {
+ ALOGW("client binder died for module %d", mDescriptor.handle);
+ detach();
+}
+
+
+SoundTriggerHwService::Model::Model(sound_model_handle_t handle) :
+ mHandle(handle), mState(STATE_IDLE), mInputHandle(AUDIO_IO_HANDLE_NONE),
+ mCaptureSession(AUDIO_SESSION_ALLOCATE),
+ mMemoryDealer(new MemoryDealer(sizeof(struct sound_trigger_recognition_event),
+ "SoundTriggerHwService::Event"))
+{
+
+}
+
+
+sp<IMemory> SoundTriggerHwService::Model::allocateMemory(size_t size)
+{
+ sp<IMemory> memory;
+ if (mMemoryDealer->getMemoryHeap()->getSize() < size) {
+ mMemoryDealer = new MemoryDealer(size, "SoundTriggerHwService::Event");
+ }
+ memory = mMemoryDealer->allocate(size);
+ return memory;
+}
+
+void SoundTriggerHwService::Model::deallocateMemory()
+{
+ mMemoryDealer->deallocate(0);
+}
+
+status_t SoundTriggerHwService::Module::dump(int fd __unused,
+ const Vector<String16>& args __unused) {
+ String8 result;
+ return NO_ERROR;
+}
+
+}; // namespace android
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
new file mode 100644
index 0000000..377f2a1
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
+
+#include <utils/Vector.h>
+//#include <binder/AppOpsManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/BinderService.h>
+#include <binder/IAppOpsCallback.h>
+#include <soundtrigger/ISoundTriggerHwService.h>
+#include <soundtrigger/ISoundTrigger.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+#include <system/sound_trigger.h>
+#include <hardware/sound_trigger.h>
+
+namespace android {
+
+class MemoryHeapBase;
+
+class SoundTriggerHwService :
+ public BinderService<SoundTriggerHwService>,
+ public BnSoundTriggerHwService
+{
+ friend class BinderService<SoundTriggerHwService>;
+public:
+ class Module;
+
+ static char const* getServiceName() { return "media.sound_trigger_hw"; }
+
+ SoundTriggerHwService();
+ virtual ~SoundTriggerHwService();
+
+ // ISoundTriggerHwService
+ virtual status_t listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules);
+
+ virtual status_t attach(const sound_trigger_module_handle_t handle,
+ const sp<ISoundTriggerClient>& client,
+ sp<ISoundTrigger>& module);
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ class Model : public RefBase {
+ public:
+
+ enum {
+ STATE_IDLE,
+ STATE_ACTIVE
+ };
+
+ Model(sound_model_handle_t handle);
+ ~Model() {}
+
+ sp<IMemory> allocateMemory(size_t size);
+ void deallocateMemory();
+
+ sound_model_handle_t mHandle;
+ int mState;
+ audio_io_handle_t mInputHandle;
+ audio_session_t mCaptureSession;
+ sp<MemoryDealer> mMemoryDealer;
+ };
+
+ class Module : public virtual RefBase,
+ public BnSoundTrigger,
+ public IBinder::DeathRecipient {
+ public:
+
+ Module(const sp<SoundTriggerHwService>& service,
+ sound_trigger_hw_device* hwDevice,
+ sound_trigger_module_descriptor descriptor,
+ const sp<ISoundTriggerClient>& client);
+
+ virtual ~Module();
+
+ virtual void detach();
+
+ virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle);
+
+ virtual status_t unloadSoundModel(sound_model_handle_t handle);
+
+ virtual status_t startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory);
+ virtual status_t stopRecognition(sound_model_handle_t handle);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+
+ sound_trigger_hw_device *hwDevice() const { return mHwDevice; }
+ struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
+ void setClient(sp<ISoundTriggerClient> client) { mClient = client; }
+ void clearClient() { mClient.clear(); }
+ sp<ISoundTriggerClient> client() { return mClient; }
+
+ void sendRecognitionEvent(struct sound_trigger_recognition_event *event);
+ void onRecognitionEvent(sp<IMemory> eventMemory);
+
+ sp<Model> getModel(sound_model_handle_t handle);
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder> &who);
+
+ private:
+ Mutex mLock;
+ wp<SoundTriggerHwService> mService;
+ struct sound_trigger_hw_device* mHwDevice;
+ struct sound_trigger_module_descriptor mDescriptor;
+ sp<ISoundTriggerClient> mClient;
+ DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels;
+ }; // class Module
+
+ class RecognitionEvent : public RefBase {
+ public:
+
+ RecognitionEvent(sp<IMemory> eventMemory, wp<Module> module);
+
+ virtual ~RecognitionEvent();
+
+ sp<IMemory> mEventMemory;
+ wp<Module> mModule;
+ };
+
+ class CallbackThread : public Thread {
+ public:
+
+ CallbackThread(const wp<SoundTriggerHwService>& service);
+
+ virtual ~CallbackThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ void exit();
+ void sendRecognitionEvent(const sp<RecognitionEvent>& event);
+
+ private:
+ wp<SoundTriggerHwService> mService;
+ Condition mCallbackCond;
+ Mutex mCallbackLock;
+ Vector< sp<RecognitionEvent> > mEventQueue;
+ };
+
+ void detachModule(sp<Module> module);
+
+ static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie);
+ void sendRecognitionEvent(const sp<RecognitionEvent>& event);
+ void onRecognitionEvent(const sp<RecognitionEvent>& event);
+
+ static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie);
+
+private:
+
+ virtual void onFirstRef();
+
+ Mutex mServiceLock;
+ volatile int32_t mNextUniqueId;
+ DefaultKeyedVector< sound_trigger_module_handle_t, sp<Module> > mModules;
+ sp<CallbackThread> mCallbackThread;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H
diff --git a/soundtrigger/Android.mk b/soundtrigger/Android.mk
new file mode 100644
index 0000000..d91c4c2
--- /dev/null
+++ b/soundtrigger/Android.mk
@@ -0,0 +1,38 @@
+# Copyright 2014 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SoundTrigger.cpp \
+ ISoundTrigger.cpp \
+ ISoundTriggerClient.cpp \
+ ISoundTriggerHwService.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ liblog \
+ libbinder \
+ libhardware
+
+#LOCAL_C_INCLUDES += \
+ system/media/camera/include \
+ system/media/private/camera/include
+
+LOCAL_MODULE:= libsoundtrigger
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp
new file mode 100644
index 0000000..42280d1
--- /dev/null
+++ b/soundtrigger/ISoundTrigger.cpp
@@ -0,0 +1,177 @@
+/*
+**
+** Copyright 2014, 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 "ISoundTrigger"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <binder/IMemory.h>
+#include <soundtrigger/ISoundTrigger.h>
+#include <soundtrigger/ISoundTriggerHwService.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+#include <system/sound_trigger.h>
+
+namespace android {
+
+enum {
+ DETACH = IBinder::FIRST_CALL_TRANSACTION,
+ LOAD_SOUND_MODEL,
+ UNLOAD_SOUND_MODEL,
+ START_RECOGNITION,
+ STOP_RECOGNITION,
+};
+
+class BpSoundTrigger: public BpInterface<ISoundTrigger>
+{
+public:
+ BpSoundTrigger(const sp<IBinder>& impl)
+ : BpInterface<ISoundTrigger>(impl)
+ {
+ }
+
+ void detach()
+ {
+ ALOGV("detach");
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
+ remote()->transact(DETACH, data, &reply);
+ }
+
+ status_t loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle)
+ {
+ if (modelMemory == 0 || handle == NULL) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
+ data.writeStrongBinder(modelMemory->asBinder());
+ status_t status = remote()->transact(LOAD_SOUND_MODEL, data, &reply);
+ if (status != NO_ERROR ||
+ (status = (status_t)reply.readInt32()) != NO_ERROR) {
+ return status;
+ }
+ reply.read(handle, sizeof(sound_model_handle_t));
+ return status;
+ }
+
+ virtual status_t unloadSoundModel(sound_model_handle_t handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
+ data.write(&handle, sizeof(sound_model_handle_t));
+ status_t status = remote()->transact(UNLOAD_SOUND_MODEL, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
+ data.write(&handle, sizeof(sound_model_handle_t));
+ if (dataMemory == 0) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(dataMemory->size());
+ }
+ data.writeStrongBinder(dataMemory->asBinder());
+ status_t status = remote()->transact(START_RECOGNITION, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t stopRecognition(sound_model_handle_t handle)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor());
+ data.write(&handle, sizeof(sound_model_handle_t));
+ status_t status = remote()->transact(STOP_RECOGNITION, data, &reply);
+ if (status != NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(SoundTrigger, "android.hardware.ISoundTrigger");
+
+// ----------------------------------------------------------------------
+
+status_t BnSoundTrigger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case DETACH: {
+ ALOGV("DETACH");
+ CHECK_INTERFACE(ISoundTrigger, data, reply);
+ detach();
+ return NO_ERROR;
+ } break;
+ case LOAD_SOUND_MODEL: {
+ CHECK_INTERFACE(ISoundTrigger, data, reply);
+ sp<IMemory> modelMemory = interface_cast<IMemory>(
+ data.readStrongBinder());
+ sound_model_handle_t handle;
+ status_t status = loadSoundModel(modelMemory, &handle);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&handle, sizeof(sound_model_handle_t));
+ }
+ return NO_ERROR;
+ }
+ case UNLOAD_SOUND_MODEL: {
+ CHECK_INTERFACE(ISoundTrigger, data, reply);
+ sound_model_handle_t handle;
+ data.read(&handle, sizeof(sound_model_handle_t));
+ status_t status = unloadSoundModel(handle);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case START_RECOGNITION: {
+ CHECK_INTERFACE(ISoundTrigger, data, reply);
+ sound_model_handle_t handle;
+ data.read(&handle, sizeof(sound_model_handle_t));
+ sp<IMemory> dataMemory;
+ if (data.readInt32() != 0) {
+ dataMemory = interface_cast<IMemory>(data.readStrongBinder());
+ }
+ status_t status = startRecognition(handle, dataMemory);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case STOP_RECOGNITION: {
+ CHECK_INTERFACE(ISoundTrigger, data, reply);
+ sound_model_handle_t handle;
+ data.read(&handle, sizeof(sound_model_handle_t));
+ status_t status = stopRecognition(handle);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/soundtrigger/ISoundTriggerClient.cpp b/soundtrigger/ISoundTriggerClient.cpp
new file mode 100644
index 0000000..1d0c0ec
--- /dev/null
+++ b/soundtrigger/ISoundTriggerClient.cpp
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+
+namespace android {
+
+enum {
+ ON_RECOGNITION_EVENT = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpSoundTriggerClient: public BpInterface<ISoundTriggerClient>
+{
+
+public:
+ BpSoundTriggerClient(const sp<IBinder>& impl)
+ : BpInterface<ISoundTriggerClient>(impl)
+ {
+ }
+
+ virtual void onRecognitionEvent(const sp<IMemory>& eventMemory)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor());
+ data.writeStrongBinder(eventMemory->asBinder());
+ remote()->transact(ON_RECOGNITION_EVENT,
+ data,
+ &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(SoundTriggerClient,
+ "android.hardware.ISoundTriggerClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnSoundTriggerClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case ON_RECOGNITION_EVENT: {
+ CHECK_INTERFACE(ISoundTriggerClient, data, reply);
+ sp<IMemory> eventMemory = interface_cast<IMemory>(
+ data.readStrongBinder());
+ onRecognitionEvent(eventMemory);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp
new file mode 100644
index 0000000..c9a0c24
--- /dev/null
+++ b/soundtrigger/ISoundTriggerHwService.cpp
@@ -0,0 +1,150 @@
+/*
+**
+** Copyright 2014, 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 "BpSoundTriggerHwService"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <soundtrigger/ISoundTriggerHwService.h>
+#include <soundtrigger/ISoundTrigger.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+
+namespace android {
+
+enum {
+ LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION,
+ ATTACH,
+};
+
+class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService>
+{
+public:
+ BpSoundTriggerHwService(const sp<IBinder>& impl)
+ : BpInterface<ISoundTriggerHwService>(impl)
+ {
+ }
+
+ virtual status_t listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules)
+ {
+ if (numModules == NULL || (*numModules != 0 && modules == NULL)) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor());
+ unsigned int numModulesReq = (modules == NULL) ? 0 : *numModules;
+ data.writeInt32(numModulesReq);
+ status_t status = remote()->transact(LIST_MODULES, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ *numModules = (unsigned int)reply.readInt32();
+ }
+ ALOGV("listModules() status %d got *numModules %d", status, *numModules);
+ if (status == NO_ERROR) {
+ if (numModulesReq > *numModules) {
+ numModulesReq = *numModules;
+ }
+ if (numModulesReq > 0) {
+ reply.read(modules, numModulesReq * sizeof(struct sound_trigger_module_descriptor));
+ }
+ }
+ return status;
+ }
+
+ virtual status_t attach(const sound_trigger_module_handle_t handle,
+ const sp<ISoundTriggerClient>& client,
+ sp<ISoundTrigger>& module)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor());
+ data.write(&handle, sizeof(sound_trigger_module_handle_t));
+ data.writeStrongBinder(client->asBinder());
+ remote()->transact(ATTACH, data, &reply);
+ status_t status = reply.readInt32();
+ if (reply.readInt32() != 0) {
+ module = interface_cast<ISoundTrigger>(reply.readStrongBinder());
+ }
+ return status;
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(SoundTriggerHwService, "android.hardware.ISoundTriggerHwService");
+
+// ----------------------------------------------------------------------
+
+status_t BnSoundTriggerHwService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case LIST_MODULES: {
+ CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
+ unsigned int numModulesReq = data.readInt32();
+ unsigned int numModules = numModulesReq;
+ struct sound_trigger_module_descriptor *modules =
+ (struct sound_trigger_module_descriptor *)calloc(numModulesReq,
+ sizeof(struct sound_trigger_module_descriptor));
+ status_t status = listModules(modules, &numModules);
+ reply->writeInt32(status);
+ reply->writeInt32(numModules);
+ ALOGV("LIST_MODULES status %d got numModules %d", status, numModules);
+
+ if (status == NO_ERROR) {
+ if (numModulesReq > numModules) {
+ numModulesReq = numModules;
+ }
+ reply->write(modules,
+ numModulesReq * sizeof(struct sound_trigger_module_descriptor));
+ }
+ free(modules);
+ return NO_ERROR;
+ }
+
+ case ATTACH: {
+ CHECK_INTERFACE(ISoundTriggerHwService, data, reply);
+ sound_trigger_module_handle_t handle;
+ data.read(&handle, sizeof(sound_trigger_module_handle_t));
+ sp<ISoundTriggerClient> client =
+ interface_cast<ISoundTriggerClient>(data.readStrongBinder());
+ sp<ISoundTrigger> module;
+ status_t status = attach(handle, client, module);
+ reply->writeInt32(status);
+ if (module != 0) {
+ reply->writeInt32(1);
+ reply->writeStrongBinder(module->asBinder());
+ } else {
+ reply->writeInt32(0);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
new file mode 100644
index 0000000..e43acd0
--- /dev/null
+++ b/soundtrigger/SoundTrigger.cpp
@@ -0,0 +1,253 @@
+/*
+**
+** Copyright (C) 2014, 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 "SoundTrigger"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <soundtrigger/SoundTrigger.h>
+#include <soundtrigger/ISoundTrigger.h>
+#include <soundtrigger/ISoundTriggerHwService.h>
+#include <soundtrigger/ISoundTriggerClient.h>
+#include <soundtrigger/SoundTriggerCallback.h>
+
+namespace android {
+
+namespace {
+ sp<ISoundTriggerHwService> gSoundTriggerHwService;
+ const int kSoundTriggerHwServicePollDelay = 500000; // 0.5s
+ const char* kSoundTriggerHwServiceName = "media.sound_trigger_hw";
+ Mutex gLock;
+
+ class DeathNotifier : public IBinder::DeathRecipient
+ {
+ public:
+ DeathNotifier() {
+ }
+
+ virtual void binderDied(const wp<IBinder>& who __unused) {
+ ALOGV("binderDied");
+ Mutex::Autolock _l(gLock);
+ gSoundTriggerHwService.clear();
+ ALOGW("Sound trigger service died!");
+ }
+ };
+
+ sp<DeathNotifier> gDeathNotifier;
+}; // namespace anonymous
+
+const sp<ISoundTriggerHwService>& SoundTrigger::getSoundTriggerHwService()
+{
+ Mutex::Autolock _l(gLock);
+ if (gSoundTriggerHwService.get() == 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16(kSoundTriggerHwServiceName));
+ if (binder != 0) {
+ break;
+ }
+ ALOGW("SoundTriggerHwService not published, waiting...");
+ usleep(kSoundTriggerHwServicePollDelay);
+ } while(true);
+ if (gDeathNotifier == NULL) {
+ gDeathNotifier = new DeathNotifier();
+ }
+ binder->linkToDeath(gDeathNotifier);
+ gSoundTriggerHwService = interface_cast<ISoundTriggerHwService>(binder);
+ }
+ ALOGE_IF(gSoundTriggerHwService == 0, "no SoundTriggerHwService!?");
+ return gSoundTriggerHwService;
+}
+
+// Static methods
+status_t SoundTrigger::listModules(struct sound_trigger_module_descriptor *modules,
+ uint32_t *numModules)
+{
+ ALOGV("listModules()");
+ const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService();
+ if (service == 0) {
+ return NO_INIT;
+ }
+ return service->listModules(modules, numModules);
+}
+
+sp<SoundTrigger> SoundTrigger::attach(const sound_trigger_module_handle_t module,
+ const sp<SoundTriggerCallback>& callback)
+{
+ ALOGV("attach()");
+ sp<SoundTrigger> soundTrigger;
+ const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService();
+ if (service == 0) {
+ return soundTrigger;
+ }
+ soundTrigger = new SoundTrigger(module, callback);
+ status_t status = service->attach(module, soundTrigger, soundTrigger->mISoundTrigger);
+
+ if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) {
+ soundTrigger->mISoundTrigger->asBinder()->linkToDeath(soundTrigger);
+ } else {
+ ALOGW("Error %d connecting to sound trigger service", status);
+ soundTrigger.clear();
+ }
+ return soundTrigger;
+}
+
+
+// SoundTrigger
+SoundTrigger::SoundTrigger(sound_trigger_module_handle_t module,
+ const sp<SoundTriggerCallback>& callback)
+ : mModule(module), mCallback(callback)
+{
+}
+
+SoundTrigger::~SoundTrigger()
+{
+ if (mISoundTrigger != 0) {
+ mISoundTrigger->detach();
+ }
+}
+
+
+void SoundTrigger::detach() {
+ ALOGV("detach()");
+ Mutex::Autolock _l(mLock);
+ mCallback.clear();
+ if (mISoundTrigger != 0) {
+ mISoundTrigger->detach();
+ mISoundTrigger->asBinder()->unlinkToDeath(this);
+ mISoundTrigger = 0;
+ }
+}
+
+status_t SoundTrigger::loadSoundModel(const sp<IMemory>& modelMemory,
+ sound_model_handle_t *handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mISoundTrigger == 0) {
+ return NO_INIT;
+ }
+
+ return mISoundTrigger->loadSoundModel(modelMemory, handle);
+}
+
+status_t SoundTrigger::unloadSoundModel(sound_model_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mISoundTrigger == 0) {
+ return NO_INIT;
+ }
+ return mISoundTrigger->unloadSoundModel(handle);
+}
+
+status_t SoundTrigger::startRecognition(sound_model_handle_t handle,
+ const sp<IMemory>& dataMemory)
+{
+ Mutex::Autolock _l(mLock);
+ if (mISoundTrigger == 0) {
+ return NO_INIT;
+ }
+ return mISoundTrigger->startRecognition(handle, dataMemory);
+}
+
+status_t SoundTrigger::stopRecognition(sound_model_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+ if (mISoundTrigger == 0) {
+ return NO_INIT;
+ }
+ return mISoundTrigger->stopRecognition(handle);
+}
+
+// BpSoundTriggerClient
+void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory)
+{
+ Mutex::Autolock _l(mLock);
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+
+ if (mCallback != 0) {
+ mCallback->onRecognitionEvent(
+ (struct sound_trigger_recognition_event *)eventMemory->pointer());
+ }
+}
+
+
+//IBinder::DeathRecipient
+void SoundTrigger::binderDied(const wp<IBinder>& who __unused) {
+ Mutex::Autolock _l(mLock);
+ ALOGW("SoundTrigger server binder Died ");
+ mISoundTrigger = 0;
+ if (mCallback != 0) {
+ mCallback->onServiceDied();
+ }
+}
+
+status_t SoundTrigger::stringToGuid(const char *str, sound_trigger_uuid_t *guid)
+{
+ if (str == NULL || guid == NULL) {
+ return BAD_VALUE;
+ }
+
+ int tmp[10];
+
+ if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
+ return BAD_VALUE;
+ }
+ guid->timeLow = (uint32_t)tmp[0];
+ guid->timeMid = (uint16_t)tmp[1];
+ guid->timeHiAndVersion = (uint16_t)tmp[2];
+ guid->clockSeq = (uint16_t)tmp[3];
+ guid->node[0] = (uint8_t)tmp[4];
+ guid->node[1] = (uint8_t)tmp[5];
+ guid->node[2] = (uint8_t)tmp[6];
+ guid->node[3] = (uint8_t)tmp[7];
+ guid->node[4] = (uint8_t)tmp[8];
+ guid->node[5] = (uint8_t)tmp[9];
+
+ return NO_ERROR;
+}
+
+status_t SoundTrigger::guidToString(const sound_trigger_uuid_t *guid, char *str, size_t maxLen)
+{
+ if (guid == NULL || str == NULL) {
+ return BAD_VALUE;
+ }
+
+ snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ guid->timeLow,
+ guid->timeMid,
+ guid->timeHiAndVersion,
+ guid->clockSeq,
+ guid->node[0],
+ guid->node[1],
+ guid->node[2],
+ guid->node[3],
+ guid->node[4],
+ guid->node[5]);
+
+ return NO_ERROR;
+}
+
+}; // namespace android