summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp81
-rw-r--r--include/media/AudioResamplerPublic.h38
-rw-r--r--include/media/AudioSystem.h17
-rw-r--r--include/media/AudioTrack.h44
-rw-r--r--include/media/IAudioPolicyService.h1
-rw-r--r--include/media/ICrypto.h8
-rw-r--r--include/media/IMediaCodecList.h4
-rw-r--r--include/media/MediaCodecInfo.h4
-rw-r--r--include/media/stagefright/ACodec.h1
-rw-r--r--include/media/stagefright/MediaClock.h1
-rw-r--r--include/media/stagefright/MediaCodec.h15
-rw-r--r--include/media/stagefright/MediaCodecList.h15
-rw-r--r--include/private/media/AudioTrackShared.h36
-rw-r--r--media/libmedia/AudioRecord.cpp10
-rw-r--r--media/libmedia/AudioSystem.cpp3
-rw-r--r--media/libmedia/AudioTrack.cpp166
-rw-r--r--media/libmedia/AudioTrackShared.cpp10
-rw-r--r--media/libmedia/IAudioPolicyService.cpp5
-rw-r--r--media/libmedia/ICrypto.cpp66
-rw-r--r--media/libmedia/IMediaCodecList.cpp28
-rw-r--r--media/libmedia/MediaCodecInfo.cpp11
-rw-r--r--media/libmediaplayerservice/Crypto.cpp15
-rw-r--r--media/libmediaplayerservice/Crypto.h4
-rw-r--r--media/libmediaplayerservice/Drm.cpp66
-rw-r--r--media/libmediaplayerservice/Drm.h9
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp263
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h11
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp3
-rw-r--r--media/libstagefright/ACodec.cpp49
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/ESDS.cpp6
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp92
-rw-r--r--media/libstagefright/MediaClock.cpp5
-rw-r--r--media/libstagefright/MediaCodec.cpp120
-rw-r--r--media/libstagefright/MediaCodecList.cpp259
-rw-r--r--media/libstagefright/MediaCodecListOverrides.cpp404
-rw-r--r--media/libstagefright/MediaCodecListOverrides.h50
-rw-r--r--media/libstagefright/MediaCodecSource.cpp6
-rw-r--r--media/libstagefright/SampleTable.cpp30
-rw-r--r--media/libstagefright/codecs/on2/dec/SoftVPX.cpp14
-rw-r--r--media/libstagefright/httplive/LiveSession.cpp214
-rw-r--r--media/libstagefright/httplive/LiveSession.h5
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.cpp98
-rw-r--r--media/libstagefright/httplive/PlaylistFetcher.h5
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp31
-rw-r--r--media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp17
-rw-r--r--media/libstagefright/omx/SoftOMXPlugin.cpp2
-rw-r--r--media/libstagefright/tests/Android.mk27
-rw-r--r--media/libstagefright/tests/MediaCodecListOverrides_test.cpp316
-rw-r--r--media/mediaserver/Android.mk3
-rw-r--r--services/audioflinger/Android.mk10
-rw-r--r--services/audioflinger/AudioFlinger.cpp56
-rw-r--r--services/audioflinger/AudioHwDevice.cpp25
-rw-r--r--services/audioflinger/AudioMixer.cpp378
-rw-r--r--services/audioflinger/AudioMixer.h128
-rw-r--r--services/audioflinger/AudioResampler.cpp23
-rw-r--r--services/audioflinger/AudioResampler.h12
-rw-r--r--services/audioflinger/AudioResamplerCubic.cpp23
-rw-r--r--services/audioflinger/AudioResamplerCubic.h6
-rw-r--r--services/audioflinger/AudioResamplerDyn.cpp7
-rw-r--r--services/audioflinger/AudioResamplerDyn.h6
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp14
-rw-r--r--services/audioflinger/AudioResamplerSinc.h4
-rw-r--r--services/audioflinger/BufferProviders.cpp540
-rw-r--r--services/audioflinger/BufferProviders.h193
-rw-r--r--services/audioflinger/PatchPanel.cpp38
-rw-r--r--services/audioflinger/PlaybackTracks.h1
-rw-r--r--services/audioflinger/RecordTracks.h21
-rw-r--r--services/audioflinger/ServiceUtilities.cpp7
-rw-r--r--services/audioflinger/ServiceUtilities.h1
-rw-r--r--services/audioflinger/SpdifStreamOut.cpp15
-rw-r--r--services/audioflinger/SpdifStreamOut.h8
-rw-r--r--services/audioflinger/Threads.cpp447
-rw-r--r--services/audioflinger/Threads.h114
-rw-r--r--services/audioflinger/Tracks.cpp57
-rw-r--r--services/audioflinger/tests/Android.mk7
-rw-r--r--services/audioflinger/tests/resampler_tests.cpp5
-rw-r--r--services/audiopolicy/AudioPolicyInterface.h1
-rwxr-xr-xservices/audiopolicy/common/include/Volume.h22
-rw-r--r--services/audiopolicy/common/managerdefinitions/Android.mk1
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h6
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h97
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h18
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/AudioPort.h31
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h54
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h7
-rw-r--r--services/audiopolicy/common/managerdefinitions/include/IOProfile.h4
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp20
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp297
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp8
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp60
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp92
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp8
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp43
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/HwModule.cpp14
-rw-r--r--services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp25
-rwxr-xr-xservices/audiopolicy/engine/interface/AudioPolicyManagerInterface.h6
-rwxr-xr-xservices/audiopolicy/engine/interface/AudioPolicyManagerObserver.h2
-rwxr-xr-xservices/audiopolicy/enginedefault/src/Engine.cpp13
-rwxr-xr-xservices/audiopolicy/enginedefault/src/Engine.h6
-rw-r--r--services/audiopolicy/enginedefault/src/Gains.cpp30
-rw-r--r--services/audiopolicy/enginedefault/src/Gains.h9
-rw-r--r--services/audiopolicy/managerdefault/AudioPolicyManager.cpp711
-rw-r--r--services/audiopolicy/managerdefault/AudioPolicyManager.h114
-rw-r--r--services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp6
-rw-r--r--services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp4
-rw-r--r--services/audiopolicy/service/AudioPolicyService.h1
-rw-r--r--services/camera/libcameraservice/CameraService.cpp153
-rw-r--r--services/camera/libcameraservice/CameraService.h60
-rw-r--r--services/camera/libcameraservice/api1/Camera2Client.cpp3
-rw-r--r--services/camera/libcameraservice/utils/ClientManager.h49
111 files changed, 5082 insertions, 1728 deletions
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 9b786c5..851ad2c 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -56,7 +56,7 @@ namespace android {
return true;
}
- status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
+ status_t MockDrmFactory::createDrmPlugin(const uint8_t /* uuid */[16], DrmPlugin **plugin)
{
*plugin = new MockDrmPlugin();
return OK;
@@ -68,8 +68,9 @@ namespace android {
return (!memcmp(uuid, mock_uuid, sizeof(mock_uuid)));
}
- status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
- size_t size, CryptoPlugin **plugin)
+ status_t MockCryptoFactory::createPlugin(const uint8_t /* uuid */[16],
+ const void * /* data */,
+ size_t /* size */, CryptoPlugin **plugin)
{
*plugin = new MockCryptoPlugin();
return OK;
@@ -150,7 +151,7 @@ namespace android {
// Properties used in mock test, set by cts test app returned from mock plugin
// byte[] mock-request -> request
// string mock-default-url -> defaultUrl
- // string mock-key-request-type -> keyRequestType
+ // string mock-keyRequestType -> keyRequestType
index = mByteArrayProperties.indexOfKey(String8("mock-request"));
if (index < 0) {
@@ -266,8 +267,8 @@ namespace android {
return OK;
}
- status_t MockDrmPlugin::getProvisionRequest(String8 const &certType,
- String8 const &certAuthority,
+ status_t MockDrmPlugin::getProvisionRequest(String8 const & /* certType */,
+ String8 const & /* certAuthority */,
Vector<uint8_t> &request,
String8 &defaultUrl)
{
@@ -297,8 +298,8 @@ namespace android {
}
status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response,
- Vector<uint8_t> &certificate,
- Vector<uint8_t> &wrappedKey)
+ Vector<uint8_t> & /* certificate */,
+ Vector<uint8_t> & /* wrappedKey */)
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
@@ -317,7 +318,8 @@ namespace android {
return OK;
}
- status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop)
+ status_t MockDrmPlugin::getSecureStop(Vector<uint8_t> const & /* ssid */,
+ Vector<uint8_t> & secureStop)
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::getSecureStop()");
@@ -439,6 +441,63 @@ namespace android {
pData ? vectorToString(*pData) : "{}");
sendEvent(eventType, extra, pSessionId, pData);
+ } else if (name == "mock-send-expiration-update") {
+ int64_t expiryTimeMS;
+ sscanf(value.string(), "%jd", &expiryTimeMS);
+
+ Vector<uint8_t> const *pSessionId = NULL;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+ if (index >= 0) {
+ pSessionId = &mByteArrayProperties[index];
+ }
+
+ ALOGD("sending expiration-update from mock drm plugin: %jd %s",
+ expiryTimeMS, pSessionId ? vectorToString(*pSessionId) : "{}");
+
+ sendExpirationUpdate(pSessionId, expiryTimeMS);
+ } else if (name == "mock-send-keys-change") {
+ Vector<uint8_t> const *pSessionId = NULL;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
+ if (index >= 0) {
+ pSessionId = &mByteArrayProperties[index];
+ }
+
+ ALOGD("sending keys-change from mock drm plugin: %s",
+ pSessionId ? vectorToString(*pSessionId) : "{}");
+
+ Vector<DrmPlugin::KeyStatus> keyStatusList;
+ DrmPlugin::KeyStatus keyStatus;
+ uint8_t keyId1[] = {'k', 'e', 'y', '1'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId1, sizeof(keyId1));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_Usable;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId2[] = {'k', 'e', 'y', '2'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId2, sizeof(keyId2));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_Expired;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId3[] = {'k', 'e', 'y', '3'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId3, sizeof(keyId3));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_OutputNotAllowed;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId4[] = {'k', 'e', 'y', '4'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId4, sizeof(keyId4));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_StatusPending;
+ keyStatusList.add(keyStatus);
+
+ uint8_t keyId5[] = {'k', 'e', 'y', '5'};
+ keyStatus.mKeyId.clear();
+ keyStatus.mKeyId.appendArray(keyId5, sizeof(keyId5));
+ keyStatus.mType = DrmPlugin::kKeyStatusType_InternalError;
+ keyStatusList.add(keyStatus);
+
+ sendKeysChange(pSessionId, &keyStatusList, true);
} else {
mStringProperties.add(name, value);
}
@@ -740,7 +799,7 @@ namespace android {
ssize_t
MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
Mode mode, const void *srcPtr, const SubSample *subSamples,
- size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
+ size_t numSubSamples, void *dstPtr, AString * /* errorDetailMsg */)
{
ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
"subSamples=%s, dst=%p)",
@@ -769,7 +828,7 @@ namespace android {
{
String8 result;
for (size_t i = 0; i < numSubSamples; i++) {
- result.appendFormat("[%zu] {clear:%zu, encrypted:%zu} ", i,
+ result.appendFormat("[%zu] {clear:%u, encrypted:%u} ", i,
subSamples[i].mNumBytesOfClearData,
subSamples[i].mNumBytesOfEncryptedData);
}
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
index b705efa..07d946d 100644
--- a/include/media/AudioResamplerPublic.h
+++ b/include/media/AudioResamplerPublic.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_AUDIO_RESAMPLER_PUBLIC_H
#define ANDROID_AUDIO_RESAMPLER_PUBLIC_H
+#include <stdint.h>
+
// AUDIO_RESAMPLER_DOWN_RATIO_MAX is the maximum ratio between the original
// audio sample rate and the target rate when downsampling,
// as permitted in the audio framework, e.g. AudioTrack and AudioFlinger.
@@ -26,6 +28,22 @@
// TODO: replace with an API
#define AUDIO_RESAMPLER_DOWN_RATIO_MAX 256
+// AUDIO_RESAMPLER_UP_RATIO_MAX is the maximum suggested ratio between the original
+// audio sample rate and the target rate when upsampling. It is loosely enforced by
+// the system. One issue with large upsampling ratios is the approximation by
+// an int32_t of the phase increments, making the resulting sample rate inexact.
+#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536
+
+#define AUDIO_TIMESTRETCH_SPEED_MIN 0.5f
+#define AUDIO_TIMESTRETCH_SPEED_MAX 2.0f
+#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f
+
+#define AUDIO_TIMESTRETCH_PITCH_MIN 0.5f
+#define AUDIO_TIMESTRETCH_PITCH_MAX 2.0f
+#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f
+
+// TODO: Consider putting these inlines into a class scope
+
// Returns the source frames needed to resample to destination frames. This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
@@ -39,4 +57,24 @@ static inline size_t sourceFramesNeeded(
size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
}
+// An upper bound for the number of destination frames possible from srcFrames
+// after sample rate conversion. This may be used for buffer sizing.
+static inline size_t destinationFramesPossible(size_t srcFrames, uint32_t srcSampleRate,
+ uint32_t dstSampleRate) {
+ if (srcSampleRate == dstSampleRate) {
+ return srcFrames;
+ }
+ uint64_t dstFrames = (uint64_t)srcFrames * dstSampleRate / srcSampleRate;
+ return dstFrames > 2 ? dstFrames - 2 : 0;
+}
+
+static inline size_t sourceFramesNeededWithTimestretch(
+ uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
+ float speed) {
+ // required is the number of input frames the resampler needs
+ size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
+ // to deliver this, the time stretcher requires:
+ return required * (double)speed + 1 + 1; // accounting for rounding dependencies
+}
+
#endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index f5db1bb..3b6db8c 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -221,14 +221,15 @@ public:
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
const audio_offload_info_t *offloadInfo = NULL);
static status_t getOutputForAttr(const audio_attributes_t *attr,
- audio_io_handle_t *output,
- audio_session_t session,
- audio_stream_type_t *stream,
- uint32_t samplingRate = 0,
- audio_format_t format = AUDIO_FORMAT_DEFAULT,
- audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
- const audio_offload_info_t *offloadInfo = NULL);
+ audio_io_handle_t *output,
+ audio_session_t session,
+ audio_stream_type_t *stream,
+ uint32_t samplingRate = 0,
+ audio_format_t format = AUDIO_FORMAT_DEFAULT,
+ audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t session);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index d9b7057..a06197f 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -359,6 +359,21 @@ public:
/* Return current source sample rate in Hz */
uint32_t getSampleRate() const;
+ /* Set source playback rate for timestretch
+ * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
+ * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
+ *
+ * AUDIO_TIMESTRETCH_SPEED_MIN <= speed <= AUDIO_TIMESTRETCH_SPEED_MAX
+ * AUDIO_TIMESTRETCH_PITCH_MIN <= pitch <= AUDIO_TIMESTRETCH_PITCH_MAX
+ *
+ * Speed increases the playback rate of media, but does not alter pitch.
+ * Pitch increases the "tonal frequency" of media, but does not affect the playback rate.
+ */
+ status_t setPlaybackRate(float speed, float pitch);
+
+ /* Return current playback rate */
+ void getPlaybackRate(float *speed, float *pitch) const;
+
/* Enables looping and sets the start and end points of looping.
* Only supported for static buffer mode.
*
@@ -477,6 +492,26 @@ private:
audio_io_handle_t getOutput() const;
public:
+ /* Selects the audio device to use for output of this AudioTrack. A value of
+ * AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+ *
+ * Parameters:
+ * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ *
+ * Returned value:
+ * - NO_ERROR: successful operation
+ * TODO: what else can happen here?
+ */
+ status_t setOutputDevice(audio_port_handle_t deviceId);
+
+ /* Returns the ID of the audio device used for output of this AudioTrack.
+ * A value of AUDIO_PORT_HANDLE_NONE indicates default (AudioPolicyManager) routing.
+ *
+ * Parameters:
+ * none.
+ */
+ audio_port_handle_t getOutputDevice();
+
/* Returns the unique session ID associated with this track.
*
* Parameters:
@@ -699,6 +734,9 @@ protected:
// increment mPosition by the delta of mServer, and return new value of mPosition
uint32_t updateAndGetPosition_l();
+ // check sample rate and speed is compatible with AudioTrack
+ bool isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -710,6 +748,8 @@ protected:
float mVolume[2];
float mSendLevel;
mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it
+ float mSpeed; // timestretch: 1.0f for normal speed.
+ float mPitch; // timestretch: 1.0f for normal pitch.
size_t mFrameCount; // corresponds to current IAudioTrack, value is
// reported back by AudioFlinger to the client
size_t mReqFrameCount; // frame count to request the first or next time
@@ -817,6 +857,10 @@ protected:
bool mInUnderrun; // whether track is currently in underrun state
uint32_t mPausedPosition;
+ // For Device Selection API
+ // a value of AUDIO_PORT_HANDLE_NONE indicated default (AudioPolicyManager) routing.
+ int mSelectedDeviceId;
+
private:
class DeathNotifier : public IBinder::DeathRecipient {
public:
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index fecc6f1..7506153 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -66,6 +66,7 @@ public:
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
const audio_offload_info_t *offloadInfo = NULL) = 0;
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
index 07742ca..aa04dbe 100644
--- a/include/media/ICrypto.h
+++ b/include/media/ICrypto.h
@@ -25,6 +25,7 @@
namespace android {
struct AString;
+struct IMemory;
struct ICrypto : public IInterface {
DECLARE_META_INTERFACE(Crypto);
@@ -43,12 +44,14 @@ struct ICrypto : public IInterface {
virtual void notifyResolution(uint32_t width, uint32_t height) = 0;
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) = 0;
@@ -61,6 +64,9 @@ struct BnCrypto : public BnInterface<ICrypto> {
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
+private:
+ void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+ void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
};
} // namespace android
diff --git a/include/media/IMediaCodecList.h b/include/media/IMediaCodecList.h
index e93ea8b..12b52d7 100644
--- a/include/media/IMediaCodecList.h
+++ b/include/media/IMediaCodecList.h
@@ -21,6 +21,8 @@
#include <binder/IInterface.h>
#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AMessage.h>
+
namespace android {
struct MediaCodecInfo;
@@ -33,6 +35,8 @@ public:
virtual size_t countCodecs() const = 0;
virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const = 0;
+ virtual const sp<AMessage> getGlobalSettings() const = 0;
+
virtual ssize_t findCodecByType(
const char *type, bool encoder, size_t startIndex = 0) const = 0;
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index cd56adb..895a13a 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -35,6 +35,8 @@ struct AMessage;
struct Parcel;
struct CodecCapabilities;
+typedef KeyedVector<AString, AString> CodecSettings;
+
struct MediaCodecInfo : public RefBase {
struct ProfileLevel {
uint32_t mProfile;
@@ -104,6 +106,7 @@ private:
MediaCodecInfo(AString name, bool encoder, const char *mime);
void addQuirk(const char *name);
status_t addMime(const char *mime);
+ status_t updateMime(const char *mime);
status_t initializeCapabilities(const CodecCapabilities &caps);
void addDetail(const AString &key, const AString &value);
void addFeature(const AString &key, int32_t value);
@@ -114,6 +117,7 @@ private:
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
friend class MediaCodecList;
+ friend class MediaCodecListOverridesTest;
};
} // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c1483f3..a8d0fcb 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -300,6 +300,7 @@ private:
OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
status_t setPriority(int32_t priority);
+ status_t setOperatingRate(float rateFloat, bool isVideo);
status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
diff --git a/include/media/stagefright/MediaClock.h b/include/media/stagefright/MediaClock.h
index e9c09a1..dd1a809 100644
--- a/include/media/stagefright/MediaClock.h
+++ b/include/media/stagefright/MediaClock.h
@@ -42,6 +42,7 @@ struct MediaClock : public RefBase {
void updateMaxTimeMedia(int64_t maxTimeMediaUs);
void setPlaybackRate(float rate);
+ float getPlaybackRate() const;
// query media time corresponding to real time |realUs|, and save the
// result in |outMediaUs|.
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 8241e19..3e3c276 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -30,8 +30,10 @@ struct AMessage;
struct AReplyToken;
struct AString;
struct CodecBase;
-struct ICrypto;
struct IBatteryStats;
+struct ICrypto;
+struct IMemory;
+struct MemoryDealer;
struct SoftwareRenderer;
struct Surface;
@@ -51,6 +53,13 @@ struct MediaCodec : public AHandler {
CB_OUTPUT_AVAILABLE = 2,
CB_ERROR = 3,
CB_OUTPUT_FORMAT_CHANGED = 4,
+ CB_CODEC_RELEASED = 5,
+ };
+
+ // used by CB_CODEC_RELEASED to tell the upper layer the cause of the release.
+ enum ReleaseReason {
+ REASON_UNKNOWN = 0,
+ REASON_RECLAIMED, // resources reclaimed by resource manager
};
struct BatteryNotifier;
@@ -126,6 +135,8 @@ struct MediaCodec : public AHandler {
status_t getOutputFormat(sp<AMessage> *format) const;
status_t getInputFormat(sp<AMessage> *format) const;
+ status_t getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const;
+
status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
@@ -213,6 +224,7 @@ private:
uint32_t mBufferID;
sp<ABuffer> mData;
sp<ABuffer> mEncryptedData;
+ sp<IMemory> mSharedEncryptedBuffer;
sp<AMessage> mNotify;
sp<AMessage> mFormat;
bool mOwnedByClient;
@@ -231,6 +243,7 @@ private:
sp<AMessage> mOutputFormat;
sp<AMessage> mInputFormat;
sp<AMessage> mCallback;
+ sp<MemoryDealer> mDealer;
bool mBatteryStatNotified;
bool mIsVideo;
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index c2bbe4d..9d1d675 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -48,9 +48,14 @@ struct MediaCodecList : public BnMediaCodecList {
return mCodecInfos.itemAt(index);
}
+ virtual const sp<AMessage> getGlobalSettings() const;
+
// to be used by MediaPlayerService alone
static sp<IMediaCodecList> getLocalInstance();
+ // only to be used in getLocalInstance
+ void updateDetailsForMultipleCodecs(const KeyedVector<AString, CodecSettings>& updates);
+
private:
class BinderDeathObserver : public IBinder::DeathRecipient {
void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -60,6 +65,7 @@ private:
enum Section {
SECTION_TOPLEVEL,
+ SECTION_SETTINGS,
SECTION_DECODERS,
SECTION_DECODER,
SECTION_DECODER_TYPE,
@@ -74,10 +80,14 @@ private:
status_t mInitCheck;
Section mCurrentSection;
+ bool mUpdate;
Vector<Section> mPastSections;
int32_t mDepth;
AString mHrefBase;
+ sp<AMessage> mGlobalSettings;
+ KeyedVector<AString, CodecSettings> mOverrides;
+
Vector<sp<MediaCodecInfo> > mCodecInfos;
sp<MediaCodecInfo> mCurrentInfo;
sp<IOMX> mOMX;
@@ -87,7 +97,7 @@ private:
status_t initCheck() const;
void parseXMLFile(const char *path);
- void parseTopLevelXMLFile(const char *path);
+ void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
static void StartElementHandlerWrapper(
void *me, const char *name, const char **attrs);
@@ -98,9 +108,12 @@ private:
void endElementHandler(const char *name);
status_t includeXMLFile(const char **attrs);
+ status_t addSettingFromAttributes(const char **attrs);
status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+ void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+
status_t addQuirk(const char **attrs);
status_t addTypeFromAttributes(const char **attrs);
status_t addLimit(const char **attrs);
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 5644428..6cc2e2b 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <audio_utils/roundup.h>
+#include <media/AudioResamplerPublic.h>
#include <media/SingleStateQueue.h>
namespace android {
@@ -113,6 +114,14 @@ struct AudioTrackSharedStatic {
mPosLoopQueue;
};
+
+struct AudioTrackPlaybackRate {
+ float mSpeed;
+ float mPitch;
+};
+
+typedef SingleStateQueue<AudioTrackPlaybackRate> AudioTrackPlaybackRateQueue;
+
// ----------------------------------------------------------------------------
// Important: do not add any virtual methods, including ~
@@ -159,6 +168,8 @@ private:
uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
// or 0 == default. Write-only client, read-only server.
+ AudioTrackPlaybackRateQueue::Shared mPlaybackRateQueue;
+
// client write-only, server read-only
uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0
@@ -313,7 +324,8 @@ public:
AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false)
: ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
- clientInServer) { }
+ clientInServer),
+ mPlaybackRateMutator(&cblk->mPlaybackRateQueue) { }
virtual ~AudioTrackClientProxy() { }
// No barriers on the following operations, so the ordering of loads/stores
@@ -333,6 +345,13 @@ public:
mCblk->mSampleRate = sampleRate;
}
+ void setPlaybackRate(float speed, float pitch) {
+ AudioTrackPlaybackRate playbackRate;
+ playbackRate.mSpeed = speed;
+ playbackRate.mPitch = pitch;
+ mPlaybackRateMutator.push(playbackRate);
+ }
+
virtual void flush();
virtual uint32_t getUnderrunFrames() const {
@@ -344,6 +363,9 @@ public:
bool getStreamEndDone() const;
status_t waitStreamEndDone(const struct timespec *requested);
+
+private:
+ AudioTrackPlaybackRateQueue::Mutator mPlaybackRateMutator;
};
class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +480,11 @@ class AudioTrackServerProxy : public ServerProxy {
public:
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
- : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
+ : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
+ mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
mCblk->mSampleRate = sampleRate;
+ mPlaybackRate.mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ mPlaybackRate.mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
}
protected:
virtual ~AudioTrackServerProxy() { }
@@ -493,6 +518,13 @@ public:
// Return the total number of frames that AudioFlinger has obtained and released
virtual size_t framesReleased() const { return mCblk->mServer; }
+
+ // Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
+ void getPlaybackRate(float *speed, float *pitch);
+
+private:
+ AudioTrackPlaybackRate mPlaybackRate; // last observed playback rate
+ AudioTrackPlaybackRateQueue::Observer mPlaybackRateObserver;
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 7fc1a78..5bbe786 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -189,13 +189,9 @@ status_t AudioRecord::set(
}
// validate parameters
- if (!audio_is_valid_format(format)) {
- ALOGE("Invalid format %#x", format);
- return BAD_VALUE;
- }
- // Temporary restriction: AudioFlinger currently supports 16-bit PCM only
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("Format %#x is not supported", format);
+ // AudioFlinger capture only supports linear PCM
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+ ALOGE("Format %#x is not linear pcm", format);
return BAD_VALUE;
}
mFormat = format;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 9150a94..8db72ee 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -658,13 +658,14 @@ status_t AudioSystem::getOutputForAttr(const audio_attributes_t *attr,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps->getOutputForAttr(attr, output, session, stream,
samplingRate, format, channelMask,
- flags, offloadInfo);
+ flags, selectedDeviceId, offloadInfo);
}
status_t AudioSystem::startOutput(audio_io_handle_t output,
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index cfdb19c..d32db7c 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -56,6 +56,24 @@ static int64_t getNowUs()
return convertTimespecToUs(tv);
}
+// Must match similar computation in createTrack_l in Threads.cpp.
+// TODO: Move to a common library
+static size_t calculateMinFrameCount(
+ uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
+ uint32_t sampleRate, float speed)
+{
+ // Ensure that buffer depth covers at least audio hardware latency
+ uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
+ "sampleRate %u speed %f minBufCount: %u",
+ afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+ return minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, afFrameCount, afSampleRate, speed);
+}
+
// static
status_t AudioTrack::getMinFrameCount(
size_t* frameCount,
@@ -94,13 +112,10 @@ status_t AudioTrack::getMinFrameCount(
return status;
}
- // Ensure that buffer depth covers at least audio hardware latency
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
- if (minBufCount < 2) {
- minBufCount = 2;
- }
+ // When called from createTrack, speed is 1.0f (normal speed).
+ // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
+ *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
- *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate);
// The formula above should always produce a non-zero value under normal circumstances:
// AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
// Return error in the unlikely event that it does not, as that's part of the API contract.
@@ -109,8 +124,8 @@ status_t AudioTrack::getMinFrameCount(
streamType, sampleRate);
return BAD_VALUE;
}
- ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u",
- *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
+ ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
+ *frameCount, afFrameCount, afSampleRate, afLatency);
return NO_ERROR;
}
@@ -121,7 +136,8 @@ AudioTrack::AudioTrack()
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
mAttributes.usage = AUDIO_USAGE_UNKNOWN;
@@ -149,7 +165,8 @@ AudioTrack::AudioTrack(
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
@@ -177,7 +194,8 @@ AudioTrack::AudioTrack(
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0)
+ mPausedPosition(0),
+ mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
@@ -357,6 +375,8 @@ status_t AudioTrack::set(
return BAD_VALUE;
}
mSampleRate = sampleRate;
+ mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
// Make copy of input parameter offloadInfo so that in the future:
// (a) createTrack_l doesn't need it as an input parameter
@@ -686,6 +706,7 @@ status_t AudioTrack::setSampleRate(uint32_t rate)
if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
return BAD_VALUE;
}
+ // TODO: Should we also check if the buffer size is compatible?
mSampleRate = rate;
mProxy->setSampleRate(rate);
@@ -716,6 +737,42 @@ uint32_t AudioTrack::getSampleRate() const
return mSampleRate;
}
+status_t AudioTrack::setPlaybackRate(float speed, float pitch)
+{
+ if (speed < AUDIO_TIMESTRETCH_SPEED_MIN
+ || speed > AUDIO_TIMESTRETCH_SPEED_MAX
+ || pitch < AUDIO_TIMESTRETCH_PITCH_MIN
+ || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (speed == mSpeed && pitch == mPitch) {
+ return NO_ERROR;
+ }
+ if (mIsTimed || isOffloadedOrDirect_l()) {
+ return INVALID_OPERATION;
+ }
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ return INVALID_OPERATION;
+ }
+ // Check if the buffer size is compatible.
+ if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) {
+ ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch);
+ return BAD_VALUE;
+ }
+ mSpeed = speed;
+ mPitch = pitch;
+ mProxy->setPlaybackRate(speed, pitch);
+ return NO_ERROR;
+}
+
+void AudioTrack::getPlaybackRate(float *speed, float *pitch) const
+{
+ AutoMutex lock(mLock);
+ *speed = mSpeed;
+ *pitch = mPitch;
+}
+
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -928,6 +985,21 @@ audio_io_handle_t AudioTrack::getOutput() const
return mOutput;
}
+status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ AutoMutex lock(mLock);
+ if (mSelectedDeviceId != deviceId) {
+ mSelectedDeviceId = deviceId;
+ return restoreTrack_l("setOutputDevice() restart");
+ } else {
+ return NO_ERROR;
+ }
+}
+
+audio_port_handle_t AudioTrack::getOutputDevice() {
+ AutoMutex lock(mLock);
+ return mSelectedDeviceId;
+}
+
status_t AudioTrack::attachAuxEffect(int effectId)
{
AutoMutex lock(mLock);
@@ -960,11 +1032,12 @@ status_t AudioTrack::createTrack_l()
audio_io_handle_t output;
audio_stream_type_t streamType = mStreamType;
audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
- status_t status = AudioSystem::getOutputForAttr(attr, &output,
- (audio_session_t)mSessionId, &streamType,
- mSampleRate, mFormat, mChannelMask,
- mFlags, mOffloadInfo);
+ status_t status;
+ status = AudioSystem::getOutputForAttr(attr, &output,
+ (audio_session_t)mSessionId, &streamType,
+ mSampleRate, mFormat, mChannelMask,
+ mFlags, mSelectedDeviceId, mOffloadInfo);
if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x,"
@@ -1067,8 +1140,16 @@ status_t AudioTrack::createTrack_l()
// there _is_ a frameCount parameter. We silently ignore it.
frameCount = mSharedBuffer->size() / mFrameSize;
} else {
- // For fast and normal streaming tracks,
- // the frame count calculations and checks are done by server
+ // For fast tracks the frame count calculations and checks are done by server
+
+ if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ // for normal tracks precompute the frame count based on speed.
+ const size_t minFrameCount = calculateMinFrameCount(
+ afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed);
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
}
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1211,6 +1292,7 @@ status_t AudioTrack::createTrack_l()
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
+ // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
// FIXME don't believe this lie
mLatency = afLatency + (1000*frameCount) / mSampleRate;
@@ -1236,6 +1318,7 @@ status_t AudioTrack::createTrack_l()
mProxy->setSendLevel(mSendLevel);
mProxy->setSampleRate(mSampleRate);
+ mProxy->setPlaybackRate(mSpeed, mPitch);
mProxy->setMinimum(mNotificationFramesAct);
mDeathNotifier = new DeathNotifier(this);
@@ -1604,6 +1687,7 @@ nsecs_t AudioTrack::processAudioBuffer()
// Cache other fields that will be needed soon
uint32_t sampleRate = mSampleRate;
+ float speed = mSpeed;
uint32_t notificationFrames = mNotificationFramesAct;
if (mRefreshRemaining) {
mRefreshRemaining = false;
@@ -1732,7 +1816,7 @@ nsecs_t AudioTrack::processAudioBuffer()
if (minFrames != (uint32_t) ~0) {
// This "fudge factor" avoids soaking CPU, and compensates for late progress by server
static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
- ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+ ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs;
}
// If not supplying data by EVENT_MORE_DATA, then we're done
@@ -1773,7 +1857,8 @@ nsecs_t AudioTrack::processAudioBuffer()
if (mRetryOnPartialBuffer && !isOffloaded()) {
mRetryOnPartialBuffer = false;
if (avail < mRemainingFrames) {
- int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+ int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000)
+ / ((double)sampleRate * speed);
if (ns < 0 || myns < ns) {
ns = myns;
}
@@ -1828,7 +1913,7 @@ nsecs_t AudioTrack::processAudioBuffer()
// that total to a sum == notificationFrames.
if (0 < misalignment && misalignment <= mRemainingFrames) {
mRemainingFrames = misalignment;
- return (mRemainingFrames * 1100000000LL) / sampleRate;
+ return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed);
}
#endif
@@ -1923,6 +2008,41 @@ uint32_t AudioTrack::updateAndGetPosition_l()
return mPosition += (uint32_t) delta;
}
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+{
+ // applicable for mixing tracks only (not offloaded or direct)
+ if (mStaticProxy != 0) {
+ return true; // static tracks do not have issues with buffer sizing.
+ }
+ status_t status;
+ uint32_t afLatency;
+ status = AudioSystem::getLatency(mOutput, &afLatency);
+ if (status != NO_ERROR) {
+ ALOGE("getLatency(%d) failed status %d", mOutput, status);
+ return false;
+ }
+
+ size_t afFrameCount;
+ status = AudioSystem::getFrameCount(mOutput, &afFrameCount);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCount(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ uint32_t afSampleRate;
+ status = AudioSystem::getSamplingRate(mOutput, &afSampleRate);
+ if (status != NO_ERROR) {
+ ALOGE("getSamplingRate(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ const size_t minFrameCount =
+ calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed);
+ ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu",
+ mFrameCount, minFrameCount);
+ return mFrameCount >= minFrameCount;
+}
+
status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
AutoMutex lock(mLock);
@@ -1988,7 +2108,8 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
- const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
+ const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
+ / ((double)mSampleRate * mSpeed);
if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
// Verify that the counter can't count faster than the sample rate
@@ -2075,7 +2196,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const
snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
- snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
+ snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n",
+ mSampleRate, mSpeed, mStatus);
result.append(buffer);
snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency);
result.append(buffer);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 6d5f1af..ba67b40 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -793,6 +793,16 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
(void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
}
+void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch)
+{ // do not call from multiple threads without holding lock
+ AudioTrackPlaybackRate playbackRate;
+ if (mPlaybackRateObserver.poll(playbackRate)) {
+ mPlaybackRate = playbackRate;
+ }
+ *speed = mPlaybackRate.mSpeed;
+ *pitch = mPlaybackRate.mPitch;
+}
+
// ---------------------------------------------------------------------------
StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 39374d8..4b86532 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -173,6 +173,7 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
Parcel data, reply;
@@ -208,6 +209,7 @@ public:
data.writeInt32(static_cast <uint32_t>(format));
data.writeInt32(channelMask);
data.writeInt32(static_cast <uint32_t>(flags));
+ data.writeInt32(selectedDeviceId);
// hasOffloadInfo
if (offloadInfo == NULL) {
data.writeInt32(0);
@@ -815,6 +817,7 @@ status_t BnAudioPolicyService::onTransact(
audio_channel_mask_t channelMask = data.readInt32();
audio_output_flags_t flags =
static_cast <audio_output_flags_t>(data.readInt32());
+ audio_port_handle_t selectedDeviceId = data.readInt32();
bool hasOffloadInfo = data.readInt32() != 0;
audio_offload_info_t offloadInfo;
if (hasOffloadInfo) {
@@ -824,7 +827,7 @@ status_t BnAudioPolicyService::onTransact(
status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
&output, session, &stream,
samplingRate, format, channelMask,
- flags, hasOffloadInfo ? &offloadInfo : NULL);
+ flags, selectedDeviceId, hasOffloadInfo ? &offloadInfo : NULL);
reply->writeInt32(status);
reply->writeInt32(output);
reply->writeInt32(stream);
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index c26c5bf..9246a7c 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <binder/Parcel.h>
+#include <binder/IMemory.h>
#include <media/ICrypto.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -34,6 +35,7 @@ enum {
REQUIRES_SECURE_COMPONENT,
DECRYPT,
NOTIFY_RESOLUTION,
+ SET_MEDIADRM_SESSION,
};
struct BpCrypto : public BpInterface<ICrypto> {
@@ -97,7 +99,7 @@ struct BpCrypto : public BpInterface<ICrypto> {
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
@@ -126,7 +128,8 @@ struct BpCrypto : public BpInterface<ICrypto> {
}
data.writeInt32(totalSize);
- data.write(srcPtr, totalSize);
+ data.writeStrongBinder(IInterface::asBinder(sharedBuffer));
+ data.writeInt32(offset);
data.writeInt32(numSubSamples);
data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples);
@@ -159,7 +162,28 @@ struct BpCrypto : public BpInterface<ICrypto> {
remote()->transact(NOTIFY_RESOLUTION, data, &reply);
}
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
+
+ return reply.readInt32();
+ }
+
private:
+ void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+ uint32_t size = reply.readInt32();
+ vector.insertAt((size_t)0, size);
+ reply.read(vector.editArray(), size);
+ }
+
+ void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+ data.writeInt32(vector.size());
+ data.write(vector.array(), vector.size());
+ }
+
DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
};
@@ -167,6 +191,17 @@ IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
////////////////////////////////////////////////////////////////////////////////
+void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+ uint32_t size = data.readInt32();
+ vector.insertAt((size_t)0, size);
+ data.read(vector.editArray(), size);
+}
+
+void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+ reply->writeInt32(vector.size());
+ reply->write(vector.array(), vector.size());
+}
+
status_t BnCrypto::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
@@ -245,8 +280,9 @@ status_t BnCrypto::onTransact(
data.read(iv, sizeof(iv));
size_t totalSize = data.readInt32();
- void *srcData = malloc(totalSize);
- data.read(srcData, totalSize);
+ sp<IMemory> sharedBuffer =
+ interface_cast<IMemory>(data.readStrongBinder());
+ int32_t offset = data.readInt32();
int32_t numSubSamples = data.readInt32();
@@ -265,15 +301,21 @@ status_t BnCrypto::onTransact(
}
AString errorDetailMsg;
- ssize_t result = decrypt(
+ ssize_t result;
+
+ if (offset + totalSize > sharedBuffer->size()) {
+ result = -EINVAL;
+ } else {
+ result = decrypt(
secure,
key,
iv,
mode,
- srcData,
+ sharedBuffer, offset,
subSamples, numSubSamples,
dstPtr,
&errorDetailMsg);
+ }
reply->writeInt32(result);
@@ -294,9 +336,6 @@ status_t BnCrypto::onTransact(
delete[] subSamples;
subSamples = NULL;
- free(srcData);
- srcData = NULL;
-
return OK;
}
@@ -311,6 +350,15 @@ status_t BnCrypto::onTransact(
return OK;
}
+ case SET_MEDIADRM_SESSION:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId;
+ readVector(data, sessionId);
+ reply->writeInt32(setMediaDrmSession(sessionId));
+ return OK;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp
index 80020db..e2df104 100644
--- a/media/libmedia/IMediaCodecList.cpp
+++ b/media/libmedia/IMediaCodecList.cpp
@@ -30,6 +30,7 @@ enum {
CREATE = IBinder::FIRST_CALL_TRANSACTION,
COUNT_CODECS,
GET_CODEC_INFO,
+ GET_GLOBAL_SETTINGS,
FIND_CODEC_BY_TYPE,
FIND_CODEC_BY_NAME,
};
@@ -64,6 +65,19 @@ public:
}
}
+ virtual const sp<AMessage> getGlobalSettings() const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaCodecList::getInterfaceDescriptor());
+ remote()->transact(GET_GLOBAL_SETTINGS, data, &reply);
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ return AMessage::FromParcel(reply);
+ } else {
+ return NULL;
+ }
+ }
+
virtual ssize_t findCodecByType(
const char *type, bool encoder, size_t startIndex = 0) const
{
@@ -125,6 +139,20 @@ status_t BnMediaCodecList::onTransact(
}
break;
+ case GET_GLOBAL_SETTINGS:
+ {
+ CHECK_INTERFACE(IMediaCodecList, data, reply);
+ const sp<AMessage> info = getGlobalSettings();
+ if (info != NULL) {
+ reply->writeInt32(OK);
+ info->writeToParcel(reply);
+ } else {
+ reply->writeInt32(-ERANGE);
+ }
+ return NO_ERROR;
+ }
+ break;
+
case FIND_CODEC_BY_TYPE:
{
CHECK_INTERFACE(IMediaCodecList, data, reply);
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 7b4c4e2..8d3fa7b 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -206,6 +206,17 @@ status_t MediaCodecInfo::addMime(const char *mime) {
return OK;
}
+status_t MediaCodecInfo::updateMime(const char *mime) {
+ ssize_t ix = getCapabilityIndex(mime);
+ if (ix < 0) {
+ ALOGE("updateMime mime not found %s", mime);
+ return -EINVAL;
+ }
+
+ mCurrentCaps = mCaps.valueAt(ix);
+ return OK;
+}
+
void MediaCodecInfo::removeMime(const char *mime) {
ssize_t ix = getCapabilityIndex(mime);
if (ix >= 0) {
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index 8ee7c0b..f639193 100644
--- a/media/libmediaplayerservice/Crypto.cpp
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -22,6 +22,7 @@
#include "Crypto.h"
+#include <binder/IMemory.h>
#include <media/hardware/CryptoAPI.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
@@ -238,7 +239,7 @@ ssize_t Crypto::decrypt(
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
@@ -252,6 +253,8 @@ ssize_t Crypto::decrypt(
return -EINVAL;
}
+ const void *srcPtr = static_cast<uint8_t *>(sharedBuffer->pointer()) + offset;
+
return mPlugin->decrypt(
secure, key, iv, mode, srcPtr, subSamples, numSubSamples, dstPtr,
errorDetailMsg);
@@ -265,4 +268,14 @@ void Crypto::notifyResolution(uint32_t width, uint32_t height) {
}
}
+status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t result = NO_INIT;
+ if (mInitCheck == OK && mPlugin != NULL) {
+ result = mPlugin->setMediaDrmSession(sessionId);
+ }
+ return result;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
index 0037c2e..99ea95d 100644
--- a/media/libmediaplayerservice/Crypto.h
+++ b/media/libmediaplayerservice/Crypto.h
@@ -47,12 +47,14 @@ struct Crypto : public BnCrypto {
virtual void notifyResolution(uint32_t width, uint32_t height);
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
- const void *srcPtr,
+ const sp<IMemory> &sharedBuffer, size_t offset,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg);
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 49e01d1..62cf3e5 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -136,22 +136,54 @@ void Drm::sendEvent(DrmPlugin::EventType eventType, int extra,
if (listener != NULL) {
Parcel obj;
- if (sessionId && sessionId->size()) {
- obj.writeInt32(sessionId->size());
- obj.write(sessionId->array(), sessionId->size());
- } else {
- obj.writeInt32(0);
- }
+ writeByteArray(obj, sessionId);
+ writeByteArray(obj, data);
- if (data && data->size()) {
- obj.writeInt32(data->size());
- obj.write(data->array(), data->size());
- } else {
- obj.writeInt32(0);
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(eventType, extra, &obj);
+ }
+}
+
+void Drm::sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+ int64_t expiryTimeInMS)
+{
+ mEventLock.lock();
+ sp<IDrmClient> listener = mListener;
+ mEventLock.unlock();
+
+ if (listener != NULL) {
+ Parcel obj;
+ writeByteArray(obj, sessionId);
+ obj.writeInt64(expiryTimeInMS);
+
+ Mutex::Autolock lock(mNotifyLock);
+ listener->notify(DrmPlugin::kDrmPluginEventExpirationUpdate, 0, &obj);
+ }
+}
+
+void Drm::sendKeysChange(Vector<uint8_t> const *sessionId,
+ Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+ bool hasNewUsableKey)
+{
+ mEventLock.lock();
+ sp<IDrmClient> listener = mListener;
+ mEventLock.unlock();
+
+ if (listener != NULL) {
+ Parcel obj;
+ writeByteArray(obj, sessionId);
+
+ size_t nkeys = keyStatusList->size();
+ obj.writeInt32(keyStatusList->size());
+ for (size_t i = 0; i < nkeys; ++i) {
+ const DrmPlugin::KeyStatus *keyStatus = &keyStatusList->itemAt(i);
+ writeByteArray(obj, &keyStatus->mKeyId);
+ obj.writeInt32(keyStatus->mType);
}
+ obj.writeInt32(hasNewUsableKey);
Mutex::Autolock lock(mNotifyLock);
- listener->notify(eventType, extra, &obj);
+ listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
}
}
@@ -756,4 +788,14 @@ void Drm::binderDied(const wp<IBinder> &the_late_who)
closeFactory();
}
+void Drm::writeByteArray(Parcel &obj, Vector<uint8_t> const *array)
+{
+ if (array && array->size()) {
+ obj.writeInt32(array->size());
+ obj.write(array->array(), array->size());
+ } else {
+ obj.writeInt32(0);
+ }
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 7e8f246..1591738 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -133,6 +133,13 @@ struct Drm : public BnDrm,
Vector<uint8_t> const *sessionId,
Vector<uint8_t> const *data);
+ virtual void sendExpirationUpdate(Vector<uint8_t> const *sessionId,
+ int64_t expiryTimeInMS);
+
+ virtual void sendKeysChange(Vector<uint8_t> const *sessionId,
+ Vector<DrmPlugin::KeyStatus> const *keyStatusList,
+ bool hasNewUsableKey);
+
virtual void binderDied(const wp<IBinder> &the_late_who);
private:
@@ -157,7 +164,7 @@ private:
void findFactoryForScheme(const uint8_t uuid[16]);
bool loadLibraryForScheme(const String8 &path, const uint8_t uuid[16]);
void closeFactory();
-
+ void writeByteArray(Parcel &obj, Vector<uint8_t> const *array);
DISALLOW_EVIL_CONSTRUCTORS(Drm);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 04ac699..3fff1e6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -82,25 +82,69 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatCodecNotify:
{
- if (!isStaleReply(msg)) {
- int32_t numInput, numOutput;
+ if (mPaused) {
+ break;
+ }
+
+ int32_t cbID;
+ CHECK(msg->findInt32("callbackID", &cbID));
+
+ ALOGV("kWhatCodecNotify: cbID = %d", cbID);
+ switch (cbID) {
+ case MediaCodec::CB_INPUT_AVAILABLE:
+ {
+ int32_t index;
+ CHECK(msg->findInt32("index", &index));
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
+ handleAnInputBuffer(index);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_AVAILABLE:
+ {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+
+ CHECK(msg->findInt32("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", &flags));
+
+ handleAnOutputBuffer(index, offset, size, timeUs, flags);
+ break;
}
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
+ case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
+ {
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ handleOutputFormatChange(format);
+ break;
}
- if (!mPaused) {
- while (numInput-- > 0 && handleAnInputBuffer()) {}
+ case MediaCodec::CB_ERROR:
+ {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+ ALOGE("Decoder (%s) reported error : 0x%x",
+ mIsAudio ? "audio" : "video", err);
+
+ handleError(err);
+ break;
}
- while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+ default:
+ {
+ TRESPASS();
+ break;
+ }
}
- requestCodecNotification();
break;
}
@@ -188,6 +232,9 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
+ mCodec->setCallback(reply);
+
err = mCodec->start();
if (err != OK) {
ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
@@ -197,18 +244,8 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
return;
}
- // the following should work after start
- CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
releaseAndResetMediaBuffers();
- CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
- ALOGV("[%s] got %zu input and %zu output buffers",
- mComponentName.c_str(),
- mInputBuffers.size(),
- mOutputBuffers.size());
- if (mRenderer != NULL) {
- requestCodecNotification();
- }
mPaused = false;
mResumePending = false;
}
@@ -217,16 +254,14 @@ void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
bool hadNoRenderer = (mRenderer == NULL);
mRenderer = renderer;
if (hadNoRenderer && mRenderer != NULL) {
- requestCodecNotification();
+ // this means that the widevine legacy source is ready
+ onRequestInputBuffers();
}
}
void NuPlayer::Decoder::onGetInputBuffers(
Vector<sp<ABuffer> > *dstBuffers) {
- dstBuffers->clear();
- for (size_t i = 0; i < mInputBuffers.size(); i++) {
- dstBuffers->push(mInputBuffers[i]);
- }
+ CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
}
void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -235,6 +270,7 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) {
if (notifyComplete) {
mResumePending = true;
}
+ mCodec->start();
}
void NuPlayer::Decoder::doFlush(bool notifyComplete) {
@@ -261,8 +297,10 @@ void NuPlayer::Decoder::doFlush(bool notifyComplete) {
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+ mPaused = true;
}
+
void NuPlayer::Decoder::onFlush() {
doFlush(true);
@@ -276,7 +314,6 @@ void NuPlayer::Decoder::onFlush() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
- mPaused = true;
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -320,7 +357,9 @@ void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
}
void NuPlayer::Decoder::doRequestBuffers() {
- if (isDiscontinuityPending()) {
+ // mRenderer is only NULL if we have a legacy widevine source that
+ // is not yet ready. In this case we must not fetch input.
+ if (isDiscontinuityPending() || mRenderer == NULL) {
return;
}
status_t err = OK;
@@ -347,34 +386,50 @@ void NuPlayer::Decoder::doRequestBuffers() {
}
}
-bool NuPlayer::Decoder::handleAnInputBuffer() {
+void NuPlayer::Decoder::handleError(int32_t err)
+{
+ // We cannot immediately release the codec due to buffers still outstanding
+ // in the renderer. We signal to the player the error so it can shutdown/release the
+ // decoder after flushing and increment the generation to discard unnecessary messages.
+
+ ++mBufferGeneration;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
if (isDiscontinuityPending()) {
return false;
}
- size_t bufferIx = -1;
- status_t res = mCodec->dequeueInputBuffer(&bufferIx);
- ALOGV("[%s] dequeued input: %d",
- mComponentName.c_str(), res == OK ? (int)bufferIx : res);
- if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue input buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
+
+ sp<ABuffer> buffer;
+ mCodec->getInputBuffer(index, &buffer);
+
+ if (index >= mInputBuffers.size()) {
+ for (size_t i = mInputBuffers.size(); i <= index; ++i) {
+ mInputBuffers.add();
+ mMediaBuffers.add();
+ mInputBufferIsDequeued.add();
+ mMediaBuffers.editItemAt(i) = NULL;
+ mInputBufferIsDequeued.editItemAt(i) = false;
}
- return false;
}
+ mInputBuffers.editItemAt(index) = buffer;
- CHECK_LT(bufferIx, mInputBuffers.size());
+ //CHECK_LT(bufferIx, mInputBuffers.size());
- if (mMediaBuffers[bufferIx] != NULL) {
- mMediaBuffers[bufferIx]->release();
- mMediaBuffers.editItemAt(bufferIx) = NULL;
+ if (mMediaBuffers[index] != NULL) {
+ mMediaBuffers[index]->release();
+ mMediaBuffers.editItemAt(index) = NULL;
}
- mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+ mInputBufferIsDequeued.editItemAt(index) = true;
if (!mCSDsToSubmit.isEmpty()) {
sp<AMessage> msg = new AMessage();
- msg->setSize("buffer-ix", bufferIx);
+ msg->setSize("buffer-ix", index);
sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
@@ -392,94 +447,38 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
mPendingInputMessages.erase(mPendingInputMessages.begin());
}
- if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
+ if (!mInputBufferIsDequeued.editItemAt(index)) {
return true;
}
- mDequeuedInputBuffers.push_back(bufferIx);
+ mDequeuedInputBuffers.push_back(index);
onRequestInputBuffers();
return true;
}
-bool NuPlayer::Decoder::handleAnOutputBuffer() {
- size_t bufferIx = -1;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- status_t res = mCodec->dequeueOutputBuffer(
- &bufferIx, &offset, &size, &timeUs, &flags);
-
- if (res != OK) {
- ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
- } else {
- ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
- mComponentName.c_str(), (int)bufferIx, timeUs, flags);
+bool NuPlayer::Decoder::handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags) {
+ if (mFormatChangePending) {
+ return false;
}
- if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- res = mCodec->getOutputBuffers(&mOutputBuffers);
- if (res != OK) {
- ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- // NuPlayer ignores this
- return true;
- } else if (res == INFO_FORMAT_CHANGED) {
- sp<AMessage> format = new AMessage();
- res = mCodec->getOutputFormat(&format);
- if (res != OK) {
- ALOGE("Failed to get output format for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
-
- if (!mIsAudio) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatVideoSizeChanged);
- notify->setMessage("format", format);
- notify->post();
- } else if (mRenderer != NULL) {
- uint32_t flags;
- int64_t durationUs;
- bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
- if (!hasVideo &&
- mSource->getDuration(&durationUs) == OK &&
- durationUs
- > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
- flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
- } else {
- flags = AUDIO_OUTPUT_FLAG_NONE;
- }
+// CHECK_LT(bufferIx, mOutputBuffers.size());
+ sp<ABuffer> buffer;
+ mCodec->getOutputBuffer(index, &buffer);
- res = mRenderer->openAudioSink(
- format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */);
- if (res != OK) {
- ALOGE("Failed to open AudioSink on format change for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- }
- return true;
- } else if (res == INFO_DISCONTINUITY) {
- // nothing to do
- return true;
- } else if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue output buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
+ if (index >= mOutputBuffers.size()) {
+ for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
+ mOutputBuffers.add();
}
- return false;
}
- CHECK_LT(bufferIx, mOutputBuffers.size());
- sp<ABuffer> buffer = mOutputBuffers[bufferIx];
+ mOutputBuffers.editItemAt(index) = buffer;
+
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
@@ -488,7 +487,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
// we do not expect CODECCONFIG or SYNCFRAME for decoder
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
- reply->setSize("buffer-ix", bufferIx);
+ reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
if (eos) {
@@ -522,6 +521,29 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
return true;
}
+void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
+ if (!mIsAudio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatVideoSizeChanged);
+ notify->setMessage("format", format);
+ notify->post();
+ } else if (mRenderer != NULL) {
+ uint32_t flags;
+ int64_t durationUs;
+ bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
+ if (!hasVideo &&
+ mSource->getDuration(&durationUs) == OK &&
+ durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ } else {
+ flags = AUDIO_OUTPUT_FLAG_NONE;
+ }
+
+ mRenderer->openAudioSink(
+ format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */);
+ }
+}
+
void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
for (size_t i = 0; i < mMediaBuffers.size(); i++) {
if (mMediaBuffers[i] != NULL) {
@@ -825,7 +847,8 @@ void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
mPaused = true;
} else if (mTimeChangePending) {
if (flushOnTimeChange) {
- doFlush(false /*notifyComplete*/);
+ doFlush(false /* notifyComplete */);
+ signalResume(false /* notifyComplete */);
}
// restart fetching input
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4aab2c6..0c0e90c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -87,8 +87,15 @@ private:
bool mResumePending;
AString mComponentName;
- bool handleAnInputBuffer();
- bool handleAnOutputBuffer();
+ void handleError(int32_t err);
+ bool handleAnInputBuffer(size_t index);
+ bool handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags);
+ void handleOutputFormatChange(const sp<AMessage> &format);
void releaseAndResetMediaBuffers();
void requestCodecNotification();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index a2ec51c..827bdc1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -312,6 +312,9 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
int64_t delayUs =
mAudioSink->msecsPerFrame()
* numFramesPendingPlayout * 1000ll;
+ if (mPlaybackRate > 1.0f) {
+ delayUs /= mPlaybackRate;
+ }
// Let's give it more data after about half that time
// has elapsed.
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 97f3e20..45f6339 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1685,6 +1685,16 @@ status_t ACodec::configureCodec(
err = setPriority(priority);
}
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!msg->findFloat("operating-rate", &rateFloat)) {
+ msg->findInt32("operating-rate", &rateInt);
+ rateFloat = (float)rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ err = setOperatingRate(rateFloat, video);
+ }
+
mBaseOutputFormat = outputFormat;
CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK);
@@ -1711,6 +1721,34 @@ status_t ACodec::setPriority(int32_t priority) {
return OK;
}
+status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) {
+ if (rateFloat < 0) {
+ return BAD_VALUE;
+ }
+ OMX_U32 rate;
+ if (isVideo) {
+ if (rateFloat > 65535) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f);
+ } else {
+ if (rateFloat > UINT_MAX) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat);
+ }
+ OMX_PARAM_U32TYPE config;
+ InitOMXParams(&config);
+ config.nU32 = rate;
+ status_t err = mOMX->setConfig(
+ mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate,
+ &config, sizeof(config));
+ if (err != OK) {
+ ALOGI("codec does not support config operating rate (err %d)", err);
+ }
+ return OK;
+}
+
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -4902,6 +4940,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = NULL;
+ status_t err = OMX_ErrorComponentNotFound;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).mName.string();
@@ -4910,7 +4949,7 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
- status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+ err = omx->allocateNode(componentName.c_str(), observer, &node);
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
@@ -4924,13 +4963,13 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
if (node == NULL) {
if (!mime.empty()) {
- ALOGE("Unable to instantiate a %scoder for type '%s'.",
- encoder ? "en" : "de", mime.c_str());
+ ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
+ encoder ? "en" : "de", mime.c_str(), err);
} else {
- ALOGE("Unable to instantiate codec '%s'.", componentName.c_str());
+ ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
}
- mCodec->signalError(OMX_ErrorComponentNotFound);
+ mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
return false;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index a2cbdaf..b0eeb7f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -34,6 +34,7 @@ LOCAL_SRC_FILES:= \
MediaClock.cpp \
MediaCodec.cpp \
MediaCodecList.cpp \
+ MediaCodecListOverrides.cpp \
MediaCodecSource.cpp \
MediaDefs.cpp \
MediaExtractor.cpp \
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
index 427bf7b..8fbb57c 100644
--- a/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/ESDS.cpp
@@ -136,6 +136,8 @@ status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
--size;
if (streamDependenceFlag) {
+ if (size < 2)
+ return ERROR_MALFORMED;
offset += 2;
size -= 2;
}
@@ -145,11 +147,15 @@ status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
return ERROR_MALFORMED;
}
unsigned URLlength = mData[offset];
+ if (URLlength >= size)
+ return ERROR_MALFORMED;
offset += URLlength + 1;
size -= URLlength + 1;
}
if (OCRstreamFlag) {
+ if (size < 2)
+ return ERROR_MALFORMED;
offset += 2;
size -= 2;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d0f42cc..f7fa2b6 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -874,6 +874,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
}
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->sampleTable = new SampleTable(mDataSource);
}
@@ -1028,6 +1031,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
}
original_fourcc = ntohl(original_fourcc);
ALOGV("read original format: %d", original_fourcc);
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc));
uint32_t num_channels = 0;
uint32_t sample_rate = 0;
@@ -1083,6 +1090,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId);
mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize);
mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16);
@@ -1168,6 +1178,11 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (!timescale) {
+ ALOGE("timescale should not be ZERO.");
+ return ERROR_MALFORMED;
+ }
+
mLastTrack->timescale = ntohl(timescale);
// 14496-12 says all ones means indeterminate, but some files seem to use
@@ -1193,7 +1208,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
duration = ntohl(duration32);
}
}
- if (duration != 0) {
+ if (duration != 0 && mLastTrack->timescale != 0) {
mLastTrack->meta->setInt64(
kKeyDuration, (duration * 1000000) / mLastTrack->timescale);
}
@@ -1257,6 +1272,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
// display the timed text.
// For encrypted files, there may also be more than one entry.
const char *mime;
+
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime));
if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) &&
strcasecmp(mime, "application/octet-stream")) {
@@ -1303,6 +1322,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
uint16_t sample_size = U16_AT(&buffer[18]);
uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
// if the chunk type is enca, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1364,6 +1386,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
// printf("*** coding='%s' width=%d height=%d\n",
// chunk, width, height);
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
if (chunk_type != FOURCC('e', 'n', 'c', 'v')) {
// if the chunk type is encv, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1389,6 +1414,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('s', 't', 'c', 'o'):
case FOURCC('c', 'o', '6', '4'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setChunkOffsetParams(
chunk_type, data_offset, chunk_data_size);
@@ -1404,6 +1432,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('s', 't', 's', 'c'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setSampleToChunkParams(
data_offset, chunk_data_size);
@@ -1420,6 +1451,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('s', 't', 's', 'z'):
case FOURCC('s', 't', 'z', '2'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
status_t err =
mLastTrack->sampleTable->setSampleSizeParams(
chunk_type, data_offset, chunk_data_size);
@@ -1489,6 +1523,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('s', 't', 't', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1504,6 +1541,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('c', 't', 't', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1519,6 +1559,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('s', 't', 's', 's'):
{
+ if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL))
+ return ERROR_MALFORMED;
+
*offset += chunk_size;
status_t err =
@@ -1591,6 +1634,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_MALFORMED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
@@ -1623,6 +1669,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
@@ -1637,6 +1686,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(
kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size);
@@ -1670,6 +1722,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size);
break;
@@ -1767,7 +1822,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
}
duration = d32;
}
- if (duration != 0) {
+ if (duration != 0 && mHeaderTimescale != 0) {
mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
@@ -1816,7 +1871,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_MALFORMED;
}
- if (duration != 0) {
+ if (duration != 0 && mHeaderTimescale != 0) {
mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale);
}
@@ -1851,6 +1906,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
return ERROR_IO;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
uint32_t type = ntohl(buffer);
// For the 3GPP file format, the handler-type within the 'hdlr' box
// shall be 'text'. We also want to support 'sbtl' handler type
@@ -1883,6 +1941,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
case FOURCC('t', 'x', '3', 'g'):
{
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
uint32_t type;
const void *data;
size_t size = 0;
@@ -2024,6 +2085,8 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
return ERROR_MALFORMED;
}
ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale);
+ if (timeScale == 0)
+ return ERROR_MALFORMED;
uint64_t earliestPresentationTime;
uint64_t firstOffset;
@@ -2107,6 +2170,9 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) {
uint64_t sidxDuration = total_duration * 1000000 / timeScale;
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
int64_t metaDuration;
if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) {
mLastTrack->meta->setInt64(kKeyDuration, sidxDuration);
@@ -2157,6 +2223,9 @@ status_t MPEG4Extractor::parseTrackHeader(
return ERROR_UNSUPPORTED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyTrackID, id);
size_t matrixOffset = dynSize + 16;
@@ -2339,6 +2408,9 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) {
int32_t delay, padding;
if (sscanf(mLastCommentData,
" %*x %x %x %*x", &delay, &padding) == 2) {
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setInt32(kKeyEncoderDelay, delay);
mLastTrack->meta->setInt32(kKeyEncoderPadding, padding);
}
@@ -2635,6 +2707,11 @@ status_t MPEG4Extractor::verifyTrack(Track *track) {
return ERROR_MALFORMED;
}
+ if (track->timescale == 0) {
+ ALOGE("timescale invalid.");
+ return ERROR_MALFORMED;
+ }
+
return OK;
}
@@ -2701,6 +2778,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
if (objectTypeIndication == 0xe1) {
// This isn't MPEG4 audio at all, it's QCELP 14k...
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
return OK;
}
@@ -2749,6 +2829,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
objectType = 32 + br.getBits(6);
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
//keep AOT type
mLastTrack->meta->setInt32(kKeyAACAOT, objectType);
@@ -2919,6 +3002,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
return ERROR_UNSUPPORTED;
}
+ if (mLastTrack == NULL)
+ return ERROR_MALFORMED;
+
int32_t prevSampleRate;
CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 433f555..2641e4e 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -92,6 +92,11 @@ void MediaClock::setPlaybackRate(float rate) {
mPlaybackRate = rate;
}
+float MediaClock::getPlaybackRate() const {
+ Mutex::Autolock autoLock(mLock);
+ return mPlaybackRate;
+}
+
status_t MediaClock::getMediaTime(
int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const {
if (outMediaUs == NULL) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 0597f1d..5538cb0 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,7 +22,9 @@
#include "include/SoftwareRenderer.h"
#include <binder/IBatteryStats.h>
+#include <binder/IMemory.h>
#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
#include <gui/Surface.h>
#include <media/ICrypto.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -47,16 +49,31 @@ namespace android {
struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> {
BatteryNotifier();
+ virtual ~BatteryNotifier();
void noteStartVideo();
void noteStopVideo();
void noteStartAudio();
void noteStopAudio();
+ void onBatteryStatServiceDied();
private:
+ struct DeathNotifier : public IBinder::DeathRecipient {
+ DeathNotifier() {}
+ virtual void binderDied(const wp<IBinder>& /*who*/) {
+ BatteryNotifier::getInstance().onBatteryStatServiceDied();
+ }
+ };
+
+ Mutex mLock;
int32_t mVideoRefCount;
int32_t mAudioRefCount;
sp<IBatteryStats> mBatteryStatService;
+ sp<DeathNotifier> mDeathNotifier;
+
+ sp<IBatteryStats> getBatteryService_l();
+
+ DISALLOW_EVIL_CONSTRUCTORS(BatteryNotifier);
};
ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
@@ -64,54 +81,103 @@ ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
MediaCodec::BatteryNotifier::BatteryNotifier() :
mVideoRefCount(0),
mAudioRefCount(0) {
- // get battery service
+}
+
+sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() {
+ if (mBatteryStatService != NULL) {
+ return mBatteryStatService;
+ }
+ // get battery service from service manager
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != NULL) {
const String16 name("batterystats");
- mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name));
+ mBatteryStatService =
+ interface_cast<IBatteryStats>(sm->getService(name));
if (mBatteryStatService == NULL) {
ALOGE("batterystats service unavailable!");
+ return NULL;
+ }
+ mDeathNotifier = new DeathNotifier();
+ if (IInterface::asBinder(mBatteryStatService)->
+ linkToDeath(mDeathNotifier) != OK) {
+ mBatteryStatService.clear();
+ mDeathNotifier.clear();
+ return NULL;
}
+ // notify start now if media already started
+ if (mVideoRefCount > 0) {
+ mBatteryStatService->noteStartVideo(AID_MEDIA);
+ }
+ if (mAudioRefCount > 0) {
+ mBatteryStatService->noteStartAudio(AID_MEDIA);
+ }
+ }
+ return mBatteryStatService;
+}
+
+MediaCodec::BatteryNotifier::~BatteryNotifier() {
+ if (mDeathNotifier != NULL) {
+ IInterface::asBinder(mBatteryStatService)->
+ unlinkToDeath(mDeathNotifier);
}
}
void MediaCodec::BatteryNotifier::noteStartVideo() {
- if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStartVideo(AID_MEDIA);
+ Mutex::Autolock _l(mLock);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mVideoRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStartVideo(AID_MEDIA);
}
mVideoRefCount++;
}
void MediaCodec::BatteryNotifier::noteStopVideo() {
+ Mutex::Autolock _l(mLock);
if (mVideoRefCount == 0) {
ALOGW("BatteryNotifier::noteStop(): video refcount is broken!");
return;
}
mVideoRefCount--;
- if (mVideoRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStopVideo(AID_MEDIA);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mVideoRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStopVideo(AID_MEDIA);
}
}
void MediaCodec::BatteryNotifier::noteStartAudio() {
- if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStartAudio(AID_MEDIA);
+ Mutex::Autolock _l(mLock);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mAudioRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStartAudio(AID_MEDIA);
}
mAudioRefCount++;
}
void MediaCodec::BatteryNotifier::noteStopAudio() {
+ Mutex::Autolock _l(mLock);
if (mAudioRefCount == 0) {
ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!");
return;
}
mAudioRefCount--;
- if (mAudioRefCount == 0 && mBatteryStatService != NULL) {
- mBatteryStatService->noteStopAudio(AID_MEDIA);
+ sp<IBatteryStats> batteryService = getBatteryService_l();
+ if (mAudioRefCount == 0 && batteryService != NULL) {
+ batteryService->noteStopAudio(AID_MEDIA);
}
}
+
+void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() {
+ Mutex::Autolock _l(mLock);
+ mBatteryStatService.clear();
+ mDeathNotifier.clear();
+ // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+ // counting is independent of the battery service availability.
+ // We need this if battery service becomes available after media
+ // started.
+}
+
// static
sp<MediaCodec> MediaCodec::CreateByType(
const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
@@ -544,6 +610,16 @@ status_t MediaCodec::getName(AString *name) const {
return OK;
}
+status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
+ msg->setInt32("portIndex", kPortIndexInput);
+ msg->setPointer("buffers", buffers);
+ msg->setInt32("widevine", true);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
msg->setInt32("portIndex", kPortIndexInput);
@@ -969,6 +1045,17 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
size_t numBuffers = portDesc->countBuffers();
+ size_t totalSize = 0;
+ for (size_t i = 0; i < numBuffers; ++i) {
+ if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ totalSize += portDesc->bufferAt(i)->capacity();
+ }
+ }
+
+ if (totalSize) {
+ mDealer = new MemoryDealer(totalSize, "MediaCodec");
+ }
+
for (size_t i = 0; i < numBuffers; ++i) {
BufferInfo info;
info.mBufferID = portDesc->bufferIDAt(i);
@@ -976,8 +1063,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
info.mData = portDesc->bufferAt(i);
if (portIndex == kPortIndexInput && mCrypto != NULL) {
+ sp<IMemory> mem = mDealer->allocate(info.mData->capacity());
info.mEncryptedData =
- new ABuffer(info.mData->capacity());
+ new ABuffer(mem->pointer(), info.mData->capacity());
+ info.mSharedEncryptedBuffer = mem;
}
buffers->push_back(info);
@@ -1587,8 +1676,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ // Unfortunately widevine legacy source requires knowing all of the
+ // codec input buffers, so we have to provide them even in async mode.
+ int32_t widevine = 0;
+ msg->findInt32("widevine", &widevine);
- if (!isExecuting() || (mFlags & kFlagIsAsync)) {
+ if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) {
PostReplyWithError(replyID, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
@@ -1953,7 +2046,8 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
key,
iv,
mode,
- info->mEncryptedData->base() + offset,
+ info->mSharedEncryptedBuffer,
+ offset,
subSamples,
numSubSamples,
info->mData->base(),
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cf6e937..26798ae 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "MediaCodecList"
#include <utils/Log.h>
+#include "MediaCodecListOverrides.h"
+
#include <binder/IServiceManager.h>
#include <media/IMediaCodecList.h>
@@ -31,6 +33,7 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <sys/stat.h>
#include <utils/threads.h>
#include <libexpat/expat.h>
@@ -41,21 +44,58 @@ static Mutex sInitMutex;
static MediaCodecList *gCodecList = NULL;
+static const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
+
+static bool parseBoolean(const char *s) {
+ if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
+ return true;
+ }
+ char *end;
+ unsigned long res = strtoul(s, &end, 10);
+ return *s != '\0' && *end == '\0' && res > 0;
+}
+
// static
sp<IMediaCodecList> MediaCodecList::sCodecList;
// static
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
- Mutex::Autolock autoLock(sInitMutex);
-
- if (gCodecList == NULL) {
- gCodecList = new MediaCodecList;
- if (gCodecList->initCheck() == OK) {
- sCodecList = gCodecList;
+ bool profilingNeeded = false;
+ KeyedVector<AString, CodecSettings> updates;
+ Vector<sp<MediaCodecInfo>> infos;
+
+ {
+ Mutex::Autolock autoLock(sInitMutex);
+
+ if (gCodecList == NULL) {
+ gCodecList = new MediaCodecList;
+ if (gCodecList->initCheck() == OK) {
+ sCodecList = gCodecList;
+
+ struct stat s;
+ if (stat(kProfilingResults, &s) == -1) {
+ // profiling results doesn't existed
+ profilingNeeded = true;
+ for (size_t i = 0; i < gCodecList->countCodecs(); ++i) {
+ infos.push_back(gCodecList->getCodecInfo(i));
+ }
+ }
+ }
}
}
- return sCodecList;
+ if (profilingNeeded) {
+ profileCodecs(infos, &updates);
+ }
+
+ {
+ Mutex::Autolock autoLock(sInitMutex);
+ if (updates.size() > 0) {
+ gCodecList->updateDetailsForMultipleCodecs(updates);
+ }
+
+ return sCodecList;
+ }
}
static Mutex sRemoteInitMutex;
@@ -94,11 +134,27 @@ sp<IMediaCodecList> MediaCodecList::getInstance() {
}
MediaCodecList::MediaCodecList()
- : mInitCheck(NO_INIT) {
+ : mInitCheck(NO_INIT),
+ mUpdate(false),
+ mGlobalSettings(new AMessage()) {
parseTopLevelXMLFile("/etc/media_codecs.xml");
+ parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
+}
+
+void MediaCodecList::updateDetailsForMultipleCodecs(
+ const KeyedVector<AString, CodecSettings>& updates) {
+ if (updates.size() == 0) {
+ return;
+ }
+
+ exportResultsToXML(kProfilingResults, updates);
+
+ for (size_t i = 0; i < updates.size(); ++i) {
+ applyCodecSettings(updates.keyAt(i), updates.valueAt(i), &mCodecInfos);
+ }
}
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
+void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
// get href_base
char *href_base_end = strrchr(codecs_xml, '/');
if (href_base_end != NULL) {
@@ -119,13 +175,16 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
mOMX.clear();
if (mInitCheck != OK) {
+ if (ignore_errors) {
+ mInitCheck = OK;
+ return;
+ }
mCodecInfos.clear();
return;
}
for (size_t i = mCodecInfos.size(); i-- > 0;) {
const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
if (info.mCaps.size() == 0) {
// No types supported by this component???
ALOGW("Component %s does not support any type of media?",
@@ -169,6 +228,16 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
}
ALOGV(" levels=[%s]", nice.c_str());
}
+ {
+ AString quirks;
+ for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
+ if (ix > 0) {
+ quirks.append(", ");
+ }
+ quirks.append(info.mQuirks[ix]);
+ }
+ ALOGV(" quirks=[%s]", quirks.c_str());
+ }
}
#endif
}
@@ -328,6 +397,16 @@ void MediaCodecList::startElementHandler(
mCurrentSection = SECTION_DECODERS;
} else if (!strcmp(name, "Encoders")) {
mCurrentSection = SECTION_ENCODERS;
+ } else if (!strcmp(name, "Settings")) {
+ mCurrentSection = SECTION_SETTINGS;
+ }
+ break;
+ }
+
+ case SECTION_SETTINGS:
+ {
+ if (!strcmp(name, "Setting")) {
+ mInitCheck = addSettingFromAttributes(attrs);
}
break;
}
@@ -397,6 +476,14 @@ void MediaCodecList::endElementHandler(const char *name) {
}
switch (mCurrentSection) {
+ case SECTION_SETTINGS:
+ {
+ if (!strcmp(name, "Settings")) {
+ mCurrentSection = SECTION_TOPLEVEL;
+ }
+ break;
+ }
+
case SECTION_DECODERS:
{
if (!strcmp(name, "Decoders")) {
@@ -462,10 +549,10 @@ void MediaCodecList::endElementHandler(const char *name) {
--mDepth;
}
-status_t MediaCodecList::addMediaCodecFromAttributes(
- bool encoder, const char **attrs) {
+status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
const char *name = NULL;
- const char *type = NULL;
+ const char *value = NULL;
+ const char *update = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
@@ -475,11 +562,17 @@ status_t MediaCodecList::addMediaCodecFromAttributes(
}
name = attrs[i + 1];
++i;
- } else if (!strcmp(attrs[i], "type")) {
+ } else if (!strcmp(attrs[i], "value")) {
if (attrs[i + 1] == NULL) {
return -EINVAL;
}
- type = attrs[i + 1];
+ value = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
++i;
} else {
return -EINVAL;
@@ -488,10 +581,34 @@ status_t MediaCodecList::addMediaCodecFromAttributes(
++i;
}
- if (name == NULL) {
+ if (name == NULL || value == NULL) {
return -EINVAL;
}
+ mUpdate = (update != NULL) && parseBoolean(update);
+ if (mUpdate != mGlobalSettings->contains(name)) {
+ return -EINVAL;
+ }
+
+ mGlobalSettings->setString(name, value);
+ return OK;
+}
+
+void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ if (AString(name) == mCodecInfos[i]->getCodecName()) {
+ if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
+ ALOGW("Overrides with an unexpected mime %s", type);
+ // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
+ // overrides we don't want.
+ mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+ } else {
+ mCurrentInfo = mCodecInfos.editItemAt(i);
+ mCurrentInfo->updateMime(type); // to set the current cap
+ }
+ return;
+ }
+ }
mCurrentInfo = new MediaCodecInfo(name, encoder, type);
// The next step involves trying to load the codec, which may
// fail. Only list the codec if this succeeds.
@@ -500,6 +617,78 @@ status_t MediaCodecList::addMediaCodecFromAttributes(
if (initializeCapabilities(type) == OK) {
mCodecInfos.push_back(mCurrentInfo);
}
+}
+
+status_t MediaCodecList::addMediaCodecFromAttributes(
+ bool encoder, const char **attrs) {
+ const char *name = NULL;
+ const char *type = NULL;
+ const char *update = NULL;
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (!strcmp(attrs[i], "name")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ name = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "type")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ type = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ mUpdate = (update != NULL) && parseBoolean(update);
+ ssize_t index = -1;
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ if (AString(name) == mCodecInfos[i]->getCodecName()) {
+ index = i;
+ }
+ }
+ if (mUpdate != (index >= 0)) {
+ return -EINVAL;
+ }
+
+ if (index >= 0) {
+ // existing codec
+ mCurrentInfo = mCodecInfos.editItemAt(index);
+ if (type != NULL) {
+ // existing type
+ if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
+ return -EINVAL;
+ }
+ mCurrentInfo->updateMime(type);
+ }
+ } else {
+ // new codec
+ mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+ // The next step involves trying to load the codec, which may
+ // fail. Only list the codec if this succeeds.
+ // However, keep mCurrentInfo object around until parsing
+ // of full codec info is completed.
+ if (initializeCapabilities(type) == OK) {
+ mCodecInfos.push_back(mCurrentInfo);
+ }
+ }
+
return OK;
}
@@ -553,6 +742,7 @@ status_t MediaCodecList::addQuirk(const char **attrs) {
status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
const char *name = NULL;
+ const char *update = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
@@ -562,6 +752,12 @@ status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
}
name = attrs[i + 1];
++i;
+ } else if (!strcmp(attrs[i], "update")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ update = attrs[i + 1];
+ ++i;
} else {
return -EINVAL;
}
@@ -573,14 +769,25 @@ status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
return -EINVAL;
}
- status_t ret = mCurrentInfo->addMime(name);
+ bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
+ if (mUpdate != isExistingType) {
+ return -EINVAL;
+ }
+
+ status_t ret;
+ if (mUpdate) {
+ ret = mCurrentInfo->updateMime(name);
+ } else {
+ ret = mCurrentInfo->addMime(name);
+ }
+
if (ret != OK) {
return ret;
}
// The next step involves trying to load the codec, which may
// fail. Handle this gracefully (by not reporting such mime).
- if (initializeCapabilities(name) != OK) {
+ if (!mUpdate && initializeCapabilities(name) != OK) {
mCurrentInfo->removeMime(name);
}
return OK;
@@ -758,7 +965,8 @@ status_t MediaCodecList::addLimit(const char **attrs) {
return limitFoundMissingAttr(name, "ranges", found);
} else if (msg->contains("scale")) {
return limitFoundMissingAttr(name, "scale");
- } else if ((name == "alignment" || name == "block-size") ^
+ } else if ((name == "alignment" || name == "block-size"
+ || name == "max-supported-instances") ^
(found = msg->findString("value", &value))) {
return limitFoundMissingAttr(name, "value", found);
}
@@ -780,15 +988,6 @@ status_t MediaCodecList::addLimit(const char **attrs) {
return OK;
}
-static bool parseBoolean(const char *s) {
- if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
- return true;
- }
- char *end;
- unsigned long res = strtoul(s, &end, 10);
- return *s != '\0' && *end == '\0' && res > 0;
-}
-
status_t MediaCodecList::addFeature(const char **attrs) {
size_t i = 0;
const char *name = NULL;
@@ -860,4 +1059,8 @@ size_t MediaCodecList::countCodecs() const {
return mCodecInfos.size();
}
+const sp<AMessage> MediaCodecList::getGlobalSettings() const {
+ return mGlobalSettings;
+}
+
} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
new file mode 100644
index 0000000..3c54f34
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2015 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 "MediaCodecListOverrides"
+#include <utils/Log.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <media/IMediaCodecList.h>
+#include <media/MediaCodecInfo.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+
+namespace android {
+
+// a limit to avoid allocating unreasonable number of codec instances in the measurement.
+// this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java.
+static const int kMaxInstances = 32;
+
+// TODO: move MediaCodecInfo to C++. Until then, some temp methods to parse out info.
+static bool getMeasureSize(sp<MediaCodecInfo::Capabilities> caps, int32_t *width, int32_t *height) {
+ AString sizeRange;
+ if (!caps->getDetails()->findString("size-range", &sizeRange)) {
+ return false;
+ }
+ AString minSize;
+ AString maxSize;
+ if (!splitString(sizeRange, "-", &minSize, &maxSize)) {
+ return false;
+ }
+ AString sWidth;
+ AString sHeight;
+ if (!splitString(minSize, "x", &sWidth, &sHeight)) {
+ if (!splitString(minSize, "*", &sWidth, &sHeight)) {
+ return false;
+ }
+ }
+
+ *width = strtol(sWidth.c_str(), NULL, 10);
+ *height = strtol(sHeight.c_str(), NULL, 10);
+ return (*width > 0) && (*height > 0);
+}
+
+static void getMeasureBitrate(sp<MediaCodecInfo::Capabilities> caps, int32_t *bitrate) {
+ // Until have native MediaCodecInfo, we cannot get bitrates based on profile/levels.
+ // We use 200000 as default value for our measurement.
+ *bitrate = 200000;
+ AString bitrateRange;
+ if (!caps->getDetails()->findString("bitrate-range", &bitrateRange)) {
+ return;
+ }
+ AString minBitrate;
+ AString maxBitrate;
+ if (!splitString(bitrateRange, "-", &minBitrate, &maxBitrate)) {
+ return;
+ }
+
+ *bitrate = strtol(minBitrate.c_str(), NULL, 10);
+}
+
+static sp<AMessage> getMeasureFormat(
+ bool isEncoder, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+ sp<AMessage> format = new AMessage();
+ format->setString("mime", mime);
+
+ if (isEncoder) {
+ int32_t bitrate = 0;
+ getMeasureBitrate(caps, &bitrate);
+ format->setInt32("bitrate", bitrate);
+ }
+
+ if (mime.startsWith("video/")) {
+ int32_t width = 0;
+ int32_t height = 0;
+ if (!getMeasureSize(caps, &width, &height)) {
+ return NULL;
+ }
+ format->setInt32("width", width);
+ format->setInt32("height", height);
+
+ Vector<uint32_t> colorFormats;
+ caps->getSupportedColorFormats(&colorFormats);
+ if (colorFormats.size() == 0) {
+ return NULL;
+ }
+ format->setInt32("color-format", colorFormats[0]);
+
+ format->setFloat("frame-rate", 10.0);
+ format->setInt32("i-frame-interval", 10);
+ } else {
+ // TODO: profile hw audio
+ return NULL;
+ }
+
+ return format;
+}
+
+static size_t doProfileCodecs(
+ bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+ sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps);
+ if (format == NULL) {
+ return 0;
+ }
+ if (isEncoder) {
+ format->setInt32("encoder", 1);
+ }
+ ALOGV("doProfileCodecs %s %s %s %s",
+ name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder",
+ format->debugString().c_str());
+
+ status_t err = OK;
+ Vector<sp<MediaCodec>> codecs;
+ while (err == OK && codecs.size() < kMaxInstances) {
+ sp<ALooper> looper = new ALooper;
+ looper->setName("MediaCodec_looper");
+ ALOGV("doProfileCodecs for codec #%u", codecs.size());
+ ALOGV("doProfileCodecs start looper");
+ looper->start(
+ false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO);
+ ALOGV("doProfileCodecs CreateByComponentName");
+ sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err);
+ if (err != OK) {
+ ALOGV("Failed to create codec: %s", name.c_str());
+ break;
+ }
+ const sp<Surface> nativeWindow;
+ const sp<ICrypto> crypto;
+ uint32_t flags = 0;
+ ALOGV("doProfileCodecs configure");
+ err = codec->configure(format, nativeWindow, crypto, flags);
+ if (err != OK) {
+ ALOGV("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ break;
+ }
+ ALOGV("doProfileCodecs start");
+ err = codec->start();
+ if (err != OK) {
+ ALOGV("Failed to start codec: %s with mime: %s", name.c_str(), mime.c_str());
+ codec->release();
+ break;
+ }
+ codecs.push_back(codec);
+ }
+
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ ALOGV("doProfileCodecs release %s", name.c_str());
+ err = codecs[i]->release();
+ if (err != OK) {
+ ALOGE("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str());
+ }
+ }
+
+ return codecs.size();
+}
+
+static void printLongString(const char *buf, size_t size) {
+ AString print;
+ const char *start = buf;
+ size_t len;
+ size_t totalLen = size;
+ while (totalLen > 0) {
+ len = (totalLen > 500) ? 500 : totalLen;
+ print.setTo(start, len);
+ ALOGV("%s", print.c_str());
+ totalLen -= len;
+ start += len;
+ }
+}
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) {
+ ssize_t pos = s.find(delimiter.c_str());
+ if (pos < 0) {
+ return false;
+ }
+ *s1 = AString(s, 0, pos);
+ *s2 = AString(s, pos + 1, s.size() - pos - 1);
+ return true;
+}
+
+bool splitString(
+ const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3) {
+ AString temp;
+ if (!splitString(s, delimiter, s1, &temp)) {
+ return false;
+ }
+ if (!splitString(temp, delimiter, s2, s3)) {
+ return false;
+ }
+ return true;
+}
+
+void profileCodecs(
+ const Vector<sp<MediaCodecInfo>> &infos,
+ KeyedVector<AString, CodecSettings> *results,
+ bool forceToMeasure) {
+ KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
+ for (size_t i = 0; i < infos.size(); ++i) {
+ const sp<MediaCodecInfo> info = infos[i];
+ AString name = info->getCodecName();
+ if (name.startsWith("OMX.google.") ||
+ // TODO: reenable below codecs once fixed
+ name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
+ continue;
+ }
+
+ Vector<AString> mimes;
+ info->getSupportedMimes(&mimes);
+ for (size_t i = 0; i < mimes.size(); ++i) {
+ const sp<MediaCodecInfo::Capabilities> &caps =
+ info->getCapabilitiesFor(mimes[i].c_str());
+ if (!forceToMeasure && caps->getDetails()->contains("max-supported-instances")) {
+ continue;
+ }
+
+ size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps);
+ if (max > 0) {
+ CodecSettings settings;
+ char maxStr[32];
+ sprintf(maxStr, "%u", max);
+ settings.add("max-supported-instances", maxStr);
+
+ AString key = name;
+ key.append(" ");
+ key.append(mimes[i]);
+ key.append(" ");
+ key.append(info->isEncoder() ? "encoder" : "decoder");
+ results->add(key, settings);
+ }
+ }
+ }
+}
+
+void applyCodecSettings(
+ const AString& codecInfo,
+ const CodecSettings &settings,
+ Vector<sp<MediaCodecInfo>> *infos) {
+ AString name;
+ AString mime;
+ AString type;
+ if (!splitString(codecInfo, " ", &name, &mime, &type)) {
+ return;
+ }
+
+ for (size_t i = 0; i < infos->size(); ++i) {
+ const sp<MediaCodecInfo> &info = infos->itemAt(i);
+ if (name != info->getCodecName()) {
+ continue;
+ }
+
+ Vector<AString> mimes;
+ info->getSupportedMimes(&mimes);
+ for (size_t j = 0; j < mimes.size(); ++j) {
+ if (mimes[j] != mime) {
+ continue;
+ }
+ const sp<MediaCodecInfo::Capabilities> &caps = info->getCapabilitiesFor(mime.c_str());
+ for (size_t k = 0; k < settings.size(); ++k) {
+ caps->getDetails()->setString(
+ settings.keyAt(k).c_str(), settings.valueAt(k).c_str());
+ }
+ }
+ }
+}
+
+void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results) {
+#if LOG_NDEBUG == 0
+ ALOGE("measurement results");
+ for (size_t i = 0; i < results.size(); ++i) {
+ ALOGE("key %s", results.keyAt(i).c_str());
+ const CodecSettings &settings = results.valueAt(i);
+ for (size_t j = 0; j < settings.size(); ++j) {
+ ALOGE("name %s value %s", settings.keyAt(j).c_str(), settings.valueAt(j).c_str());
+ }
+ }
+#endif
+
+ AString overrides;
+ FILE *f = fopen(fileName, "rb");
+ if (f != NULL) {
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ rewind(f);
+
+ char *buf = (char *)malloc(size);
+ if (fread(buf, size, 1, f) == 1) {
+ overrides.setTo(buf, size);
+#if LOG_NDEBUG == 0
+ ALOGV("Existing overrides:");
+ printLongString(buf, size);
+#endif
+ } else {
+ ALOGE("Failed to read %s", fileName);
+ }
+ fclose(f);
+ free(buf);
+ }
+
+ for (size_t i = 0; i < results.size(); ++i) {
+ AString name;
+ AString mime;
+ AString type;
+ if (!splitString(results.keyAt(i), " ", &name, &mime, &type)) {
+ continue;
+ }
+ name = AStringPrintf("\"%s\"", name.c_str());
+ mime = AStringPrintf("\"%s\"", mime.c_str());
+ ALOGV("name(%s) mime(%s) type(%s)", name.c_str(), mime.c_str(), type.c_str());
+ ssize_t posCodec = overrides.find(name.c_str());
+ size_t posInsert = 0;
+ if (posCodec < 0) {
+ AString encodersDecoders = (type == "encoder") ? "<Encoders>" : "<Decoders>";
+ AString encodersDecodersEnd = (type == "encoder") ? "</Encoders>" : "</Decoders>";
+ ssize_t posEncodersDecoders = overrides.find(encodersDecoders.c_str());
+ if (posEncodersDecoders < 0) {
+ AString mediaCodecs = "<MediaCodecs>";
+ ssize_t posMediaCodec = overrides.find(mediaCodecs.c_str());
+ if (posMediaCodec < 0) {
+ posMediaCodec = overrides.size();
+ overrides.insert("\n<MediaCodecs>\n</MediaCodecs>\n", posMediaCodec);
+ posMediaCodec = overrides.find(mediaCodecs.c_str(), posMediaCodec);
+ }
+ posEncodersDecoders = posMediaCodec + mediaCodecs.size();
+ AString codecs = AStringPrintf(
+ "\n %s\n %s", encodersDecoders.c_str(), encodersDecodersEnd.c_str());
+ overrides.insert(codecs.c_str(), posEncodersDecoders);
+ posEncodersDecoders = overrides.find(encodersDecoders.c_str(), posEncodersDecoders);
+ }
+ posCodec = posEncodersDecoders + encodersDecoders.size();
+ AString codec = AStringPrintf(
+ "\n <MediaCodec name=%s type=%s update=\"true\" >\n </MediaCodec>",
+ name.c_str(),
+ mime.c_str());
+ overrides.insert(codec.c_str(), posCodec);
+ posCodec = overrides.find(name.c_str());
+ }
+
+ // insert to existing entry
+ ssize_t posMime = overrides.find(mime.c_str(), posCodec);
+ ssize_t posEnd = overrides.find(">", posCodec);
+ if (posEnd < 0) {
+ ALOGE("Format error in overrides file.");
+ return;
+ }
+ if (posMime < 0 || posMime > posEnd) {
+ // new mime for an existing component
+ AString codecEnd = "</MediaCodec>";
+ posInsert = overrides.find(codecEnd.c_str(), posCodec) + codecEnd.size();
+ AString codec = AStringPrintf(
+ "\n <MediaCodec name=%s type=%s update=\"true\" >\n </MediaCodec>",
+ name.c_str(),
+ mime.c_str());
+ overrides.insert(codec.c_str(), posInsert);
+ posInsert = overrides.find(">", posInsert) + 1;
+ } else {
+ posInsert = posEnd + 1;
+ }
+
+ CodecSettings settings = results.valueAt(i);
+ for (size_t i = 0; i < settings.size(); ++i) {
+ // WARNING: we assume all the settings are "Limit". Currently we have only one type
+ // of setting in this case, which is "max-supported-instances".
+ AString strInsert = AStringPrintf(
+ "\n <Limit name=\"%s\" value=\"%s\" />",
+ settings.keyAt(i).c_str(),
+ settings.valueAt(i).c_str());
+ overrides.insert(strInsert, posInsert);
+ }
+ }
+
+#if LOG_NDEBUG == 0
+ ALOGV("New overrides:");
+ printLongString(overrides.c_str(), overrides.size());
+#endif
+
+ f = fopen(fileName, "wb");
+ if (f == NULL) {
+ ALOGE("Failed to open %s for writing.", fileName);
+ return;
+ }
+ if (fwrite(overrides.c_str(), 1, overrides.size(), f) != overrides.size()) {
+ ALOGE("Failed to write to %s.", fileName);
+ }
+ fclose(f);
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
new file mode 100644
index 0000000..f97ce63
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 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 MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#define MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class MediaCodecInfo;
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
+
+bool splitString(
+ const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3);
+
+void profileCodecs(
+ const Vector<sp<MediaCodecInfo>> &infos,
+ KeyedVector<AString, CodecSettings> *results,
+ bool forceToMeasure = false); // forceToMeasure is mainly for testing
+
+void applyCodecSettings(
+ const AString& codecInfo,
+ const CodecSettings &settings,
+ Vector<sp<MediaCodecInfo>> *infos);
+
+void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results);
+
+} // namespace android
+
+#endif // MEDIA_CODEC_LIST_OVERRIDES_H_
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index b6fa810..6568d25 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -399,6 +399,9 @@ status_t MediaCodecSource::initEncoder() {
ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+ mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+ mEncoder->setCallback(mEncoderActivityNotify);
+
status_t err = mEncoder->configure(
mOutputFormat,
NULL /* nativeWindow */,
@@ -422,9 +425,6 @@ status_t MediaCodecSource::initEncoder() {
}
}
- mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
- mEncoder->setCallback(mEncoderActivityNotify);
-
err = mEncoder->start();
if (err != OK) {
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index bdd6d56..aba64d5 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -230,8 +230,13 @@ status_t SampleTable::setSampleToChunkParams(
return ERROR_MALFORMED;
}
+ if (SIZE_MAX / sizeof(SampleToChunkEntry) <= mNumSampleToChunkOffsets)
+ return ERROR_OUT_OF_RANGE;
+
mSampleToChunkEntries =
- new SampleToChunkEntry[mNumSampleToChunkOffsets];
+ new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
+ if (!mSampleToChunkEntries)
+ return ERROR_OUT_OF_RANGE;
for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
uint8_t buffer[12];
@@ -330,11 +335,13 @@ status_t SampleTable::setTimeToSampleParams(
}
mTimeToSampleCount = U32_AT(&header[4]);
- uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
+ uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+ mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2];
+ if (!mTimeToSample)
+ return ERROR_OUT_OF_RANGE;
size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
if (mDataSource->readAt(
@@ -376,12 +383,14 @@ status_t SampleTable::setCompositionTimeToSampleParams(
}
mNumCompositionTimeDeltaEntries = numEntries;
- uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
+ uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
+ mCompositionTimeDeltaEntries = new (std::nothrow) uint32_t[2 * numEntries];
+ if (!mCompositionTimeDeltaEntries)
+ return ERROR_OUT_OF_RANGE;
if (mDataSource->readAt(
data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8)
@@ -426,12 +435,15 @@ status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size)
ALOGV("Table of sync samples is empty or has only a single entry!");
}
- uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t);
+ uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}
- mSyncSamples = new uint32_t[mNumSyncSamples];
+ mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples];
+ if (!mSyncSamples)
+ return ERROR_OUT_OF_RANGE;
+
size_t size = mNumSyncSamples * sizeof(uint32_t);
if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
!= (ssize_t)size) {
@@ -499,7 +511,9 @@ void SampleTable::buildSampleEntriesTable() {
return;
}
- mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+ mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes];
+ if (!mSampleTimeEntries)
+ return;
uint32_t sampleIndex = 0;
uint32_t sampleTime = 0;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 6e6a78a..a35909e 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -139,7 +139,7 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por
uint32_t height = mImg->d_h;
outInfo = *outQueue.begin();
outHeader = outInfo->mHeader;
- CHECK_EQ(mImg->fmt, IMG_FMT_I420);
+ CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420);
handlePortSettingsChange(portWillReset, width, height);
if (*portWillReset) {
return true;
@@ -151,12 +151,12 @@ bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *por
outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv;
uint8_t *dst = outHeader->pBuffer;
- const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y];
- const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U];
- const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V];
- size_t srcYStride = mImg->stride[PLANE_Y];
- size_t srcUStride = mImg->stride[PLANE_U];
- size_t srcVStride = mImg->stride[PLANE_V];
+ const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
+ const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
+ const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V];
+ size_t srcYStride = mImg->stride[VPX_PLANE_Y];
+ size_t srcUStride = mImg->stride[VPX_PLANE_U];
+ size_t srcVStride = mImg->stride[VPX_PLANE_V];
copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
mImg = NULL;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 2d93152..74f58e9 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -141,6 +141,21 @@ const char *LiveSession::getKeyForStream(StreamType type) {
return NULL;
}
+//static
+const char *LiveSession::getNameForStream(StreamType type) {
+ switch (type) {
+ case STREAMTYPE_VIDEO:
+ return "video";
+ case STREAMTYPE_AUDIO:
+ return "audio";
+ case STREAMTYPE_SUBTITLES:
+ return "subs";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
LiveSession::LiveSession(
const sp<AMessage> &notify, uint32_t flags,
const sp<IMediaHTTPService> &httpService)
@@ -192,7 +207,11 @@ status_t LiveSession::dequeueAccessUnit(
status_t finalResult = OK;
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
- ssize_t idx = typeToIndex(stream);
+ ssize_t streamIdx = typeToIndex(stream);
+ if (streamIdx < 0) {
+ return INVALID_VALUE;
+ }
+ const char *streamStr = getNameForStream(stream);
// Do not let client pull data if we don't have data packets yet.
// We might only have a format discontinuity queued without data.
// When NuPlayerDecoder dequeues the format discontinuity, it will
@@ -200,6 +219,9 @@ status_t LiveSession::dequeueAccessUnit(
// thinks it can do seamless change, so will not shutdown decoder.
// When the actual format arrives, it can't handle it and get stuck.
if (!packetSource->hasDataBufferAvailable(&finalResult)) {
+ ALOGV("[%s] dequeueAccessUnit: no buffer available (finalResult=%d)",
+ streamStr, finalResult);
+
if (finalResult == OK) {
return -EAGAIN;
} else {
@@ -212,25 +234,6 @@ status_t LiveSession::dequeueAccessUnit(
status_t err = packetSource->dequeueAccessUnit(accessUnit);
- size_t streamIdx;
- const char *streamStr;
- switch (stream) {
- case STREAMTYPE_AUDIO:
- streamIdx = kAudioIndex;
- streamStr = "audio";
- break;
- case STREAMTYPE_VIDEO:
- streamIdx = kVideoIndex;
- streamStr = "video";
- break;
- case STREAMTYPE_SUBTITLES:
- streamIdx = kSubtitleIndex;
- streamStr = "subs";
- break;
- default:
- TRESPASS();
- }
-
StreamItem& strm = mStreams[streamIdx];
if (err == INFO_DISCONTINUITY) {
// adaptive streaming, discontinuities in the playlist
@@ -249,9 +252,10 @@ status_t LiveSession::dequeueAccessUnit(
} else if (err == OK) {
if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
- int64_t timeUs;
+ int64_t timeUs, originalTimeUs;
int32_t discontinuitySeq = 0;
CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs));
+ originalTimeUs = timeUs;
(*accessUnit)->meta()->findInt32("discontinuitySeq", &discontinuitySeq);
if (discontinuitySeq > (int32_t) strm.mCurDiscontinuitySeq) {
int64_t offsetTimeUs;
@@ -303,7 +307,8 @@ status_t LiveSession::dequeueAccessUnit(
timeUs += mDiscontinuityOffsetTimesUs.valueFor(discontinuitySeq);
}
- ALOGV("[%s] read buffer at time %" PRId64 " us", streamStr, timeUs);
+ ALOGV("[%s] dequeueAccessUnit: time %lld us, original %lld us",
+ streamStr, (long long)timeUs, (long long)originalTimeUs);
(*accessUnit)->meta()->setInt64("timeUs", timeUs);
mLastDequeuedTimeUs = timeUs;
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
@@ -409,7 +414,7 @@ bool LiveSession::checkSwitchProgress(
if (lastDequeueMeta == NULL) {
// this means we don't have enough cushion, try again later
ALOGV("[%s] up switching failed due to insufficient buffer",
- stream == STREAMTYPE_AUDIO ? "audio" : "video");
+ getNameForStream(stream));
return false;
}
} else {
@@ -428,7 +433,7 @@ bool LiveSession::checkSwitchProgress(
if (firstNewMeta[i] == NULL) {
HLSTime dequeueTime(lastDequeueMeta);
ALOGV("[%s] dequeue time (%d, %lld) past start time",
- stream == STREAMTYPE_AUDIO ? "audio" : "video",
+ getNameForStream(stream),
dequeueTime.mSeq, (long long) dequeueTime.mTimeUs);
return false;
}
@@ -493,16 +498,15 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
case kWhatSeek:
{
- sp<AReplyToken> seekReplyID;
- CHECK(msg->senderAwaitsResponse(&seekReplyID));
- mSeekReplyID = seekReplyID;
- mSeekReply = new AMessage;
-
- status_t err = onSeek(msg);
-
- if (err != OK) {
+ if (mReconfigurationInProgress) {
msg->post(50000);
+ break;
}
+
+ CHECK(msg->senderAwaitsResponse(&mSeekReplyID));
+ mSeekReply = new AMessage;
+
+ onSeek(msg);
break;
}
@@ -525,6 +529,11 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ ALOGV("fetcher-%d %s",
+ mFetcherInfos[index].mFetcher->getFetcherID(),
+ what == PlaylistFetcher::kWhatPaused ?
+ "paused" : "stopped");
+
if (what == PlaylistFetcher::kWhatStopped) {
mFetcherLooper->unregisterHandler(
mFetcherInfos[index].mFetcher->id());
@@ -544,6 +553,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
if (--mContinuationCounter == 0) {
mContinuation->post();
}
+ ALOGV("%zu fetcher(s) left", mContinuationCounter);
}
break;
}
@@ -636,6 +646,9 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
int32_t switchGeneration;
CHECK(msg->findInt32("switchGeneration", &switchGeneration));
+ ALOGV("kWhatStartedAt: switchGen=%d, mSwitchGen=%d",
+ switchGeneration, mSwitchGeneration);
+
if (switchGeneration != mSwitchGeneration) {
break;
}
@@ -667,6 +680,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
if (checkSwitchProgress(stopParams, delayUs, &needResumeUntil)) {
// playback time hasn't passed startAt time
if (!needResumeUntil) {
+ ALOGV("finish switch");
for (size_t i = 0; i < kMaxStreams; ++i) {
if ((mSwapMask & indexToType(i))
&& uri == mStreams[i].mNewUri) {
@@ -682,6 +696,7 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
// Resume fetcher for the original variant; the resumed fetcher should
// continue until the timestamps found in msg, which is stored by the
// new fetcher to indicate where the new variant has started buffering.
+ ALOGV("finish switch with resumeUntilAsync");
for (size_t i = 0; i < mFetcherInfos.size(); i++) {
const FetcherInfo &info = mFetcherInfos.valueAt(i);
if (info.mToBeRemoved) {
@@ -693,8 +708,10 @@ void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
// playback time passed startAt time
if (switchUp) {
// if switching up, cancel and retry if condition satisfies again
+ ALOGV("cancel up switch because we're too late");
cancelBandwidthSwitch(true /* resume */);
} else {
+ ALOGV("retry down switch at next sample");
resumeFetcher(uri, mSwapMask, -1, true /* newUri */);
}
}
@@ -933,7 +950,8 @@ sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {
notify->setInt32("switchGeneration", mSwitchGeneration);
FetcherInfo info;
- info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
+ info.mFetcher = new PlaylistFetcher(
+ notify, this, uri, mCurBandwidthIndex, mSubtitleGeneration);
info.mDurationUs = -1ll;
info.mToBeRemoved = false;
info.mToBeResumed = false;
@@ -1167,9 +1185,13 @@ bool LiveSession::resumeFetcher(
}
if (resume) {
- ALOGV("resuming fetcher %s, timeUs %lld", uri.c_str(), (long long)timeUs);
+ sp<PlaylistFetcher> &fetcher = mFetcherInfos.editValueAt(index).mFetcher;
SeekMode seekMode = newUri ? kSeekModeNextSample : kSeekModeExactPosition;
- mFetcherInfos.editValueAt(index).mFetcher->startAsync(
+
+ ALOGV("resuming fetcher-%d, timeUs=%lld, seekMode=%d",
+ fetcher->getFetcherID(), (long long)timeUs, seekMode);
+
+ fetcher->startAsync(
sources[kAudioIndex],
sources[kVideoIndex],
sources[kSubtitleIndex],
@@ -1349,16 +1371,10 @@ HLSTime LiveSession::latestMediaSegmentStartTime() const {
return audioTime < videoTime ? videoTime : audioTime;
}
-status_t LiveSession::onSeek(const sp<AMessage> &msg) {
+void LiveSession::onSeek(const sp<AMessage> &msg) {
int64_t timeUs;
CHECK(msg->findInt64("timeUs", &timeUs));
-
- if (!mReconfigurationInProgress) {
- changeConfiguration(timeUs);
- return OK;
- } else {
- return -EWOULDBLOCK;
- }
+ changeConfiguration(timeUs);
}
status_t LiveSession::getDuration(int64_t *durationUs) const {
@@ -1406,6 +1422,9 @@ status_t LiveSession::selectTrack(size_t index, bool select) {
return INVALID_OPERATION;
}
+ ALOGV("selectTrack: index=%zu, select=%d, mSubtitleGen=%d++",
+ index, select, mSubtitleGeneration);
+
++mSubtitleGeneration;
status_t err = mPlaylist->selectTrack(index, select);
if (err == OK) {
@@ -1426,6 +1445,9 @@ ssize_t LiveSession::getSelectedTrack(media_track_type type) const {
void LiveSession::changeConfiguration(
int64_t timeUs, ssize_t bandwidthIndex, bool pickTrack) {
+ ALOGV("changeConfiguration: timeUs=%lld us, bwIndex=%zd, pickTrack=%d",
+ (long long)timeUs, bandwidthIndex, pickTrack);
+
cancelBandwidthSwitch();
CHECK(!mReconfigurationInProgress);
@@ -1433,6 +1455,10 @@ void LiveSession::changeConfiguration(
if (bandwidthIndex >= 0) {
mOrigBandwidthIndex = mCurBandwidthIndex;
mCurBandwidthIndex = bandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ }
}
CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size());
const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex);
@@ -1478,6 +1504,7 @@ void LiveSession::changeConfiguration(
}
if (discardFetcher) {
+ ALOGV("discarding fetcher-%d", fetcher->getFetcherID());
fetcher->stopAsync();
} else {
float threshold = -1.0f; // always finish fetching by default
@@ -1490,8 +1517,8 @@ void LiveSession::changeConfiguration(
mOrigBandwidthIndex, mCurBandwidthIndex);
}
- ALOGV("Pausing with threshold %.3f", threshold);
-
+ ALOGV("pausing fetcher-%d, threshold=%.2f",
+ fetcher->getFetcherID(), threshold);
fetcher->pauseAsync(threshold);
}
}
@@ -1526,6 +1553,8 @@ void LiveSession::changeConfiguration(
}
void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
+ ALOGV("onChangeConfiguration");
+
if (!mReconfigurationInProgress) {
int32_t pickTrack = 0;
msg->findInt32("pickTrack", &pickTrack);
@@ -1536,6 +1565,8 @@ void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
}
void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
+ ALOGV("onChangeConfiguration2");
+
mContinuation.clear();
// All fetchers are either suspended or have been removed now.
@@ -1547,6 +1578,7 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
if (timeUs >= 0) {
mLastSeekTimeUs = timeUs;
+ mLastDequeuedTimeUs = timeUs;
for (size_t i = 0; i < mPacketSources.size(); i++) {
mPacketSources.editValueAt(i)->clear();
@@ -1599,8 +1631,10 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
mStreams[i].mUri.c_str(), URIs[i].c_str());
sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
- source->queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ if (source->getLatestDequeuedMeta() != NULL) {
+ source->queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ }
}
// Determine which decoders to shutdown on the player side,
// a decoder has to be shutdown if its streamtype was active
@@ -1660,16 +1694,17 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
// and resume audio.
mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask;
switching = (mSwapMask != 0);
- if (!switching) {
- ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd",
- mOrigBandwidthIndex, mCurBandwidthIndex);
- }
}
mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs;
} else {
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
}
+ ALOGV("onChangeConfiguration3: timeUs=%lld, switching=%d, pickTrack=%d, "
+ "mStreamMask=0x%x, mNewStreamMask=0x%x, mSwapMask=0x%x",
+ (long long)timeUs, switching, pickTrack,
+ mStreamMask, mNewStreamMask, mSwapMask);
+
for (size_t i = 0; i < kMaxStreams; ++i) {
if (streamMask & indexToType(i)) {
if (switching) {
@@ -1687,6 +1722,9 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
const AString &uri = mFetcherInfos.keyAt(i);
if (!resumeFetcher(uri, resumeMask, timeUs)) {
+ ALOGV("marking fetcher-%d to be removed",
+ mFetcherInfos[i].mFetcher->getFetcherID());
+
mFetcherInfos.editValueAt(i).mToBeRemoved = true;
}
}
@@ -1776,6 +1814,14 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
}
}
+ ALOGV("[fetcher-%d] startAsync: startTimeUs %lld mLastSeekTimeUs %lld "
+ "segmentStartTimeUs %lld seekMode %d",
+ fetcher->getFetcherID(),
+ (long long)startTime.mTimeUs,
+ (long long)mLastSeekTimeUs,
+ (long long)startTime.getSegmentTimeUs(true /* midpoint */),
+ seekMode);
+
// Set the target segment start time to the middle point of the
// segment where the last sample was.
// This gives a better guess if segments of the two variants are not
@@ -1795,22 +1841,28 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) {
// All fetchers have now been started, the configuration change
// has completed.
- ALOGV("XXX configuration change completed.");
mReconfigurationInProgress = false;
if (switching) {
mSwitchInProgress = true;
} else {
mStreamMask = mNewStreamMask;
- mOrigBandwidthIndex = mCurBandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ mOrigBandwidthIndex = mCurBandwidthIndex;
+ }
}
+ ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x",
+ mSwitchInProgress, mStreamMask);
+
if (mDisconnectReplyID != NULL) {
finishDisconnect();
}
}
void LiveSession::swapPacketSource(StreamType stream) {
- ALOGV("swapPacketSource: stream = %d", stream);
+ ALOGV("[%s] swapPacketSource", getNameForStream(stream));
// transfer packets from source2 to source
sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
@@ -1858,7 +1910,7 @@ void LiveSession::tryToFinishBandwidthSwitch(const AString &oldUri) {
mFetcherInfos.editValueAt(index).mFetcher->stopAsync(false /* clear */);
- ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask);
+ ALOGV("tryToFinishBandwidthSwitch: mSwapMask=0x%x", mSwapMask);
if (mSwapMask != 0) {
return;
}
@@ -1925,11 +1977,19 @@ void LiveSession::onPollBuffering() {
bool underflow, ready, down, up;
if (checkBuffering(underflow, ready, down, up)) {
- if (mInPreparationPhase && ready) {
- postPrepared(OK);
+ if (mInPreparationPhase) {
+ // Allow down switch even if we're still preparing.
+ //
+ // Some streams have a high bandwidth index as default,
+ // when bandwidth is low, it takes a long time to buffer
+ // to ready mark, then it immediately pauses after start
+ // as we have to do a down switch. It's better experience
+ // to restart from a lower index, if we detect low bw.
+ if (!switchBandwidthIfNeeded(false /* up */, down) && ready) {
+ postPrepared(OK);
+ }
}
- // don't switch before we report prepared
if (!mInPreparationPhase) {
if (ready) {
stopBufferingIfNecessary();
@@ -1937,8 +1997,7 @@ void LiveSession::onPollBuffering() {
startBufferingIfNecessary();
}
switchBandwidthIfNeeded(up, down);
- }
-
+ }
}
schedulePollBuffering();
@@ -1983,7 +2042,7 @@ void LiveSession::cancelBandwidthSwitch(bool resume) {
}
ALOGI("#### Canceled Bandwidth Switch: %zd => %zd",
- mCurBandwidthIndex, mOrigBandwidthIndex);
+ mOrigBandwidthIndex, mCurBandwidthIndex);
mSwitchGeneration++;
mSwitchInProgress = false;
@@ -2022,13 +2081,16 @@ bool LiveSession::checkBuffering(
int64_t bufferedDurationUs =
mPacketSources[i]->getEstimatedDurationUs();
- ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
+ ALOGV("[%s] buffered %lld us",
+ getNameForStream(mPacketSources.keyAt(i)),
+ (long long)bufferedDurationUs);
if (durationUs >= 0) {
int32_t percent;
if (mPacketSources[i]->isFinished(0 /* duration */)) {
percent = 100;
} else {
- percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
+ percent = (int32_t)(100.0 *
+ (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
}
if (minBufferPercent < 0 || percent < minBufferPercent) {
minBufferPercent = percent;
@@ -2111,10 +2173,14 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) {
notify->post();
}
-void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+/*
+ * returns true if a bandwidth switch is actually needed (and started),
+ * returns false otherwise
+ */
+bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// no need to check bandwidth if we only have 1 bandwidth settings
if (mSwitchInProgress || mBandwidthItems.size() < 2) {
- return;
+ return false;
}
int32_t bandwidthBps;
@@ -2123,7 +2189,7 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
mLastBandwidthBps = bandwidthBps;
} else {
ALOGV("no bandwidth estimate.");
- return;
+ return false;
}
int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
@@ -2142,16 +2208,16 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70%
// of measured bw. In that case we don't want to do anything, since we have
// both enough buffer and enough bw.
- if (bandwidthIndex == mCurBandwidthIndex
- || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex)
- || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) {
- return;
+ if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+ || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) {
+ // if not yet prepared, just restart again with new bw index.
+ // this is faster and playback experience is cleaner.
+ changeConfiguration(
+ mInPreparationPhase ? 0 : -1ll, bandwidthIndex);
+ return true;
}
-
- ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
- mCurBandwidthIndex, bandwidthIndex);
- changeConfiguration(-1, bandwidthIndex, false);
}
+ return false;
}
void LiveSession::postError(status_t err) {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index b5e31c9..9117bb1 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -91,6 +91,7 @@ struct LiveSession : public AHandler {
bool hasDynamicDuration() const;
static const char *getKeyForStream(StreamType type);
+ static const char *getNameForStream(StreamType type);
enum {
kWhatStreamsChanged,
@@ -236,7 +237,7 @@ private:
sp<PlaylistFetcher> addFetcher(const char *uri);
void onConnect(const sp<AMessage> &msg);
- status_t onSeek(const sp<AMessage> &msg);
+ void onSeek(const sp<AMessage> &msg);
void onFinishDisconnect2();
// If given a non-zero block_size (default 0), it is used to cap the number of
@@ -291,7 +292,7 @@ private:
bool checkSwitchProgress(
sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil);
- void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
+ bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
void schedulePollBuffering();
void cancelPollBuffering();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 368612d..ce79cc2 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -45,6 +45,10 @@
#include <openssl/aes.h>
#include <openssl/md5.h>
+#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
+#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
+ LiveSession::getNameForStream(stream), ##__VA_ARGS__)
+
namespace android {
// static
@@ -143,10 +147,12 @@ PlaylistFetcher::PlaylistFetcher(
const sp<AMessage> &notify,
const sp<LiveSession> &session,
const char *uri,
+ int32_t id,
int32_t subtitleGeneration)
: mNotify(notify),
mSession(session),
mURI(uri),
+ mFetcherID(id),
mStreamTypeMask(0),
mStartTimeUs(-1ll),
mSegmentStartTimeUs(-1ll),
@@ -176,6 +182,10 @@ PlaylistFetcher::PlaylistFetcher(
PlaylistFetcher::~PlaylistFetcher() {
}
+int32_t PlaylistFetcher::getFetcherID() const {
+ return mFetcherID;
+}
+
int64_t PlaylistFetcher::getSegmentStartTimeUs(int32_t seqNumber) const {
CHECK(mPlaylist != NULL);
@@ -436,7 +446,7 @@ void PlaylistFetcher::postMonitorQueue(int64_t delayUs, int64_t minDelayUs) {
maxDelayUs = minDelayUs;
}
if (delayUs > maxDelayUs) {
- ALOGV("Need to refresh playlist in %" PRId64 , maxDelayUs);
+ FLOGV("Need to refresh playlist in %lld", (long long)maxDelayUs);
delayUs = maxDelayUs;
}
sp<AMessage> msg = new AMessage(kWhatMonitorQueue, this);
@@ -507,6 +517,8 @@ void PlaylistFetcher::stopAsync(bool clear) {
}
void PlaylistFetcher::resumeUntilAsync(const sp<AMessage> &params) {
+ FLOGV("resumeUntilAsync: params=%s", params->debugString().c_str());
+
AMessage* msg = new AMessage(kWhatResumeUntil, this);
msg->setMessage("params", params);
msg->post();
@@ -763,8 +775,9 @@ void PlaylistFetcher::onMonitorQueue() {
int64_t bufferedStreamDurationUs =
mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
- ALOGV("buffered %" PRId64 " for stream %d",
- bufferedStreamDurationUs, mPacketSources.keyAt(i));
+
+ FSLOGV(mPacketSources.keyAt(i), "buffered %lld", (long long)bufferedStreamDurationUs);
+
if (bufferedDurationUs == -1ll
|| bufferedStreamDurationUs < bufferedDurationUs) {
bufferedDurationUs = bufferedStreamDurationUs;
@@ -776,8 +789,9 @@ void PlaylistFetcher::onMonitorQueue() {
}
if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) {
- ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "",
- bufferedDurationUs, kMinBufferedDurationUs);
+ FLOGV("monitoring, buffered=%lld < %lld",
+ (long long)bufferedDurationUs, (long long)kMinBufferedDurationUs);
+
// delay the next download slightly; hopefully this gives other concurrent fetchers
// a better chance to run.
// onDownloadNext();
@@ -792,8 +806,12 @@ void PlaylistFetcher::onMonitorQueue() {
if (delayUs > targetDurationUs / 2) {
delayUs = targetDurationUs / 2;
}
- ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
- delayUs, bufferedDurationUs, kMinBufferedDurationUs);
+
+ FLOGV("pausing for %lld, buffered=%lld > %lld",
+ (long long)delayUs,
+ (long long)bufferedDurationUs,
+ (long long)kMinBufferedDurationUs);
+
postMonitorQueue(delayUs);
}
}
@@ -891,6 +909,12 @@ bool PlaylistFetcher::shouldPauseDownload() {
}
}
lastEnqueueUs -= mSegmentFirstPTS;
+
+ FLOGV("%spausing now, thresholdUs %lld, remaining %lld",
+ targetDurationUs - lastEnqueueUs > thresholdUs ? "" : "not ",
+ (long long)thresholdUs,
+ (long long)(targetDurationUs - lastEnqueueUs));
+
if (targetDurationUs - lastEnqueueUs > thresholdUs) {
return true;
}
@@ -940,8 +964,8 @@ bool PlaylistFetcher::initDownloadState(
mStartTimeUs -= getSegmentStartTimeUs(mSeqNumber);
}
mStartTimeUsRelative = true;
- ALOGV("Initial sequence number for time %" PRId64 " is %d from (%d .. %d)",
- mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
+ FLOGV("Initial sequence number for time %lld is %d from (%d .. %d)",
+ (long long)mStartTimeUs, mSeqNumber, firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
} else {
// When adapting or track switching, mSegmentStartTimeUs (relative
@@ -966,7 +990,7 @@ bool PlaylistFetcher::initDownloadState(
if (mSeqNumber > lastSeqNumberInPlaylist) {
mSeqNumber = lastSeqNumberInPlaylist;
}
- ALOGV("Initial sequence number for live event %d from (%d .. %d)",
+ FLOGV("Initial sequence number is %d from (%d .. %d)",
mSeqNumber, firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
}
@@ -995,10 +1019,10 @@ bool PlaylistFetcher::initDownloadState(
if (delayUs > kMaxMonitorDelayUs) {
delayUs = kMaxMonitorDelayUs;
}
- ALOGV("sequence number high: %d from (%d .. %d), "
- "monitor in %" PRId64 " (retry=%d)",
+ FLOGV("sequence number high: %d from (%d .. %d), "
+ "monitor in %lld (retry=%d)",
mSeqNumber, firstSeqNumberInPlaylist,
- lastSeqNumberInPlaylist, delayUs, mNumRetries);
+ lastSeqNumberInPlaylist, (long long)delayUs, mNumRetries);
postMonitorQueue(delayUs);
return false;
}
@@ -1067,9 +1091,9 @@ bool PlaylistFetcher::initDownloadState(
// Seek jumped to a new discontinuity sequence. We need to signal
// a format change to decoder. Decoder needs to shutdown and be
// created again if seamless format change is unsupported.
- ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+ FLOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
"mDiscontinuitySeq %d, mStartTimeUs %lld",
- mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
+ mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
discontinuity = true;
}
mLastDiscontinuitySeq = -1;
@@ -1134,7 +1158,7 @@ bool PlaylistFetcher::initDownloadState(
}
}
- ALOGV("fetching segment %d from (%d .. %d)",
+ FLOGV("fetching segment %d from (%d .. %d)",
mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
return true;
}
@@ -1157,7 +1181,7 @@ void PlaylistFetcher::onDownloadNext() {
firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist);
connectHTTP = false;
- ALOGV("resuming: '%s'", uri.c_str());
+ FLOGV("resuming: '%s'", uri.c_str());
} else {
if (!initDownloadState(
uri,
@@ -1166,7 +1190,7 @@ void PlaylistFetcher::onDownloadNext() {
lastSeqNumberInPlaylist)) {
return;
}
- ALOGV("fetching: '%s'", uri.c_str());
+ FLOGV("fetching: '%s'", uri.c_str());
}
int64_t range_offset, range_length;
@@ -1196,6 +1220,11 @@ void PlaylistFetcher::onDownloadNext() {
| LiveSession::STREAMTYPE_VIDEO))) {
int64_t delayUs = ALooper::GetNowUs() - startUs;
mSession->addBandwidthMeasurement(bytesRead, delayUs);
+
+ if (delayUs > 2000000ll) {
+ FLOGV("bytesRead %zd took %.2f seconds - abnormal bandwidth dip",
+ bytesRead, (double)delayUs / 1.0e6);
+ }
}
connectHTTP = false;
@@ -1584,6 +1613,16 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
// (newSeqNumber), start at least 1 segment prior.
int32_t newSeqNumber = getSeqNumberWithAnchorTime(
timeUs, targetDiffUs);
+
+ FLOGV("guessed wrong seq number: timeUs=%lld, mStartTimeUs=%lld, "
+ "targetDurationUs=%lld, mSeqNumber=%d, newSeq=%d, firstSeq=%d",
+ (long long)timeUs,
+ (long long)mStartTimeUs,
+ (long long)targetDurationUs,
+ mSeqNumber,
+ newSeqNumber,
+ firstSeqNumberInPlaylist);
+
if (newSeqNumber >= mSeqNumber) {
--mSeqNumber;
} else {
@@ -1604,8 +1643,13 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
}
bool startTimeReached = true;
if (mStartTimeUsRelative) {
+ FLOGV("startTimeUsRelative, timeUs (%lld) - %lld = %lld",
+ (long long)timeUs,
+ (long long)mFirstTimeUs,
+ (long long)(timeUs - mFirstTimeUs));
timeUs -= mFirstTimeUs;
if (timeUs < 0) {
+ FLOGV("clamp negative timeUs to 0");
timeUs = 0;
}
startTimeReached = (timeUs >= mStartTimeUs);
@@ -1614,13 +1658,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
if (!startTimeReached || (isAvc && !mIDRFound)) {
// buffer up to the closest preceding IDR frame in the next segement,
// or the closest succeeding IDR frame after the exact position
+ FSLOGV(stream, "timeUs=%lld, mStartTimeUs=%lld, mIDRFound=%d",
+ (long long)timeUs, (long long)mStartTimeUs, mIDRFound);
if (isAvc) {
if (IsIDR(accessUnit)) {
mVideoBuffer->clear();
+ FSLOGV(stream, "found IDR, clear mVideoBuffer");
mIDRFound = true;
}
if (mIDRFound && mStartTimeUsRelative && !startTimeReached) {
mVideoBuffer->queueAccessUnit(accessUnit);
+ FSLOGV(stream, "saving AVC video AccessUnit");
}
}
if (!startTimeReached || (isAvc && !mIDRFound)) {
@@ -1635,15 +1683,17 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
if (!(streamMask & mPacketSources.keyAt(i))) {
streamMask |= mPacketSources.keyAt(i);
mStartTimeUsNotify->setInt32("streamMask", streamMask);
+ FSLOGV(stream, "found start point, timeUs=%lld, streamMask becomes %x",
+ (long long)timeUs, streamMask);
if (streamMask == mStreamTypeMask) {
+ FLOGV("found start point for all streams");
mStartup = false;
}
}
}
if (mStopParams != NULL) {
- // Queue discontinuity in original stream.
int32_t discontinuitySeq;
int64_t stopTimeUs;
if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
@@ -1651,13 +1701,13 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
|| !mStopParams->findInt64(key, &stopTimeUs)
|| (discontinuitySeq == mDiscontinuitySeq
&& timeUs >= stopTimeUs)) {
+ FSLOGV(stream, "reached stop point, timeUs=%lld", (long long)timeUs);
mStreamTypeMask &= ~stream;
mPacketSources.removeItemsAt(i);
break;
}
}
- // Note that we do NOT dequeue any discontinuities except for format change.
if (stream == LiveSession::STREAMTYPE_VIDEO) {
const bool discard = true;
status_t status;
@@ -1666,11 +1716,16 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
mVideoBuffer->dequeueAccessUnit(&videoBuffer);
setAccessUnitProperties(videoBuffer, source, discard);
packetSource->queueAccessUnit(videoBuffer);
+ int64_t bufferTimeUs;
+ CHECK(videoBuffer->meta()->findInt64("timeUs", &bufferTimeUs));
+ FSLOGV(stream, "queueAccessUnit (saved), timeUs=%lld",
+ (long long)bufferTimeUs);
}
}
setAccessUnitProperties(accessUnit, source);
packetSource->queueAccessUnit(accessUnit);
+ FSLOGV(stream, "queueAccessUnit, timeUs=%lld", (long long)timeUs);
}
if (err != OK) {
@@ -1688,7 +1743,7 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu
if (!mStreamTypeMask) {
// Signal gap is filled between original and new stream.
- ALOGV("ERROR OUT OF RANGE");
+ FLOGV("reached stop point for all streams");
return ERROR_OUT_OF_RANGE;
}
@@ -1918,7 +1973,6 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits(
}
if (mStopParams != NULL) {
- // Queue discontinuity in original stream.
int32_t discontinuitySeq;
int64_t stopTimeUs;
if (!mStopParams->findInt32("discontinuitySeq", &discontinuitySeq)
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index dab56df..f64d160 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -55,8 +55,11 @@ struct PlaylistFetcher : public AHandler {
const sp<AMessage> &notify,
const sp<LiveSession> &session,
const char *uri,
+ int32_t id,
int32_t subtitleGeneration);
+ int32_t getFetcherID() const;
+
sp<DataSource> getDataSource();
void startAsync(
@@ -113,6 +116,8 @@ private:
sp<LiveSession> mSession;
AString mURI;
+ int32_t mFetcherID;
+
uint32_t mStreamTypeMask;
int64_t mStartTimeUs;
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c5bb41b..c7912c0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -355,10 +355,15 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) {
int64_t time2 = -1;
int64_t durationUs = 0;
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- while (it != mBuffers.end()) {
+ List<sp<ABuffer> >::iterator it;
+ for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
const sp<ABuffer> &buffer = *it;
+ int32_t discard;
+ if (buffer->meta()->findInt32("discard", &discard) && discard) {
+ continue;
+ }
+
int64_t timeUs;
if (buffer->meta()->findInt64("timeUs", &timeUs)) {
if (time1 < 0 || timeUs < time1) {
@@ -373,8 +378,6 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) {
durationUs += time2 - time1;
time1 = time2 = -1;
}
-
- ++it;
}
return durationUs + (time2 - time1);
@@ -393,11 +396,19 @@ int64_t AnotherPacketSource::getEstimatedDurationUs() {
return getBufferedDurationUs_l(&finalResult);
}
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- sp<ABuffer> buffer = *it;
+ sp<ABuffer> buffer;
+ int32_t discard;
+ int64_t startTimeUs = -1ll;
+ List<sp<ABuffer> >::iterator it;
+ for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
+ buffer = *it;
+ if (buffer->meta()->findInt32("discard", &discard) && discard) {
+ continue;
+ }
+ buffer->meta()->findInt64("timeUs", &startTimeUs);
+ break;
+ }
- int64_t startTimeUs;
- buffer->meta()->findInt64("timeUs", &startTimeUs);
if (startTimeUs < 0) {
return 0;
}
@@ -514,7 +525,7 @@ void AnotherPacketSource::trimBuffersAfterMeta(
}
HLSTime stopTime(meta);
- ALOGV("trimBuffersAfterMeta: discontinuitySeq %zu, timeUs %lld",
+ ALOGV("trimBuffersAfterMeta: discontinuitySeq %d, timeUs %lld",
stopTime.mSeq, (long long)stopTime.mTimeUs);
List<sp<ABuffer> >::iterator it;
@@ -554,7 +565,7 @@ void AnotherPacketSource::trimBuffersAfterMeta(
sp<AMessage> AnotherPacketSource::trimBuffersBeforeMeta(
const sp<AMessage> &meta) {
HLSTime startTime(meta);
- ALOGV("trimBuffersBeforeMeta: discontinuitySeq %zu, timeUs %lld",
+ ALOGV("trimBuffersBeforeMeta: discontinuitySeq %d, timeUs %lld",
startTime.mSeq, (long long)startTime.mTimeUs);
sp<AMessage> firstMeta;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 1f43d6d..33cfd1d 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -85,12 +85,6 @@ status_t MPEG2TSSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
- int64_t seekTimeUs;
- ReadOptions::SeekMode seekMode;
- if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
- return ERROR_UNSUPPORTED;
- }
-
status_t finalResult;
while (!mImpl->hasBufferAvailable(&finalResult)) {
if (finalResult != OK) {
@@ -103,6 +97,17 @@ status_t MPEG2TSSource::read(
}
}
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode seekMode;
+ if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+ // A seek was requested, but we don't actually support seeking and so can only "seek" to
+ // the current position
+ int64_t nextBufTimeUs;
+ if (mImpl->nextBufferTime(&nextBufTimeUs) != OK || seekTimeUs != nextBufTimeUs) {
+ return ERROR_UNSUPPORTED;
+ }
+ }
+
return mImpl->read(out, options);
}
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 9b6958a..3ab241a 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -85,7 +85,7 @@ OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
if (libHandle == NULL) {
- ALOGE("unable to dlopen %s", libName.c_str());
+ ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());
return OMX_ErrorComponentNotFound;
}
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 8d6ff5b..51e1c78 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -62,6 +62,33 @@ LOCAL_C_INCLUDES := \
include $(BUILD_NATIVE_TEST)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := MediaCodecListOverrides_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ MediaCodecListOverrides_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libstagefright_omx \
+ libutils \
+ liblog
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/libstagefright \
+ frameworks/av/media/libstagefright/include \
+ frameworks/native/include/media/openmax \
+
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_NATIVE_TEST)
+
# Include subdirectory makefiles
# ============================================================
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
new file mode 100644
index 0000000..cacaa84
--- /dev/null
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2015 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 "MediaCodecListOverrides_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecList.h>
+
+namespace android {
+
+static const char kTestOverridesStr[] =
+"<MediaCodecs>\n"
+" <Settings>\n"
+" <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n"
+" </Settings>\n"
+" <Encoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+" <Limit name=\"bitrate\" range=\"1-20000000\" />\n"
+" <Feature name=\"can-swap-width-height\" />\n"
+" </MediaCodec>\n"
+" </Encoders>\n"
+" <Decoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+" <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+" <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n"
+" </MediaCodec>\n"
+" </Decoders>\n"
+"</MediaCodecs>\n";
+
+static const char kTestOverridesStrNew1[] =
+"<MediaCodecs>\n"
+" <Settings>\n"
+" <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n"
+" </Settings>\n"
+" <Encoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+" <Limit name=\"bitrate\" range=\"1-20000000\" />\n"
+" <Feature name=\"can-swap-width-height\" />\n"
+" </MediaCodec>\n"
+" </Encoders>\n"
+" <Decoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+" <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+" <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" </Decoders>\n"
+"</MediaCodecs>\n";
+
+static const char kTestOverridesStrNew2[] =
+"\n"
+"<MediaCodecs>\n"
+" <Encoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" </Encoders>\n"
+" <Decoders>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+" </MediaCodec>\n"
+" <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+" <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+" </MediaCodec>\n"
+" </Decoders>\n"
+"</MediaCodecs>\n";
+
+class MediaCodecListOverridesTest : public ::testing::Test {
+public:
+ MediaCodecListOverridesTest() {}
+
+ void verifyOverrides(const KeyedVector<AString, CodecSettings> &overrides) {
+ EXPECT_EQ(3u, overrides.size());
+
+ EXPECT_TRUE(overrides.keyAt(0) == "OMX.qcom.video.decoder.avc video/avc decoder");
+ const CodecSettings &settings0 = overrides.valueAt(0);
+ EXPECT_EQ(1u, settings0.size());
+ EXPECT_TRUE(settings0.keyAt(0) == "max-supported-instances");
+ EXPECT_TRUE(settings0.valueAt(0) == "4");
+
+ EXPECT_TRUE(overrides.keyAt(1) == "OMX.qcom.video.encoder.avc video/avc encoder");
+ const CodecSettings &settings1 = overrides.valueAt(1);
+ EXPECT_EQ(1u, settings1.size());
+ EXPECT_TRUE(settings1.keyAt(0) == "max-supported-instances");
+ EXPECT_TRUE(settings1.valueAt(0) == "3");
+
+ EXPECT_TRUE(overrides.keyAt(2) == "global");
+ const CodecSettings &settings2 = overrides.valueAt(2);
+ EXPECT_EQ(3u, settings2.size());
+ EXPECT_TRUE(settings2.keyAt(0) == "max-max-supported-instances");
+ EXPECT_TRUE(settings2.valueAt(0) == "8");
+ EXPECT_TRUE(settings2.keyAt(1) == "supports-multiple-secure-codecs");
+ EXPECT_TRUE(settings2.valueAt(1) == "false");
+ EXPECT_TRUE(settings2.keyAt(2) == "supports-secure-with-non-secure-codec");
+ EXPECT_TRUE(settings2.valueAt(2) == "true");
+ }
+
+ void verifySetting(const sp<AMessage> &details, const char *name, const char *value) {
+ AString value1;
+ EXPECT_TRUE(details->findString(name, &value1));
+ EXPECT_TRUE(value1 == value);
+ }
+
+ void createTestInfos(Vector<sp<MediaCodecInfo>> *infos) {
+ const char *name = "OMX.qcom.video.decoder.avc";
+ const bool encoder = false;
+ const char *mime = "video/avc";
+ sp<MediaCodecInfo> info = new MediaCodecInfo(name, encoder, mime);
+ infos->push_back(info);
+ const sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mime);
+ const sp<AMessage> details = caps->getDetails();
+ details->setString("cap1", "value1");
+ details->setString("max-max-supported-instances", "16");
+
+ info = new MediaCodecInfo("anothercodec", true, "anothermime");
+ infos->push_back(info);
+ }
+
+ void addMaxInstancesSetting(
+ const AString &key,
+ const AString &value,
+ KeyedVector<AString, CodecSettings> *results) {
+ CodecSettings settings;
+ settings.add("max-supported-instances", value);
+ results->add(key, settings);
+ }
+
+ void exportTestResultsToXML(const char *fileName) {
+ KeyedVector<AString, CodecSettings> r;
+ addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc decoder", "1", &r);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp decoder", "4", &r);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2 decoder", "3", &r);
+ addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es decoder", "3", &r);
+ addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc encoder", "4", &r);
+ addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es encoder", "4", &r);
+
+ exportResultsToXML(fileName, r);
+ }
+};
+
+TEST_F(MediaCodecListOverridesTest, splitString) {
+ AString s = "abc123";
+ AString delimiter = " ";
+ AString s1;
+ AString s2;
+ EXPECT_FALSE(splitString(s, delimiter, &s1, &s2));
+ s = "abc 123";
+ EXPECT_TRUE(splitString(s, delimiter, &s1, &s2));
+ EXPECT_TRUE(s1 == "abc");
+ EXPECT_TRUE(s2 == "123");
+
+ s = "abc123xyz";
+ delimiter = ",";
+ AString s3;
+ EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3));
+ s = "abc,123xyz";
+ EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3));
+ s = "abc,123,xyz";
+ EXPECT_TRUE(splitString(s, delimiter, &s1, &s2, &s3));
+ EXPECT_TRUE(s1 == "abc");
+ EXPECT_TRUE(s2 == "123" );
+ EXPECT_TRUE(s3 == "xyz");
+}
+
+// TODO: the codec component never returns OMX_EventCmdComplete in unit test.
+TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) {
+ sp<IMediaCodecList> list = MediaCodecList::getInstance();
+ Vector<sp<MediaCodecInfo>> infos;
+ for (size_t i = 0; i < list->countCodecs(); ++i) {
+ infos.push_back(list->getCodecInfo(i));
+ }
+ KeyedVector<AString, CodecSettings> results;
+ profileCodecs(infos, &results, true /* forceToMeasure */);
+ EXPECT_LT(0u, results.size());
+ for (size_t i = 0; i < results.size(); ++i) {
+ AString key = results.keyAt(i);
+ CodecSettings settings = results.valueAt(i);
+ EXPECT_EQ(1u, settings.size());
+ EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances");
+ AString valueS = settings.valueAt(0);
+ int32_t value = strtol(valueS.c_str(), NULL, 10);
+ EXPECT_LT(0, value);
+ ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str());
+ }
+}
+
+TEST_F(MediaCodecListOverridesTest, applyCodecSettings) {
+ AString codecInfo = "OMX.qcom.video.decoder.avc video/avc decoder";
+ Vector<sp<MediaCodecInfo>> infos;
+ createTestInfos(&infos);
+ CodecSettings settings;
+ settings.add("max-supported-instances", "3");
+ settings.add("max-max-supported-instances", "8");
+ applyCodecSettings(codecInfo, settings, &infos);
+
+ EXPECT_EQ(2u, infos.size());
+ EXPECT_TRUE(AString(infos[0]->getCodecName()) == "OMX.qcom.video.decoder.avc");
+ const sp<AMessage> details = infos[0]->getCapabilitiesFor("video/avc")->getDetails();
+ verifySetting(details, "max-supported-instances", "3");
+ verifySetting(details, "max-max-supported-instances", "8");
+
+ EXPECT_TRUE(AString(infos[1]->getCodecName()) == "anothercodec");
+ EXPECT_EQ(0u, infos[1]->getCapabilitiesFor("anothermime")->getDetails()->countEntries());
+}
+
+TEST_F(MediaCodecListOverridesTest, exportResultsToExistingFile) {
+ const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+ remove(fileName);
+
+ FILE *f = fopen(fileName, "wb");
+ if (f == NULL) {
+ ALOGW("Failed to open %s for writing.", fileName);
+ return;
+ }
+ EXPECT_EQ(
+ strlen(kTestOverridesStr),
+ fwrite(kTestOverridesStr, 1, strlen(kTestOverridesStr), f));
+ fclose(f);
+
+ exportTestResultsToXML(fileName);
+
+ // verify
+ AString overrides;
+ f = fopen(fileName, "rb");
+ ASSERT_TRUE(f != NULL);
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ rewind(f);
+
+ char *buf = (char *)malloc(size);
+ EXPECT_EQ(1, fread(buf, size, 1, f));
+ overrides.setTo(buf, size);
+ fclose(f);
+ free(buf);
+
+ EXPECT_TRUE(overrides == kTestOverridesStrNew1);
+
+ remove(fileName);
+}
+
+TEST_F(MediaCodecListOverridesTest, exportResultsToEmptyFile) {
+ const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+ remove(fileName);
+
+ exportTestResultsToXML(fileName);
+
+ // verify
+ AString overrides;
+ FILE *f = fopen(fileName, "rb");
+ ASSERT_TRUE(f != NULL);
+ fseek(f, 0, SEEK_END);
+ long size = ftell(f);
+ rewind(f);
+
+ char *buf = (char *)malloc(size);
+ EXPECT_EQ(1, fread(buf, size, 1, f));
+ overrides.setTo(buf, size);
+ fclose(f);
+ free(buf);
+
+ EXPECT_TRUE(overrides == kTestOverridesStrNew2);
+
+ remove(fileName);
+}
+
+} // namespace android
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index 0e2e48c..ba47172 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -45,7 +45,8 @@ LOCAL_C_INCLUDES := \
frameworks/av/services/mediaresourcemanager \
$(call include-path-for, audio-utils) \
frameworks/av/services/soundtrigger \
- frameworks/av/services/radio
+ frameworks/av/services/radio \
+ external/sonic
LOCAL_MODULE:= mediaserver
LOCAL_32_BIT_ONLY := true
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index fee2347..c359be5 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -44,12 +44,13 @@ LOCAL_SRC_FILES:= \
SpdifStreamOut.cpp \
Effects.cpp \
AudioMixer.cpp.arm \
- PatchPanel.cpp
-
-LOCAL_SRC_FILES += StateQueue.cpp
+ BufferProviders.cpp \
+ PatchPanel.cpp \
+ StateQueue.cpp
LOCAL_C_INCLUDES := \
$(TOPDIR)frameworks/av/services/audiopolicy \
+ $(TOPDIR)external/sonic \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
@@ -68,7 +69,8 @@ LOCAL_SHARED_LIBRARIES := \
libhardware_legacy \
libeffects \
libpowermanager \
- libserviceutility
+ libserviceutility \
+ libsonic
LOCAL_STATIC_LIBRARIES := \
libscheduling_policy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f3206cb..5002099 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -45,6 +45,8 @@
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
+#include <media/AudioResamplerPublic.h>
+
#include <media/EffectsFactoryApi.h>
#include <audio_effects/effect_visualizer.h>
#include <audio_effects/effect_ns.h>
@@ -1140,19 +1142,46 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form
if (ret != NO_ERROR) {
return 0;
}
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
+ return 0;
+ }
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- audio_config_t config;
- memset(&config, 0, sizeof(config));
- config.sample_rate = sampleRate;
- config.channel_mask = channelMask;
- config.format = format;
+ audio_config_t config, proposed;
+ memset(&proposed, 0, sizeof(proposed));
+ proposed.sample_rate = sampleRate;
+ proposed.channel_mask = channelMask;
+ proposed.format = format;
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
- size_t size = dev->get_input_buffer_size(dev, &config);
+ size_t frames;
+ for (;;) {
+ // Note: config is currently a const parameter for get_input_buffer_size()
+ // but we use a copy from proposed in case config changes from the call.
+ config = proposed;
+ frames = dev->get_input_buffer_size(dev, &config);
+ if (frames != 0) {
+ break; // hal success, config is the result
+ }
+ // change one parameter of the configuration each iteration to a more "common" value
+ // to see if the device will support it.
+ if (proposed.format != AUDIO_FORMAT_PCM_16_BIT) {
+ proposed.format = AUDIO_FORMAT_PCM_16_BIT;
+ } else if (proposed.sample_rate != 44100) { // 44.1 is claimed as must in CDD as well as
+ proposed.sample_rate = 44100; // legacy AudioRecord.java. TODO: Query hw?
+ } else {
+ ALOGW("getInputBufferSize failed with minimum buffer size sampleRate %u, "
+ "format %#x, channelMask 0x%X",
+ sampleRate, format, channelMask);
+ break; // retries failed, break out of loop with frames == 0.
+ }
+ }
mHardwareStatus = AUDIO_HW_IDLE;
- return size;
+ if (frames > 0 && config.sample_rate != sampleRate) {
+ frames = destinationFramesPossible(frames, sampleRate, config.sample_rate);
+ }
+ return frames; // may be converted to bytes at the Java level.
}
uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
@@ -1419,9 +1448,8 @@ sp<IAudioRecord> AudioFlinger::openRecord(
goto Exit;
}
- // we don't yet support anything other than 16-bit PCM
- if (!(audio_is_valid_format(format) &&
- audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) {
+ // we don't yet support anything other than linear PCM
+ if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
ALOGE("openRecord() invalid format %#x", format);
lStatus = BAD_VALUE;
goto Exit;
@@ -2002,11 +2030,11 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m
status, address.string());
// If the input could not be opened with the requested parameters and we can handle the
- // conversion internally, try to open again with the proposed parameters. The AudioFlinger can
- // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs.
+ // conversion internally, try to open again with the proposed parameters.
if (status == BAD_VALUE &&
- config->format == halconfig.format && halconfig.format == AUDIO_FORMAT_PCM_16_BIT &&
- (halconfig.sample_rate <= 2 * config->sample_rate) &&
+ audio_is_linear_pcm(config->format) &&
+ audio_is_linear_pcm(halconfig.format) &&
+ (halconfig.sample_rate <= AUDIO_RESAMPLER_DOWN_RATIO_MAX * config->sample_rate) &&
(audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_2) &&
(audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2)) {
// FIXME describe the change proposed by HAL (save old values so we can log them here)
diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp
index 09d86ea..3191598 100644
--- a/services/audioflinger/AudioHwDevice.cpp
+++ b/services/audioflinger/AudioHwDevice.cpp
@@ -44,7 +44,7 @@ status_t AudioHwDevice::openOutputStream(
AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
// Try to open the HAL first using the current format.
- ALOGV("AudioHwDevice::openOutputStream(), try "
+ ALOGV("openOutputStream(), try "
" sampleRate %d, Format %#x, "
"channelMask %#x",
config->sample_rate,
@@ -59,7 +59,7 @@ status_t AudioHwDevice::openOutputStream(
// FIXME Look at any modification to the config.
// The HAL might modify the config to suggest a wrapped format.
// Log this so we can see what the HALs are doing.
- ALOGI("AudioHwDevice::openOutputStream(), HAL returned"
+ ALOGI("openOutputStream(), HAL returned"
" sampleRate %d, Format %#x, "
"channelMask %#x, status %d",
config->sample_rate,
@@ -72,16 +72,19 @@ status_t AudioHwDevice::openOutputStream(
&& ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)
&& ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0);
- // FIXME - Add isEncodingSupported() query to SPDIF wrapper then
- // call it from here.
if (wrapperNeeded) {
- outputStream = new SpdifStreamOut(this, flags);
- status = outputStream->open(handle, devices, &originalConfig, address);
- if (status != NO_ERROR) {
- ALOGE("ERROR - AudioHwDevice::openOutputStream(), SPDIF open returned %d",
- status);
- delete outputStream;
- outputStream = NULL;
+ if (SPDIFEncoder::isFormatSupported(originalConfig.format)) {
+ outputStream = new SpdifStreamOut(this, flags, originalConfig.format);
+ status = outputStream->open(handle, devices, &originalConfig, address);
+ if (status != NO_ERROR) {
+ ALOGE("ERROR - openOutputStream(), SPDIF open returned %d",
+ status);
+ delete outputStream;
+ outputStream = NULL;
+ }
+ } else {
+ ALOGE("ERROR - openOutputStream(), SPDIFEncoder does not support format 0x%08x",
+ originalConfig.format);
}
}
}
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index dddca02..c2c791f 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -38,9 +38,7 @@
#include <audio_utils/format.h>
#include <common_time/local_clock.h>
#include <common_time/cc_helper.h>
-
-#include <media/EffectsFactoryApi.h>
-#include <audio_effects/effect_downmix.h>
+#include <media/AudioResamplerPublic.h>
#include "AudioMixerOps.h"
#include "AudioMixer.h"
@@ -91,323 +89,6 @@ T min(const T& a, const T& b)
return a < b ? a : b;
}
-AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
- size_t outputFrameSize, size_t bufferFrameCount) :
- mInputFrameSize(inputFrameSize),
- mOutputFrameSize(outputFrameSize),
- mLocalBufferFrameCount(bufferFrameCount),
- mLocalBufferData(NULL),
- mConsumed(0)
-{
- ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
- inputFrameSize, outputFrameSize, bufferFrameCount);
- LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
- "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
- inputFrameSize, outputFrameSize);
- if (mLocalBufferFrameCount) {
- (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
- }
- mBuffer.frameCount = 0;
-}
-
-AudioMixer::CopyBufferProvider::~CopyBufferProvider()
-{
- ALOGV("~CopyBufferProvider(%p)", this);
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- free(mLocalBufferData);
-}
-
-status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
- int64_t pts)
-{
- //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
- // this, pBuffer, pBuffer->frameCount, pts);
- if (mLocalBufferFrameCount == 0) {
- status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
- if (res == OK) {
- copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
- }
- return res;
- }
- if (mBuffer.frameCount == 0) {
- mBuffer.frameCount = pBuffer->frameCount;
- status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
- // At one time an upstream buffer provider had
- // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
- //
- // By API spec, if res != OK, then mBuffer.frameCount == 0.
- // but there may be improper implementations.
- ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
- if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
- return res;
- }
- mConsumed = 0;
- }
- ALOG_ASSERT(mConsumed < mBuffer.frameCount);
- size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
- count = min(count, pBuffer->frameCount);
- pBuffer->raw = mLocalBufferData;
- pBuffer->frameCount = count;
- copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
- pBuffer->frameCount);
- return OK;
-}
-
-void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
-{
- //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
- // this, pBuffer, pBuffer->frameCount);
- if (mLocalBufferFrameCount == 0) {
- 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) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- ALOG_ASSERT(mBuffer.frameCount == 0);
- }
- pBuffer->raw = NULL;
- pBuffer->frameCount = 0;
-}
-
-void AudioMixer::CopyBufferProvider::reset()
-{
- if (mBuffer.frameCount != 0) {
- mTrackBufferProvider->releaseBuffer(&mBuffer);
- }
- mConsumed = 0;
-}
-
-AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider(
- audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
- CopyBufferProvider(
- audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
- audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
- bufferFrameCount) // set bufferFrameCount to 0 to do in-place
-{
- ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
- this, inputChannelMask, outputChannelMask, format,
- sampleRate, sessionId);
- if (!sIsMultichannelCapable
- || EffectCreate(&sDwnmFxDesc.uuid,
- sessionId,
- SESSION_ID_INVALID_AND_IGNORED,
- &mDownmixHandle) != 0) {
- ALOGE("DownmixerBufferProvider() error creating downmixer effect");
- mDownmixHandle = NULL;
- return;
- }
- // channel input configuration will be overridden per-track
- mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits
- mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
- mDownmixConfig.inputCfg.format = format;
- mDownmixConfig.outputCfg.format = format;
- mDownmixConfig.inputCfg.samplingRate = sampleRate;
- mDownmixConfig.outputCfg.samplingRate = sampleRate;
- mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
- mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
- // input and output buffer provider, and frame count will not be used as the downmix effect
- // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
- mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
- EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
- mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
-
- int cmdStatus;
- uint32_t replySize = sizeof(int);
-
- // Configure downmixer
- status_t status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
- &mDownmixConfig /*pCmdData*/,
- &replySize, &cmdStatus /*pReplyData*/);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
-
- // Enable downmixer
- replySize = sizeof(int);
- status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
- &replySize, &cmdStatus /*pReplyData*/);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
-
- // Set downmix type
- // parameter size rounded for padding on 32bit boundary
- const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
- const int downmixParamSize =
- sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
- effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
- param->psize = sizeof(downmix_params_t);
- const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
- memcpy(param->data, &downmixParam, param->psize);
- const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
- param->vsize = sizeof(downmix_type_t);
- memcpy(param->data + psizePadded, &downmixType, param->vsize);
- replySize = sizeof(int);
- status = (*mDownmixHandle)->command(mDownmixHandle,
- EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
- param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
- free(param);
- if (status != 0 || cmdStatus != 0) {
- ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
- status, cmdStatus);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
- return;
- }
- ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
-}
-
-AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
-{
- ALOGV("~DownmixerBufferProvider (%p)", this);
- EffectRelease(mDownmixHandle);
- mDownmixHandle = NULL;
-}
-
-void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- mDownmixConfig.inputCfg.buffer.frameCount = frames;
- mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
- mDownmixConfig.outputCfg.buffer.frameCount = frames;
- mDownmixConfig.outputCfg.buffer.raw = dst;
- // may be in-place if src == dst.
- status_t res = (*mDownmixHandle)->process(mDownmixHandle,
- &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
- ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
-}
-
-/* call once in a pthread_once handler. */
-/*static*/ status_t AudioMixer::DownmixerBufferProvider::init()
-{
- // find multichannel downmix effect if we have to play multichannel content
- uint32_t numEffects = 0;
- int ret = EffectQueryNumberEffects(&numEffects);
- if (ret != 0) {
- ALOGE("AudioMixer() error %d querying number of effects", ret);
- return NO_INIT;
- }
- ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
-
- for (uint32_t i = 0 ; i < numEffects ; i++) {
- if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
- ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
- if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
- ALOGI("found effect \"%s\" from %s",
- sDwnmFxDesc.name, sDwnmFxDesc.implementor);
- sIsMultichannelCapable = true;
- break;
- }
- }
- }
- ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
- return NO_INIT;
-}
-
-/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false;
-/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc;
-
-AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- size_t bufferFrameCount) :
- CopyBufferProvider(
- audio_bytes_per_sample(format)
- * audio_channel_count_from_out_mask(inputChannelMask),
- audio_bytes_per_sample(format)
- * audio_channel_count_from_out_mask(outputChannelMask),
- bufferFrameCount),
- mFormat(format),
- mSampleSize(audio_bytes_per_sample(format)),
- mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
- mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
-{
- ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
- this, format, inputChannelMask, outputChannelMask,
- mInputChannels, mOutputChannels);
-
- const audio_channel_representation_t inputRepresentation =
- audio_channel_mask_get_representation(inputChannelMask);
- const audio_channel_representation_t outputRepresentation =
- audio_channel_mask_get_representation(outputChannelMask);
- const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
- const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);
-
- switch (inputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- switch (outputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
- outputBits, inputBits);
- return;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- // TODO: output channel index mask not currently allowed
- // fall through
- default:
- break;
- }
- break;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- switch (outputRepresentation) {
- case AUDIO_CHANNEL_REPRESENTATION_POSITION:
- memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
- outputBits, inputBits);
- return;
- case AUDIO_CHANNEL_REPRESENTATION_INDEX:
- // TODO: output channel index mask not currently allowed
- // fall through
- default:
- break;
- }
- break;
- default:
- break;
- }
- LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
- inputChannelMask, outputChannelMask);
-}
-
-void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- memcpy_by_index_array(dst, mOutputChannels,
- src, mInputChannels, mIdxAry, mSampleSize, frames);
-}
-
-AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat,
- size_t bufferFrameCount) :
- CopyBufferProvider(
- channels * audio_bytes_per_sample(inputFormat),
- channels * audio_bytes_per_sample(outputFormat),
- bufferFrameCount),
- mChannels(channels),
- mInputFormat(inputFormat),
- mOutputFormat(outputFormat)
-{
- ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
-}
-
-void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
-{
- memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
-}
-
// ----------------------------------------------------------------------------
// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
@@ -442,6 +123,7 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
t->resampler = NULL;
t->downmixerBufferProvider = NULL;
t->mReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t++;
}
@@ -454,6 +136,7 @@ AudioMixer::~AudioMixer()
delete t->resampler;
delete t->downmixerBufferProvider;
delete t->mReformatBufferProvider;
+ delete t->mTimestretchBufferProvider;
t++;
}
delete [] mState.outputTemp;
@@ -532,6 +215,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
t->mReformatBufferProvider = NULL;
t->downmixerBufferProvider = NULL;
t->mPostDownmixReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
t->mFormat = format;
t->mMixerInFormat = selectMixerInFormat(format);
@@ -539,6 +223,8 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
+ t->mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ t->mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
// Check the downmixing (or upmixing) requirements.
status_t status = t->prepareForDownmix();
if (status != OK) {
@@ -731,6 +417,10 @@ void AudioMixer::track_t::reconfigureBufferProviders()
mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
bufferProvider = mPostDownmixReformatBufferProvider;
}
+ if (mTimestretchBufferProvider) {
+ mTimestretchBufferProvider->setBufferProvider(bufferProvider);
+ bufferProvider = mTimestretchBufferProvider;
+ }
}
void AudioMixer::deleteTrackName(int name)
@@ -751,7 +441,9 @@ void AudioMixer::deleteTrackName(int name)
mState.tracks[name].unprepareForDownmix();
// delete the reformatter
mState.tracks[name].unprepareForReformat();
-
+ // delete the timestretch provider
+ delete track.mTimestretchBufferProvider;
+ track.mTimestretchBufferProvider = NULL;
mTrackNames &= ~(1<<name);
}
@@ -973,6 +665,26 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
}
}
break;
+ case TIMESTRETCH:
+ switch (param) {
+ case PLAYBACK_RATE: {
+ const float speed = reinterpret_cast<float*>(value)[0];
+ const float pitch = reinterpret_cast<float*>(value)[1];
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= speed
+ && speed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+ "bad speed %f", speed);
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= pitch
+ && pitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+ "bad pitch %f", pitch);
+ if (track.setPlaybackRate(speed, pitch)) {
+ ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, %f %f", speed, pitch);
+ // invalidateState(1 << name);
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
+ }
+ break;
default:
LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -1018,6 +730,28 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam
return false;
}
+bool AudioMixer::track_t::setPlaybackRate(float speed, float pitch)
+{
+ if (speed == mSpeed && pitch == mPitch) {
+ return false;
+ }
+ mSpeed = speed;
+ mPitch = pitch;
+ if (mTimestretchBufferProvider == NULL) {
+ // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+ // but if none exists, it is the channel count (1 for mono).
+ const int timestretchChannelCount = downmixerBufferProvider != NULL
+ ? mMixerChannelCount : channelCount;
+ mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
+ mMixerInFormat, sampleRate, speed, pitch);
+ reconfigureBufferProviders();
+ } else {
+ reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+ ->setPlaybackRate(speed, pitch);
+ }
+ return true;
+}
+
/* Checks to see if the volume ramp has completed and clears the increment
* variables appropriately.
*
@@ -1096,6 +830,8 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
mState.tracks[name].downmixerBufferProvider->reset();
} else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
+ } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
+ mState.tracks[name].mTimestretchBufferProvider->reset();
}
mState.tracks[name].mInputBufferProvider = bufferProvider;
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 381036b..e27a0d1 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -29,6 +29,7 @@
#include <utils/threads.h>
#include "AudioResampler.h"
+#include "BufferProviders.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_INT
@@ -72,6 +73,7 @@ public:
RESAMPLE = 0x3001,
RAMP_VOLUME = 0x3002, // ramp to new volume
VOLUME = 0x3003, // don't ramp
+ TIMESTRETCH = 0x3004,
// set Parameter names
// for target TRACK
@@ -99,6 +101,9 @@ public:
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
AUXLEVEL = 0x4210,
+ // for target TIMESTRETCH
+ PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name;
+ // parameter 'value' is a pointer to the new playback rate.
};
@@ -159,7 +164,6 @@ private:
struct state_t;
struct track_t;
- class CopyBufferProvider;
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
int32_t* aux);
@@ -214,6 +218,9 @@ private:
/* Buffer providers are constructed to translate the track input data as needed.
*
+ * TODO: perhaps make a single PlaybackConverterProvider class to move
+ * all pre-mixer track buffer conversions outside the AudioMixer class.
+ *
* 1) mInputBufferProvider: The AudioTrack buffer provider.
* 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
* match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
@@ -223,13 +230,14 @@ private:
* the number of channels required by the mixer sink.
* 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
* the downmixer requirements to the mixer engine input requirements.
+ * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
- CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
- CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
- CopyBufferProvider* mPostDownmixReformatBufferProvider;
+ PassthruBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
+ PassthruBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
+ PassthruBufferProvider* mPostDownmixReformatBufferProvider;
+ PassthruBufferProvider* mTimestretchBufferProvider;
- // 16-byte boundary
int32_t sessionId;
audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
@@ -251,6 +259,9 @@ private:
audio_channel_mask_t mMixerChannelMask;
uint32_t mMixerChannelCount;
+ float mSpeed;
+ float mPitch;
+
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
@@ -263,6 +274,7 @@ private:
void unprepareForDownmix();
status_t prepareForReformat();
void unprepareForReformat();
+ bool setPlaybackRate(float speed, float pitch);
void reconfigureBufferProviders();
};
@@ -282,112 +294,6 @@ private:
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
- // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
- // and ReformatBufferProvider.
- // It handles a private buffer for use in converting format or channel masks from the
- // input data to a form acceptable by the mixer.
- // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
- // processing pipeline.
- class CopyBufferProvider : public AudioBufferProvider {
- public:
- // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
- // If bufferFrameCount is 0, no private buffer is created and in-place modification of
- // the upstream buffer provider's buffers is performed by copyFrames().
- CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
- size_t bufferFrameCount);
- virtual ~CopyBufferProvider();
-
- // Overrides AudioBufferProvider methods
- virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
- virtual void releaseBuffer(Buffer* buffer);
-
- // Other public methods
-
- // call this to release the buffer to the upstream provider.
- // treat it as an audio discontinuity for future samples.
- virtual void reset();
-
- // this function should be supplied by the derived class. It converts
- // #frames in the *src pointer to the *dst pointer. It is public because
- // some providers will allow this to work on arbitrary buffers outside
- // of the internal buffers.
- virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
-
- // set the upstream buffer provider. Consider calling "reset" before this function.
- void setBufferProvider(AudioBufferProvider *p) {
- mTrackBufferProvider = p;
- }
-
- protected:
- AudioBufferProvider* mTrackBufferProvider;
- const size_t mInputFrameSize;
- const size_t mOutputFrameSize;
- private:
- AudioBufferProvider::Buffer mBuffer;
- const size_t mLocalBufferFrameCount;
- void* mLocalBufferData;
- size_t mConsumed;
- };
-
- // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
- // position dependent downmixing by an Audio Effect.
- class DownmixerBufferProvider : public CopyBufferProvider {
- public:
- DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
- virtual ~DownmixerBufferProvider();
- virtual void copyFrames(void *dst, const void *src, size_t frames);
- bool isValid() const { return mDownmixHandle != NULL; }
-
- static status_t init();
- static bool isMultichannelCapable() { return sIsMultichannelCapable; }
-
- protected:
- effect_handle_t mDownmixHandle;
- effect_config_t mDownmixConfig;
-
- // effect descriptor for the downmixer used by the mixer
- static effect_descriptor_t sDwnmFxDesc;
- // indicates whether a downmix effect has been found and is usable by this mixer
- static bool sIsMultichannelCapable;
- // FIXME: should we allow effects outside of the framework?
- // We need to here. A special ioId that must be <= -2 so it does not map to a session.
- static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
- };
-
- // RemixBufferProvider wraps a track AudioBufferProvider to perform an
- // upmix or downmix to the proper channel count and mask.
- class RemixBufferProvider : public CopyBufferProvider {
- public:
- RemixBufferProvider(audio_channel_mask_t inputChannelMask,
- audio_channel_mask_t outputChannelMask, audio_format_t format,
- size_t bufferFrameCount);
- virtual void copyFrames(void *dst, const void *src, size_t frames);
-
- protected:
- const audio_format_t mFormat;
- const size_t mSampleSize;
- const size_t mInputChannels;
- const size_t mOutputChannels;
- int8_t mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices
- };
-
- // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
- // to an acceptable mixer input format type.
- class ReformatBufferProvider : public CopyBufferProvider {
- public:
- ReformatBufferProvider(int32_t channels,
- audio_format_t inputFormat, audio_format_t outputFormat,
- size_t bufferFrameCount);
- virtual void copyFrames(void *dst, const void *src, size_t frames);
-
- protected:
- const int32_t mChannels;
- const audio_format_t mInputFormat;
- const audio_format_t mOutputFormat;
- };
-
// bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 46e3d6c..e49b7b1 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -41,7 +41,7 @@ public:
AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) :
AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
}
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
// number of bits used in interpolation multiply - 15 bits avoids overflow
@@ -51,9 +51,9 @@ private:
static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
void init() {}
- void resampleMono16(int32_t* out, size_t outFrameCount,
+ size_t resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
- void resampleStereo16(int32_t* out, size_t outFrameCount,
+ size_t resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
@@ -329,7 +329,7 @@ void AudioResampler::reset() {
// ----------------------------------------------------------------------------
-void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
// should never happen, but we overflow if it does
@@ -338,15 +338,16 @@ void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resampleMono16(out, outFrameCount, provider);
- break;
+ return resampleMono16(out, outFrameCount, provider);
case 2:
- resampleStereo16(out, outFrameCount, provider);
- break;
+ return resampleStereo16(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
-void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -442,9 +443,10 @@ resampleStereo16_exit:
// save state
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / 2 /* channels for stereo */;
}
-void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -538,6 +540,7 @@ resampleMono16_exit:
// save state
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex;
}
#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 863614a..a8e3e6f 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -67,12 +67,18 @@ public:
// Resample int16_t samples from provider and accumulate into 'out'.
// A mono provider delivers a sequence of samples.
// A stereo provider delivers a sequence of interleaved pairs of samples.
- // Multi-channel providers are not supported.
+ //
// In either case, 'out' holds interleaved pairs of fixed-point Q4.27.
// That is, for a mono provider, there is an implicit up-channeling.
// Since this method accumulates, the caller is responsible for clearing 'out' initially.
- // FIXME assumes provider is always successful; it should return the actual frame count.
- virtual void resample(int32_t* out, size_t outFrameCount,
+ //
+ // For a float resampler, 'out' holds interleaved pairs of float samples.
+ //
+ // Multichannel interleaved frames for n > 2 is supported for quality DYN_LOW_QUALITY,
+ // DYN_MED_QUALITY, and DYN_HIGH_QUALITY.
+ //
+ // Returns the number of frames resampled into the out buffer.
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
virtual void reset();
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
index d3cbd1c..172c2a5 100644
--- a/services/audioflinger/AudioResamplerCubic.cpp
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "AudioSRC"
+#define LOG_TAG "AudioResamplerCubic"
#include <stdint.h>
#include <string.h>
@@ -32,7 +32,7 @@ void AudioResamplerCubic::init() {
memset(&right, 0, sizeof(state));
}
-void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
// should never happen, but we overflow if it does
@@ -41,15 +41,16 @@ void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resampleMono16(out, outFrameCount, provider);
- break;
+ return resampleMono16(out, outFrameCount, provider);
case 2:
- resampleStereo16(out, outFrameCount, provider);
- break;
+ return resampleStereo16(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
-void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -67,7 +68,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer, mPTS);
if (mBuffer.raw == NULL) {
- return;
+ return 0;
}
// ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
}
@@ -115,9 +116,10 @@ save_state:
// ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / 2 /* channels for stereo */;
}
-void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) {
int32_t vl = mVolume[0];
@@ -135,7 +137,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer, mPTS);
if (mBuffer.raw == NULL) {
- return;
+ return 0;
}
// ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount);
}
@@ -182,6 +184,7 @@ save_state:
// ALOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex;
}
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
index 1ddc5f9..4b45b0b 100644
--- a/services/audioflinger/AudioResamplerCubic.h
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -31,7 +31,7 @@ public:
AudioResamplerCubic(int inChannelCount, int32_t sampleRate) :
AudioResampler(inChannelCount, sampleRate, MED_QUALITY) {
}
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
// number of bits used in interpolation multiply - 14 bits avoids overflow
@@ -43,9 +43,9 @@ private:
int32_t a, b, c, y0, y1, y2, y3;
} state;
void init();
- void resampleMono16(int32_t* out, size_t outFrameCount,
+ size_t resampleMono16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
- void resampleStereo16(int32_t* out, size_t outFrameCount,
+ size_t resampleStereo16(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
static inline int32_t interp(state* p, int32_t x) {
return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1;
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index c21d4ca..6481b85 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -477,15 +477,15 @@ void AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate)
}
template<typename TC, typename TI, typename TO>
-void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
+ return (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
}
template<typename TC, typename TI, typename TO>
template<int CHANNELS, bool LOCKED, int STRIDE>
-void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
+size_t AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
// TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out.
@@ -610,6 +610,7 @@ resample_exit:
ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer
mInBuffer.setImpulse(impulse);
mPhaseFraction = phaseFraction;
+ return outputIndex / OUTPUT_CHANNELS;
}
/* instantiate templates used by AudioResampler::create */
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index 238b163..3b1c381 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -52,7 +52,7 @@ public:
virtual void setVolume(float left, float right);
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
@@ -111,10 +111,10 @@ private:
int inSampleRate, int outSampleRate, double tbwCheat);
template<int CHANNELS, bool LOCKED, int STRIDE>
- void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
+ size_t resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);
// define a pointer to member function type for resample
- typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
+ typedef size_t (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
size_t outFrameCount, AudioBufferProvider* provider);
// data - the contiguous storage and layout of these is important.
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index ba9a356..41730ee 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -256,7 +256,7 @@ void AudioResamplerSinc::setVolume(float left, float right) {
mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
}
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
// FIXME store current state (up or down sample) and only load the coefs when the state
@@ -272,17 +272,18 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
// select the appropriate resampler
switch (mChannelCount) {
case 1:
- resample<1>(out, outFrameCount, provider);
- break;
+ return resample<1>(out, outFrameCount, provider);
case 2:
- resample<2>(out, outFrameCount, provider);
- break;
+ return resample<2>(out, outFrameCount, provider);
+ default:
+ LOG_ALWAYS_FATAL("invalid channel count: %d", mChannelCount);
+ return 0;
}
}
template<int CHANNELS>
-void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+size_t AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
const Constants& c(*mConstants);
@@ -357,6 +358,7 @@ resample_exit:
mImpulse = impulse;
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
+ return outputIndex / CHANNELS;
}
template<int CHANNELS>
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 6d8e85d..0fbeac8 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -39,7 +39,7 @@ public:
virtual ~AudioResamplerSinc();
- virtual void resample(int32_t* out, size_t outFrameCount,
+ virtual size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
private:
void init();
@@ -47,7 +47,7 @@ private:
virtual void setVolume(float left, float right);
template<int CHANNELS>
- void resample(int32_t* out, size_t outFrameCount,
+ size_t resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
template<int CHANNELS>
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
new file mode 100644
index 0000000..dcae5e7
--- /dev/null
+++ b/services/audioflinger/BufferProviders.cpp
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2015 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 "BufferProvider"
+//#define LOG_NDEBUG 0
+
+#include <audio_effects/effect_downmix.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+#include <media/AudioResamplerPublic.h>
+#include <media/EffectsFactoryApi.h>
+
+#include <utils/Log.h>
+
+#include "Configuration.h"
+#include "BufferProviders.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
+ size_t outputFrameSize, size_t bufferFrameCount) :
+ mInputFrameSize(inputFrameSize),
+ mOutputFrameSize(outputFrameSize),
+ mLocalBufferFrameCount(bufferFrameCount),
+ mLocalBufferData(NULL),
+ mConsumed(0)
+{
+ ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
+ inputFrameSize, outputFrameSize, bufferFrameCount);
+ LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
+ "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
+ inputFrameSize, outputFrameSize);
+ if (mLocalBufferFrameCount) {
+ (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
+ }
+ mBuffer.frameCount = 0;
+}
+
+CopyBufferProvider::~CopyBufferProvider()
+{
+ ALOGV("~CopyBufferProvider(%p)", this);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+ int64_t pts)
+{
+ //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ // this, pBuffer, pBuffer->frameCount, pts);
+ if (mLocalBufferFrameCount == 0) {
+ status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+ if (res == OK) {
+ copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
+ }
+ return res;
+ }
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = pBuffer->frameCount;
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+ // At one time an upstream buffer provider had
+ // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
+ //
+ // By API spec, if res != OK, then mBuffer.frameCount == 0.
+ // but there may be improper implementations.
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ }
+ mConsumed = 0;
+ }
+ ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+ size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
+ count = min(count, pBuffer->frameCount);
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = count;
+ copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
+ pBuffer->frameCount);
+ return OK;
+}
+
+void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
+ // this, pBuffer, pBuffer->frameCount);
+ if (mLocalBufferFrameCount == 0) {
+ 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) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ ALOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void CopyBufferProvider::reset()
+{
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ mConsumed = 0;
+}
+
+DownmixerBufferProvider::DownmixerBufferProvider(
+ audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
+ CopyBufferProvider(
+ audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
+ audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
+ bufferFrameCount) // set bufferFrameCount to 0 to do in-place
+{
+ ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
+ this, inputChannelMask, outputChannelMask, format,
+ sampleRate, sessionId);
+ if (!sIsMultichannelCapable
+ || EffectCreate(&sDwnmFxDesc.uuid,
+ sessionId,
+ SESSION_ID_INVALID_AND_IGNORED,
+ &mDownmixHandle) != 0) {
+ ALOGE("DownmixerBufferProvider() error creating downmixer effect");
+ mDownmixHandle = NULL;
+ return;
+ }
+ // channel input configuration will be overridden per-track
+ mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits
+ mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
+ mDownmixConfig.inputCfg.format = format;
+ mDownmixConfig.outputCfg.format = format;
+ mDownmixConfig.inputCfg.samplingRate = sampleRate;
+ mDownmixConfig.outputCfg.samplingRate = sampleRate;
+ mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+ // input and output buffer provider, and frame count will not be used as the downmix effect
+ // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
+ mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
+ EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
+ mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
+
+ int cmdStatus;
+ uint32_t replySize = sizeof(int);
+
+ // Configure downmixer
+ status_t status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
+ &mDownmixConfig /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+
+ // Enable downmixer
+ replySize = sizeof(int);
+ status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
+ &replySize, &cmdStatus /*pReplyData*/);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+
+ // Set downmix type
+ // parameter size rounded for padding on 32bit boundary
+ const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
+ const int downmixParamSize =
+ sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
+ effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
+ param->psize = sizeof(downmix_params_t);
+ const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
+ memcpy(param->data, &downmixParam, param->psize);
+ const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
+ param->vsize = sizeof(downmix_type_t);
+ memcpy(param->data + psizePadded, &downmixType, param->vsize);
+ replySize = sizeof(int);
+ status = (*mDownmixHandle)->command(mDownmixHandle,
+ EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
+ param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
+ free(param);
+ if (status != 0 || cmdStatus != 0) {
+ ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
+ status, cmdStatus);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+ return;
+ }
+ ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
+}
+
+DownmixerBufferProvider::~DownmixerBufferProvider()
+{
+ ALOGV("~DownmixerBufferProvider (%p)", this);
+ EffectRelease(mDownmixHandle);
+ mDownmixHandle = NULL;
+}
+
+void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ mDownmixConfig.inputCfg.buffer.frameCount = frames;
+ mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
+ mDownmixConfig.outputCfg.buffer.frameCount = frames;
+ mDownmixConfig.outputCfg.buffer.raw = dst;
+ // may be in-place if src == dst.
+ status_t res = (*mDownmixHandle)->process(mDownmixHandle,
+ &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
+ ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
+}
+
+/* call once in a pthread_once handler. */
+/*static*/ status_t DownmixerBufferProvider::init()
+{
+ // find multichannel downmix effect if we have to play multichannel content
+ uint32_t numEffects = 0;
+ int ret = EffectQueryNumberEffects(&numEffects);
+ if (ret != 0) {
+ ALOGE("AudioMixer() error %d querying number of effects", ret);
+ return NO_INIT;
+ }
+ ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+ for (uint32_t i = 0 ; i < numEffects ; i++) {
+ if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
+ ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
+ if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
+ ALOGI("found effect \"%s\" from %s",
+ sDwnmFxDesc.name, sDwnmFxDesc.implementor);
+ sIsMultichannelCapable = true;
+ break;
+ }
+ }
+ }
+ ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
+ return NO_INIT;
+}
+
+/*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false;
+/*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc;
+
+RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ size_t bufferFrameCount) :
+ CopyBufferProvider(
+ audio_bytes_per_sample(format)
+ * audio_channel_count_from_out_mask(inputChannelMask),
+ audio_bytes_per_sample(format)
+ * audio_channel_count_from_out_mask(outputChannelMask),
+ bufferFrameCount),
+ mFormat(format),
+ mSampleSize(audio_bytes_per_sample(format)),
+ mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
+ mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
+{
+ ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
+ this, format, inputChannelMask, outputChannelMask,
+ mInputChannels, mOutputChannels);
+
+ const audio_channel_representation_t inputRepresentation =
+ audio_channel_mask_get_representation(inputChannelMask);
+ const audio_channel_representation_t outputRepresentation =
+ audio_channel_mask_get_representation(outputChannelMask);
+ const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
+ const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);
+
+ switch (inputRepresentation) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ switch (outputRepresentation) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
+ outputBits, inputBits);
+ return;
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ // TODO: output channel index mask not currently allowed
+ // fall through
+ default:
+ break;
+ }
+ break;
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ switch (outputRepresentation) {
+ case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+ memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
+ outputBits, inputBits);
+ return;
+ case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+ // TODO: output channel index mask not currently allowed
+ // fall through
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
+ inputChannelMask, outputChannelMask);
+}
+
+void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ memcpy_by_index_array(dst, mOutputChannels,
+ src, mInputChannels, mIdxAry, mSampleSize, frames);
+}
+
+ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount,
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount) :
+ CopyBufferProvider(
+ channelCount * audio_bytes_per_sample(inputFormat),
+ channelCount * audio_bytes_per_sample(outputFormat),
+ bufferFrameCount),
+ mChannelCount(channelCount),
+ mInputFormat(inputFormat),
+ mOutputFormat(outputFormat)
+{
+ ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)",
+ this, channelCount, inputFormat, outputFormat);
+}
+
+void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
+{
+ memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
+}
+
+TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate, float speed, float pitch) :
+ mChannelCount(channelCount),
+ mFormat(format),
+ mSampleRate(sampleRate),
+ mFrameSize(channelCount * audio_bytes_per_sample(format)),
+ mSpeed(speed),
+ mPitch(pitch),
+ mLocalBufferFrameCount(0),
+ mLocalBufferData(NULL),
+ mRemaining(0),
+ mSonicStream(sonicCreateStream(sampleRate, mChannelCount))
+{
+ ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)",
+ this, channelCount, format, sampleRate, speed, pitch);
+ mBuffer.frameCount = 0;
+
+ LOG_ALWAYS_FATAL_IF(mSonicStream == NULL,
+ "TimestretchBufferProvider can't allocate Sonic stream");
+ sonicSetSpeed(mSonicStream, speed);
+}
+
+TimestretchBufferProvider::~TimestretchBufferProvider()
+{
+ ALOGV("~TimestretchBufferProvider(%p)", this);
+ sonicDestroyStream(mSonicStream);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t TimestretchBufferProvider::getNextBuffer(
+ AudioBufferProvider::Buffer *pBuffer, int64_t pts)
+{
+ ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ this, pBuffer, pBuffer->frameCount, pts);
+
+ // BYPASS
+ //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+
+ // check if previously processed data is sufficient.
+ if (pBuffer->frameCount <= mRemaining) {
+ ALOGV("previous sufficient");
+ pBuffer->raw = mLocalBufferData;
+ return OK;
+ }
+
+ // do we need to resize our buffer?
+ if (pBuffer->frameCount > mLocalBufferFrameCount) {
+ void *newmem;
+ if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
+ if (mRemaining != 0) {
+ memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
+ }
+ free(mLocalBufferData);
+ mLocalBufferData = newmem;
+ mLocalBufferFrameCount = pBuffer->frameCount;
+ }
+ }
+
+ // need to fetch more data
+ const size_t outputDesired = pBuffer->frameCount - mRemaining;
+ mBuffer.frameCount = mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+ ? outputDesired : outputDesired * mSpeed + 1;
+
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ ALOGD("buffer error");
+ if (mRemaining == 0) {
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ } else { // return partial count
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+ return OK;
+ }
+ }
+
+ // time-stretch the data
+ size_t dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
+ size_t srcAvailable = mBuffer.frameCount;
+ processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
+ mBuffer.raw, &srcAvailable);
+
+ // release all data consumed
+ mBuffer.frameCount = srcAvailable;
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+
+ // update buffer vars with the actual data processed and return with buffer
+ mRemaining += dstAvailable;
+
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+
+ return OK;
+}
+
+void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
+ this, pBuffer, pBuffer->frameCount);
+
+ // BYPASS
+ //return mTrackBufferProvider->releaseBuffer(pBuffer);
+
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ if (pBuffer->frameCount < mRemaining) {
+ memcpy(mLocalBufferData,
+ (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
+ (mRemaining - pBuffer->frameCount) * mFrameSize);
+ mRemaining -= pBuffer->frameCount;
+ } else if (pBuffer->frameCount == mRemaining) {
+ mRemaining = 0;
+ } else {
+ LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
+ pBuffer->frameCount, mRemaining);
+ }
+
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void TimestretchBufferProvider::reset()
+{
+ mRemaining = 0;
+}
+
+status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch)
+{
+ mSpeed = speed;
+ mPitch = pitch;
+
+ sonicSetSpeed(mSonicStream, speed);
+ //TODO: pitch is ignored for now
+ return OK;
+}
+
+void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames)
+{
+ ALOGV("processFrames(%zu %zu) remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
+ // Note dstFrames is the required number of frames.
+
+ // Ensure consumption from src is as expected.
+ const size_t targetSrc = *dstFrames * mSpeed;
+ if (*srcFrames < targetSrc) { // limit dst frames to that possible
+ *dstFrames = *srcFrames / mSpeed;
+ } else if (*srcFrames > targetSrc + 1) {
+ *srcFrames = targetSrc + 1;
+ }
+
+ switch (mFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
+ ALOGE("sonicWriteFloatToStream cannot realloc");
+ *srcFrames = 0; // cannot consume all of srcBuffer
+ }
+ *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
+ ALOGE("sonicWriteShortToStream cannot realloc");
+ *srcFrames = 0; // cannot consume all of srcBuffer
+ }
+ *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
+ break;
+ default:
+ // could also be caught on construction
+ LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
+ }
+}
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
new file mode 100644
index 0000000..42030c0
--- /dev/null
+++ b/services/audioflinger/BufferProviders.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 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_BUFFER_PROVIDERS_H
+#define ANDROID_BUFFER_PROVIDERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <hardware/audio_effect.h>
+#include <media/AudioBufferProvider.h>
+#include <system/audio.h>
+#include <sonic.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class PassthruBufferProvider : public AudioBufferProvider {
+public:
+ PassthruBufferProvider() : mTrackBufferProvider(NULL) { }
+
+ virtual ~PassthruBufferProvider() { }
+
+ // call this to release the buffer to the upstream provider.
+ // treat it as an audio discontinuity for future samples.
+ virtual void reset() { }
+
+ // set the upstream buffer provider. Consider calling "reset" before this function.
+ virtual void setBufferProvider(AudioBufferProvider *p) {
+ mTrackBufferProvider = p;
+ }
+
+protected:
+ AudioBufferProvider *mTrackBufferProvider;
+};
+
+// Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
+// and ReformatBufferProvider.
+// It handles a private buffer for use in converting format or channel masks from the
+// input data to a form acceptable by the mixer.
+// TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
+// processing pipeline.
+class CopyBufferProvider : public PassthruBufferProvider {
+public:
+ // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
+ // If bufferFrameCount is 0, no private buffer is created and in-place modification of
+ // the upstream buffer provider's buffers is performed by copyFrames().
+ CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
+ size_t bufferFrameCount);
+ virtual ~CopyBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer *buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer *buffer);
+
+ // Overrides PassthruBufferProvider
+ virtual void reset();
+
+ // this function should be supplied by the derived class. It converts
+ // #frames in the *src pointer to the *dst pointer. It is public because
+ // some providers will allow this to work on arbitrary buffers outside
+ // of the internal buffers.
+ virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
+
+protected:
+ const size_t mInputFrameSize;
+ const size_t mOutputFrameSize;
+private:
+ AudioBufferProvider::Buffer mBuffer;
+ const size_t mLocalBufferFrameCount;
+ void *mLocalBufferData;
+ size_t mConsumed;
+};
+
+// DownmixerBufferProvider derives from CopyBufferProvider to provide
+// position dependent downmixing by an Audio Effect.
+class DownmixerBufferProvider : public CopyBufferProvider {
+public:
+ DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
+ virtual ~DownmixerBufferProvider();
+ //Overrides
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+ bool isValid() const { return mDownmixHandle != NULL; }
+ static status_t init();
+ static bool isMultichannelCapable() { return sIsMultichannelCapable; }
+
+protected:
+ effect_handle_t mDownmixHandle;
+ effect_config_t mDownmixConfig;
+
+ // effect descriptor for the downmixer used by the mixer
+ static effect_descriptor_t sDwnmFxDesc;
+ // indicates whether a downmix effect has been found and is usable by this mixer
+ static bool sIsMultichannelCapable;
+ // FIXME: should we allow effects outside of the framework?
+ // We need to here. A special ioId that must be <= -2 so it does not map to a session.
+ static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
+};
+
+// RemixBufferProvider derives from CopyBufferProvider to perform an
+// upmix or downmix to the proper channel count and mask.
+class RemixBufferProvider : public CopyBufferProvider {
+public:
+ RemixBufferProvider(audio_channel_mask_t inputChannelMask,
+ audio_channel_mask_t outputChannelMask, audio_format_t format,
+ size_t bufferFrameCount);
+ //Overrides
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+ const audio_format_t mFormat;
+ const size_t mSampleSize;
+ const size_t mInputChannels;
+ const size_t mOutputChannels;
+ int8_t mIdxAry[sizeof(uint32_t) * 8]; // 32 bits => channel indices
+};
+
+// ReformatBufferProvider derives from CopyBufferProvider to convert the input data
+// to an acceptable mixer input format type.
+class ReformatBufferProvider : public CopyBufferProvider {
+public:
+ ReformatBufferProvider(int32_t channelCount,
+ audio_format_t inputFormat, audio_format_t outputFormat,
+ size_t bufferFrameCount);
+ virtual void copyFrames(void *dst, const void *src, size_t frames);
+
+protected:
+ const uint32_t mChannelCount;
+ const audio_format_t mInputFormat;
+ const audio_format_t mOutputFormat;
+};
+
+// TimestretchBufferProvider derives from PassthruBufferProvider for time stretching
+class TimestretchBufferProvider : public PassthruBufferProvider {
+public:
+ TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate, float speed, float pitch);
+ virtual ~TimestretchBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer* buffer);
+
+ // Overrides PassthruBufferProvider
+ virtual void reset();
+
+ virtual status_t setPlaybackRate(float speed, float pitch);
+
+ // processes frames
+ // dstBuffer is where to place the data
+ // dstFrames [in/out] is the desired frames (return with actual placed in buffer)
+ // srcBuffer is the source data
+ // srcFrames [in/out] is the available source frames (return with consumed)
+ virtual void processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames);
+
+protected:
+ const uint32_t mChannelCount;
+ const audio_format_t mFormat;
+ const uint32_t mSampleRate; // const for now (TODO change this)
+ const size_t mFrameSize;
+ float mSpeed;
+ float mPitch;
+
+private:
+ AudioBufferProvider::Buffer mBuffer;
+ size_t mLocalBufferFrameCount;
+ void *mLocalBufferData;
+ size_t mRemaining;
+ sonicStream mSonicStream;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
+
+#endif // ANDROID_BUFFER_PROVIDERS_H
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index efbdcff..834947f 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -200,26 +200,17 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
status = BAD_VALUE;
goto exit;
}
- // limit to connections between devices and input streams for HAL before 3.0
- if (patch->sinks[i].ext.mix.hw_module == srcModule &&
- (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);
- status = BAD_VALUE;
- goto exit;
- }
}
- if (patch->sinks[0].ext.device.hw_module != srcModule) {
- // limit to device to device connection if not on same hw module
- if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
- ALOGW("createAudioPatch() invalid sink type for cross hw module");
- status = INVALID_OPERATION;
- goto exit;
- }
- // special case num sources == 2 -=> reuse an exiting output mix to connect to the
- // sink
+ // manage patches requiring a software bridge
+ // - Device to device AND
+ // - source HW module != destination HW module OR
+ // - audio HAL version < 3.0
+ // - special patch request with 2 sources (reuse one existing output mix)
+ if ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
+ ((patch->sinks[0].ext.device.hw_module != srcModule) ||
+ (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) ||
+ (patch->num_sources == 2))) {
if (patch->num_sources == 2) {
if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
patch->sinks[0].ext.device.hw_module !=
@@ -304,6 +295,11 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
&halHandle);
}
} else {
+ if (patch->sinks[0].type != AUDIO_PORT_TYPE_MIX) {
+ status = INVALID_OPERATION;
+ goto exit;
+ }
+
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
patch->sinks[0].ext.mix.handle);
if (thread == 0) {
@@ -472,6 +468,7 @@ status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch,
// this track is given the same buffer as the PatchRecord buffer
patch->mPatchTrack = new PlaybackThread::PatchTrack(
patch->mPlaybackThread.get(),
+ audioPatch->sources[1].ext.mix.usecase.stream,
sampleRate,
outChannelMask,
format,
@@ -578,8 +575,8 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle
break;
}
- if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
- patch->sinks[0].ext.device.hw_module != srcModule) {
+ if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
+ removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
clearPatchConnections(removedPatch);
break;
}
@@ -693,5 +690,4 @@ status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_co
return NO_ERROR;
}
-
} // namespace android
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 45df6a9..c51021b 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -298,6 +298,7 @@ class PatchTrack : public Track, public PatchProxyBufferProvider {
public:
PatchTrack(PlaybackThread *playbackThread,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 204a9d6..25d6d95 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -34,6 +34,7 @@ public:
IAudioFlinger::track_flags_t flags,
track_type type);
virtual ~RecordTrack();
+ virtual status_t initCheck() const;
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
virtual void stop();
@@ -66,21 +67,6 @@ private:
bool mOverflow; // overflow on most recent attempt to fill client buffer
- // updated by RecordThread::readInputParameters_l()
- AudioResampler *mResampler;
-
- // interleaved stereo pairs of fixed-point Q4.27
- int32_t *mRsmpOutBuffer;
- // current allocated frame count for the above, which may be larger than needed
- size_t mRsmpOutFrameCount;
-
- size_t mRsmpInUnrel; // unreleased frames remaining from
- // most recent getNextBuffer
- // for debug only
-
- // rolling counter that is never cleared
- int32_t mRsmpInFront; // next available frame
-
AudioBufferProvider::Buffer mSink; // references client's buffer sink in shared memory
// sync event triggering actual audio capture. Frames read before this event will
@@ -93,7 +79,10 @@ private:
ssize_t mFramesToDrop;
// used by resampler to find source frames
- ResamplerBufferProvider *mResamplerBufferProvider;
+ ResamplerBufferProvider *mResamplerBufferProvider;
+
+ // used by the record thread to convert frames to proper destination format
+ RecordBufferConverter *mRecordBufferConverter;
};
// playback track, used by PatchPanel
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index fae19a1..8246fef 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -50,13 +50,6 @@ bool captureHotwordAllowed() {
return ok;
}
-bool captureFmTunerAllowed() {
- static const String16 sCaptureFmTunerAllowed("android.permission.ACCESS_FM_RADIO");
- bool ok = checkCallingPermission(sCaptureFmTunerAllowed);
- if (!ok) ALOGE("android.permission.ACCESS_FM_RADIO");
- return ok;
-}
-
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index ce18a90..df6f6f4 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -23,7 +23,6 @@ extern pid_t getpid_cached;
bool recordingAllowed();
bool captureAudioOutputAllowed();
bool captureHotwordAllowed();
-bool captureFmTunerAllowed();
bool settingsAllowed();
bool modifyAudioRoutingAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp
index d23588e..45b541a 100644
--- a/services/audioflinger/SpdifStreamOut.cpp
+++ b/services/audioflinger/SpdifStreamOut.cpp
@@ -32,10 +32,12 @@ namespace android {
* If the AudioFlinger is processing encoded data and the HAL expects
* PCM then we need to wrap the data in an SPDIF wrapper.
*/
-SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
+SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev,
+ audio_output_flags_t flags,
+ audio_format_t format)
: AudioStreamOut(dev,flags)
, mRateMultiplier(1)
- , mSpdifEncoder(this)
+ , mSpdifEncoder(this, format)
, mRenderPositionHal(0)
, mPreviousHalPosition32(0)
{
@@ -49,15 +51,15 @@ status_t SpdifStreamOut::open(
{
struct audio_config customConfig = *config;
- customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-
// Some data bursts run at a higher sample rate.
+ // TODO Move this into the audio_utils as a static method.
switch(config->format) {
case AUDIO_FORMAT_E_AC3:
mRateMultiplier = 4;
break;
case AUDIO_FORMAT_AC3:
+ case AUDIO_FORMAT_DTS:
+ case AUDIO_FORMAT_DTS_HD:
mRateMultiplier = 1;
break;
default:
@@ -67,6 +69,9 @@ status_t SpdifStreamOut::open(
}
customConfig.sample_rate = config->sample_rate * mRateMultiplier;
+ customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
// Always print this because otherwise it could be very confusing if the
// HAL and AudioFlinger are using different formats.
// Print before open() because HAL may modify customConfig.
diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h
index cb82ac7..d81c064 100644
--- a/services/audioflinger/SpdifStreamOut.h
+++ b/services/audioflinger/SpdifStreamOut.h
@@ -38,7 +38,8 @@ namespace android {
class SpdifStreamOut : public AudioStreamOut {
public:
- SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
+ SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
+ audio_format_t format);
virtual ~SpdifStreamOut() { }
@@ -77,8 +78,9 @@ private:
class MySPDIFEncoder : public SPDIFEncoder
{
public:
- MySPDIFEncoder(SpdifStreamOut *spdifStreamOut)
- : mSpdifStreamOut(spdifStreamOut)
+ MySPDIFEncoder(SpdifStreamOut *spdifStreamOut, audio_format_t format)
+ : SPDIFEncoder(format)
+ , mSpdifStreamOut(spdifStreamOut)
{
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4efb3d7..b30fd20 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -86,7 +86,13 @@
#define ALOGVV(a...) do { } while(0)
#endif
+// TODO: Move these macro/inlines to a header file.
#define max(a, b) ((a) > (b) ? (a) : (b))
+template <typename T>
+static inline T min(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
namespace android {
@@ -1602,13 +1608,19 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac
// If you change this calculation, also review the start threshold which is related.
if (!(*flags & IAudioFlinger::TRACK_FAST)
&& audio_is_linear_pcm(format) && sharedBuffer == 0) {
+ // this must match AudioTrack.cpp calculateMinFrameCount().
+ // TODO: Move to a common library
uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
+ // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack
+ // or the client should compute and pass in a larger buffer request.
size_t minFrameCount =
- minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate);
+ minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount,
+ mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/);
if (frameCount < minFrameCount) { // including frameCount == 0
frameCount = minFrameCount;
}
@@ -3586,21 +3598,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
size_t desiredFrames;
- uint32_t sr = track->sampleRate();
- if (sr == mSampleRate) {
- desiredFrames = mNormalFrameCount;
- } else {
- desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate);
- // add frames already consumed but not yet released by the resampler
- // because mAudioTrackServerProxy->framesReady() will include these frames
- desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
-#if 0
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
-#endif
- }
+ const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
+ float speed, pitch;
+ track->mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
+
+ desiredFrames = sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount, mSampleRate, speed);
+ // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
+ // add frames already consumed but not yet released by the resampler
+ // because mAudioTrackServerProxy->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
@@ -3763,6 +3771,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
+
+ // set the playback rate as an float array {speed, pitch}
+ float playbackRate[2];
+ track->mAudioTrackServerProxy->getPlaybackRate(
+ &playbackRate[0] /*speed*/, &playbackRate[1] /*pitch*/);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TIMESTRETCH,
+ AudioMixer::PLAYBACK_RATE,
+ playbackRate);
+
/*
* Select the appropriate output buffer for the track.
*
@@ -5290,7 +5309,6 @@ failed: ;
// FIXME mNormalSource
}
-
AudioFlinger::RecordThread::~RecordThread()
{
if (mFastCapture != 0) {
@@ -5594,6 +5612,9 @@ reacquire_wakelock:
continue;
}
+ // TODO: This code probably should be moved to RecordTrack.
+ // TODO: Update the activeTrack buffer converter in case of reconfigure.
+
enum {
OVERRUN_UNKNOWN,
OVERRUN_TRUE,
@@ -5608,131 +5629,28 @@ reacquire_wakelock:
size_t framesOut = activeTrack->mSink.frameCount;
LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0));
- int32_t front = activeTrack->mRsmpInFront;
- ssize_t filled = rear - front;
+ // check available frames and handle overrun conditions
+ // if the record track isn't draining fast enough.
+ bool hasOverrun;
size_t framesIn;
-
- if (filled < 0) {
- // should not happen, but treat like a massive overrun and re-sync
- framesIn = 0;
- activeTrack->mRsmpInFront = rear;
- overrun = OVERRUN_TRUE;
- } else if ((size_t) filled <= mRsmpInFrames) {
- framesIn = (size_t) filled;
- } else {
- // client is not keeping up with server, but give it latest data
- framesIn = mRsmpInFrames;
- activeTrack->mRsmpInFront = front = rear - framesIn;
+ activeTrack->mResamplerBufferProvider->sync(&framesIn, &hasOverrun);
+ if (hasOverrun) {
overrun = OVERRUN_TRUE;
}
-
if (framesOut == 0 || framesIn == 0) {
break;
}
- if (activeTrack->mResampler == NULL) {
- // no resampling
- if (framesIn > framesOut) {
- framesIn = framesOut;
- } else {
- framesOut = framesIn;
- }
- int8_t *dst = activeTrack->mSink.i8;
- while (framesIn > 0) {
- front &= mRsmpInFramesP2 - 1;
- size_t part1 = mRsmpInFramesP2 - front;
- if (part1 > framesIn) {
- part1 = framesIn;
- }
- int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize);
- if (mChannelCount == activeTrack->mChannelCount) {
- memcpy(dst, src, part1 * mFrameSize);
- } else if (mChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src,
- part1);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
- (const int16_t *)src, part1);
- }
- dst += part1 * activeTrack->mFrameSize;
- front += part1;
- framesIn -= part1;
- }
- activeTrack->mRsmpInFront += framesOut;
-
- } else {
- // resampling
- // FIXME framesInNeeded should really be part of resampler API, and should
- // depend on the SRC ratio
- // to keep mRsmpInBuffer full so resampler always has sufficient input
- size_t framesInNeeded;
- // FIXME only re-calculate when it changes, and optimize for common ratios
- // Do not precompute in/out because floating point is not associative
- // e.g. a*b/c != a*(b/c).
- const double in(mSampleRate);
- const double out(activeTrack->mSampleRate);
- framesInNeeded = ceil(framesOut * in / out) + 1;
- ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
- framesInNeeded, framesOut, in / out);
- // Although we theoretically have framesIn in circular buffer, some of those are
- // unreleased frames, and thus must be discounted for purpose of budgeting.
- size_t unreleased = activeTrack->mRsmpInUnrel;
- framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
- if (framesIn < framesInNeeded) {
- ALOGV("not enough to resample: have %u frames in but need %u in to "
- "produce %u out given in/out ratio of %.4g",
- framesIn, framesInNeeded, framesOut, in / out);
- size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * out / in) : 0;
- LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
- if (newFramesOut == 0) {
- break;
- }
- framesInNeeded = ceil(newFramesOut * in / out) + 1;
- ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
- framesInNeeded, newFramesOut, out / in);
- LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
- ALOGV("success 2: have %u frames in and need %u in to produce %u out "
- "given in/out ratio of %.4g",
- framesIn, framesInNeeded, newFramesOut, in / out);
- framesOut = newFramesOut;
- } else {
- ALOGV("success 1: have %u in and need %u in to produce %u out "
- "given in/out ratio of %.4g",
- framesIn, framesInNeeded, framesOut, in / out);
- }
-
- // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
- if (activeTrack->mRsmpOutFrameCount < framesOut) {
- // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
- delete[] activeTrack->mRsmpOutBuffer;
- // resampler always outputs stereo
- activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
- activeTrack->mRsmpOutFrameCount = framesOut;
- }
-
- // resampler accumulates, but we only have one source track
- memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
- activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
- // FIXME how about having activeTrack implement this interface itself?
- activeTrack->mResamplerBufferProvider
- /*this*/ /* AudioBufferProvider* */);
- // ditherAndClamp() works as long as all buffers returned by
- // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (activeTrack->mChannelCount == 1) {
- // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t
- ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer,
- framesOut);
- // the resampler always outputs stereo samples:
- // do post stereo to mono conversion
- downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16,
- (const int16_t *)activeTrack->mRsmpOutBuffer, framesOut);
- } else {
- ditherAndClamp((int32_t *)activeTrack->mSink.raw,
- activeTrack->mRsmpOutBuffer, framesOut);
- }
- // now done with mRsmpOutBuffer
-
- }
+ // Don't allow framesOut to be larger than what is possible with resampling
+ // from framesIn.
+ // This isn't strictly necessary but helps limit buffer resizing in
+ // RecordBufferConverter. TODO: remove when no longer needed.
+ framesOut = min(framesOut,
+ destinationFramesPossible(
+ framesIn, mSampleRate, activeTrack->mSampleRate));
+ // process frames from the RecordThread buffer provider to the RecordTrack buffer
+ framesOut = activeTrack->mRecordBufferConverter->convert(
+ activeTrack->mSink.raw, activeTrack->mResamplerBufferProvider, framesOut);
if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) {
overrun = OVERRUN_FALSE;
@@ -6041,12 +5959,9 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
// was initialized to some value closer to the thread's mRsmpInFront, then the track could
// see previously buffered data before it called start(), but with greater risk of overrun.
- recordTrack->mRsmpInFront = mRsmpInRear;
- recordTrack->mRsmpInUnrel = 0;
- // FIXME why reset?
- if (recordTrack->mResampler != NULL) {
- recordTrack->mResampler->reset();
- }
+ recordTrack->mResamplerBufferProvider->reset();
+ // clear any converter state as new data will be discontinuous
+ recordTrack->mRecordBufferConverter->reset();
recordTrack->mState = TrackBase::STARTING_2;
// signal thread to start
mWaitWorkCV.broadcast();
@@ -6222,12 +6137,52 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args
write(fd, result.string(), result.size());
}
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::reset()
+{
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+ RecordThread *recordThread = (RecordThread *) threadBase.get();
+ mRsmpInFront = recordThread->mRsmpInRear;
+ mRsmpInUnrel = 0;
+}
+
+void AudioFlinger::RecordThread::ResamplerBufferProvider::sync(
+ size_t *framesAvailable, bool *hasOverrun)
+{
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
+ RecordThread *recordThread = (RecordThread *) threadBase.get();
+ const int32_t rear = recordThread->mRsmpInRear;
+ const int32_t front = mRsmpInFront;
+ const ssize_t filled = rear - front;
+
+ size_t framesIn;
+ bool overrun = false;
+ if (filled < 0) {
+ // should not happen, but treat like a massive overrun and re-sync
+ framesIn = 0;
+ mRsmpInFront = rear;
+ overrun = true;
+ } else if ((size_t) filled <= recordThread->mRsmpInFrames) {
+ framesIn = (size_t) filled;
+ } else {
+ // client is not keeping up with server, but give it latest data
+ framesIn = recordThread->mRsmpInFrames;
+ mRsmpInFront = /* front = */ rear - framesIn;
+ overrun = true;
+ }
+ if (framesAvailable != NULL) {
+ *framesAvailable = framesIn;
+ }
+ if (hasOverrun != NULL) {
+ *hasOverrun = overrun;
+ }
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
- RecordTrack *activeTrack = mRecordTrack;
- sp<ThreadBase> threadBase = activeTrack->mThread.promote();
+ sp<ThreadBase> threadBase = mRecordTrack->mThread.promote();
if (threadBase == 0) {
buffer->frameCount = 0;
buffer->raw = NULL;
@@ -6235,7 +6190,7 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
}
RecordThread *recordThread = (RecordThread *) threadBase.get();
int32_t rear = recordThread->mRsmpInRear;
- int32_t front = activeTrack->mRsmpInFront;
+ int32_t front = mRsmpInFront;
ssize_t filled = rear - front;
// FIXME should not be P2 (don't want to increase latency)
// FIXME if client not keeping up, discard
@@ -6252,17 +6207,16 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
part1 = ask;
}
if (part1 == 0) {
- // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
- LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
+ // out of data is fine since the resampler will return a short-count.
buffer->raw = NULL;
buffer->frameCount = 0;
- activeTrack->mRsmpInUnrel = 0;
+ mRsmpInUnrel = 0;
return NOT_ENOUGH_DATA;
}
buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount;
buffer->frameCount = part1;
- activeTrack->mRsmpInUnrel = part1;
+ mRsmpInUnrel = part1;
return NO_ERROR;
}
@@ -6270,18 +6224,197 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer(
void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer(
AudioBufferProvider::Buffer* buffer)
{
- RecordTrack *activeTrack = mRecordTrack;
size_t stepCount = buffer->frameCount;
if (stepCount == 0) {
return;
}
- ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel);
- activeTrack->mRsmpInUnrel -= stepCount;
- activeTrack->mRsmpInFront += stepCount;
+ ALOG_ASSERT(stepCount <= mRsmpInUnrel);
+ mRsmpInUnrel -= stepCount;
+ mRsmpInFront += stepCount;
buffer->raw = NULL;
buffer->frameCount = 0;
}
+AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate) :
+ mSrcChannelMask(AUDIO_CHANNEL_INVALID), // updateParameters will set following vars
+ // mSrcFormat
+ // mSrcSampleRate
+ // mDstChannelMask
+ // mDstFormat
+ // mDstSampleRate
+ // mSrcChannelCount
+ // mDstChannelCount
+ // mDstFrameSize
+ mBuf(NULL), mBufFrames(0), mBufFrameSize(0),
+ mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0)
+{
+ (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
+ dstChannelMask, dstFormat, dstSampleRate);
+}
+
+AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
+ free(mBuf);
+ delete mResampler;
+ free(mRsmpOutBuffer);
+}
+
+size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
+ AudioBufferProvider *provider, size_t frames)
+{
+ if (mSrcSampleRate == mDstSampleRate) {
+ ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+ mSrcSampleRate, mSrcFormat, mDstFormat);
+
+ AudioBufferProvider::Buffer buffer;
+ for (size_t i = frames; i > 0; ) {
+ buffer.frameCount = i;
+ status_t status = provider->getNextBuffer(&buffer, 0);
+ if (status != OK || buffer.frameCount == 0) {
+ frames -= i; // cannot fill request.
+ break;
+ }
+ // convert to destination buffer
+ convert(dst, buffer.raw, buffer.frameCount);
+
+ dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize;
+ i -= buffer.frameCount;
+ provider->releaseBuffer(&buffer);
+ }
+ } else {
+ ALOGVV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
+ mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat);
+
+ // reallocate mRsmpOutBuffer as needed; we will grow but never shrink
+ if (mRsmpOutFrameCount < frames) {
+ // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
+ free(mRsmpOutBuffer);
+ // resampler always outputs stereo (FOR NOW)
+ (void)posix_memalign(&mRsmpOutBuffer, 32, frames * FCC_2 * sizeof(int32_t) /*Q4.27*/);
+ mRsmpOutFrameCount = frames;
+ }
+ // resampler accumulates, but we only have one source track
+ memset(mRsmpOutBuffer, 0, frames * FCC_2 * sizeof(int32_t));
+ frames = mResampler->resample((int32_t*)mRsmpOutBuffer, frames, provider);
+
+ // convert to destination buffer
+ convert(dst, mRsmpOutBuffer, frames);
+ }
+ return frames;
+}
+
+status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate)
+{
+ // quick evaluation if there is any change.
+ if (mSrcFormat == srcFormat
+ && mSrcChannelMask == srcChannelMask
+ && mSrcSampleRate == srcSampleRate
+ && mDstFormat == dstFormat
+ && mDstChannelMask == dstChannelMask
+ && mDstSampleRate == dstSampleRate) {
+ return NO_ERROR;
+ }
+
+ const bool valid =
+ audio_is_input_channel(srcChannelMask)
+ && audio_is_input_channel(dstChannelMask)
+ && audio_is_valid_format(srcFormat) && audio_is_linear_pcm(srcFormat)
+ && audio_is_valid_format(dstFormat) && audio_is_linear_pcm(dstFormat)
+ && (srcSampleRate <= dstSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX)
+ ; // no upsampling checks for now
+ if (!valid) {
+ return BAD_VALUE;
+ }
+
+ mSrcFormat = srcFormat;
+ mSrcChannelMask = srcChannelMask;
+ mSrcSampleRate = srcSampleRate;
+ mDstFormat = dstFormat;
+ mDstChannelMask = dstChannelMask;
+ mDstSampleRate = dstSampleRate;
+
+ // compute derived parameters
+ mSrcChannelCount = audio_channel_count_from_in_mask(srcChannelMask);
+ mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask);
+ mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat);
+
+ // do we need a format buffer?
+ if (mSrcFormat != mDstFormat && mDstChannelCount != mSrcChannelCount) {
+ mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat);
+ } else {
+ mBufFrameSize = 0;
+ }
+ mBufFrames = 0; // force the buffer to be resized.
+
+ // do we need to resample?
+ if (mSrcSampleRate != mDstSampleRate) {
+ if (mResampler != NULL) {
+ delete mResampler;
+ }
+ mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
+ mSrcChannelCount, mDstSampleRate); // may seem confusing...
+ mResampler->setSampleRate(mSrcSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+ }
+ return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convert(
+ void *dst, /*const*/ void *src, size_t frames)
+{
+ // check if a memcpy will do
+ if (mResampler == NULL
+ && mSrcChannelCount == mDstChannelCount
+ && mSrcFormat == mDstFormat) {
+ memcpy(dst, src,
+ frames * mDstChannelCount * audio_bytes_per_sample(mDstFormat));
+ return;
+ }
+ // reallocate buffer if needed
+ if (mBufFrameSize != 0 && mBufFrames < frames) {
+ free(mBuf);
+ mBufFrames = frames;
+ (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+ }
+ // do processing
+ if (mResampler != NULL) {
+ // src channel count is always >= 2.
+ void *dstBuf = mBuf != NULL ? mBuf : dst;
+ // ditherAndClamp() works as long as all buffers returned by
+ // activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
+ if (mDstChannelCount == 1) {
+ // the resampler always outputs stereo samples.
+ // FIXME: this rewrites back into src
+ ditherAndClamp((int32_t *)src, (const int32_t *)src, frames);
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
+ (const int16_t *)src, frames);
+ } else {
+ ditherAndClamp((int32_t *)dstBuf, (const int32_t *)src, frames);
+ }
+ } else if (mSrcChannelCount != mDstChannelCount) {
+ void *dstBuf = mBuf != NULL ? mBuf : dst;
+ if (mSrcChannelCount == 1) {
+ upmix_to_stereo_i16_from_mono_i16((int16_t *)dstBuf, (const int16_t *)src,
+ frames);
+ } else {
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf,
+ (const int16_t *)src, frames);
+ }
+ }
+ if (mSrcFormat != mDstFormat) {
+ void *srcBuf = mBuf != NULL ? mBuf : src;
+ memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat,
+ frames * mDstChannelCount);
+ }
+}
+
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
{
@@ -6303,7 +6436,7 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ if (!audio_is_linear_pcm((audio_format_t) value)) {
status = BAD_VALUE;
} else {
reqFormat = (audio_format_t) value;
@@ -6377,10 +6510,10 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
}
if (reconfig) {
if (status == BAD_VALUE &&
- reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
- reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ audio_is_linear_pcm(mInput->stream->common.get_format(&mInput->stream->common)) &&
+ audio_is_linear_pcm(reqFormat) &&
(mInput->stream->common.get_sample_rate(&mInput->stream->common)
- <= (2 * samplingRate)) &&
+ <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate)) &&
audio_channel_count_from_in_mask(
mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&
(channelMask == AUDIO_CHANNEL_IN_MONO ||
@@ -6451,6 +6584,8 @@ void AudioFlinger::RecordThread::readInputParameters_l()
// The value is somewhat arbitrary, and could probably be even larger.
// A larger value should allow more old data to be read after a track calls start(),
// without increasing latency.
+ //
+ // Note this is independent of the maximum downsampling ratio permitted for capture.
mRsmpInFrames = mFrameCount * 7;
mRsmpInFramesP2 = roundup(mRsmpInFrames);
delete[] mRsmpInBuffer;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index d600ea9..27bc56b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1036,17 +1036,127 @@ class RecordThread : public ThreadBase
public:
class RecordTrack;
+
+ /* The ResamplerBufferProvider is used to retrieve recorded input data from the
+ * RecordThread. It maintains local state on the relative position of the read
+ * position of the RecordTrack compared with the RecordThread.
+ */
class ResamplerBufferProvider : public AudioBufferProvider
- // derives from AudioBufferProvider interface for use by resampler
{
public:
- ResamplerBufferProvider(RecordTrack* recordTrack) : mRecordTrack(recordTrack) { }
+ ResamplerBufferProvider(RecordTrack* recordTrack) :
+ mRecordTrack(recordTrack),
+ mRsmpInUnrel(0), mRsmpInFront(0) { }
virtual ~ResamplerBufferProvider() { }
+
+ // called to set the ResamplerBufferProvider to head of the RecordThread data buffer,
+ // skipping any previous data read from the hal.
+ virtual void reset();
+
+ /* Synchronizes RecordTrack position with the RecordThread.
+ * Calculates available frames and handle overruns if the RecordThread
+ * has advanced faster than the ResamplerBufferProvider has retrieved data.
+ * TODO: why not do this for every getNextBuffer?
+ *
+ * Parameters
+ * framesAvailable: pointer to optional output size_t to store record track
+ * frames available.
+ * hasOverrun: pointer to optional boolean, returns true if track has overrun.
+ */
+
+ virtual void sync(size_t *framesAvailable = NULL, bool *hasOverrun = NULL);
+
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
private:
RecordTrack * const mRecordTrack;
+ size_t mRsmpInUnrel; // unreleased frames remaining from
+ // most recent getNextBuffer
+ // for debug only
+ int32_t mRsmpInFront; // next available frame
+ // rolling counter that is never cleared
+ };
+
+ /* The RecordBufferConverter is used for format, channel, and sample rate
+ * conversion for a RecordTrack.
+ *
+ * TODO: Self contained, so move to a separate file later.
+ *
+ * RecordBufferConverter uses the convert() method rather than exposing a
+ * buffer provider interface; this is to save a memory copy.
+ */
+ class RecordBufferConverter
+ {
+ public:
+ RecordBufferConverter(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate);
+
+ ~RecordBufferConverter();
+
+ /* Converts input data from an AudioBufferProvider by format, channelMask,
+ * and sampleRate to a destination buffer.
+ *
+ * Parameters
+ * dst: buffer to place the converted data.
+ * provider: buffer provider to obtain source data.
+ * frames: number of frames to convert
+ *
+ * Returns the number of frames converted.
+ */
+ size_t convert(void *dst, AudioBufferProvider *provider, size_t frames);
+
+ // returns NO_ERROR if constructor was successful
+ status_t initCheck() const {
+ // mSrcChannelMask set on successful updateParameters
+ return mSrcChannelMask != AUDIO_CHANNEL_INVALID ? NO_ERROR : NO_INIT;
+ }
+
+ // allows dynamic reconfigure of all parameters
+ status_t updateParameters(
+ audio_channel_mask_t srcChannelMask, audio_format_t srcFormat,
+ uint32_t srcSampleRate,
+ audio_channel_mask_t dstChannelMask, audio_format_t dstFormat,
+ uint32_t dstSampleRate);
+
+ // called to reset resampler buffers on record track discontinuity
+ void reset() {
+ if (mResampler != NULL) {
+ mResampler->reset();
+ }
+ }
+
+ private:
+ // internal convert function for format and channel mask.
+ void convert(void *dst, /*const*/ void *src, size_t frames);
+
+ // user provided information
+ audio_channel_mask_t mSrcChannelMask;
+ audio_format_t mSrcFormat;
+ uint32_t mSrcSampleRate;
+ audio_channel_mask_t mDstChannelMask;
+ audio_format_t mDstFormat;
+ uint32_t mDstSampleRate;
+
+ // derived information
+ uint32_t mSrcChannelCount;
+ uint32_t mDstChannelCount;
+ size_t mDstFrameSize;
+
+ // format conversion buffer
+ void *mBuf;
+ size_t mBufFrames;
+ size_t mBufFrameSize;
+
+ // resampler info
+ AudioResampler *mResampler;
+ // interleaved stereo pairs of fixed-point Q4.27 or float depending on resampler
+ void *mRsmpOutBuffer;
+ // current allocated frame count for the above, which may be larger than needed
+ size_t mRsmpOutFrameCount;
};
#include "RecordTracks.h"
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index dc9f249..da2d634 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -903,9 +903,14 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times
mPreviousTimestampValid = false;
return INVALID_OPERATION;
}
+ // FIXME Not accurate under dynamic changes of sample rate and speed.
+ // Do not use track's mSampleRate as it is not current for mixer tracks.
+ uint32_t sampleRate = mAudioTrackServerProxy->getSampleRate();
+ float speed, pitch;
+ mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
uint32_t unpresentedFrames =
- ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
- playbackThread->mSampleRate;
+ ((double) playbackThread->mLatchQ.mUnpresentedFrames * sampleRate * speed)
+ / playbackThread->mSampleRate;
// FIXME Since we're using a raw pointer as the key, it is theoretically possible
// for a brand new track to share the same address as a recently destroyed
// track, and thus for us to get the frames released of the wrong track.
@@ -1861,13 +1866,14 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThread,
+ audio_stream_type_t streamType,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
void *buffer,
IAudioFlinger::track_flags_t flags)
- : Track(playbackThread, NULL, AUDIO_STREAM_PATCH,
+ : Track(playbackThread, NULL, streamType,
sampleRate, format, channelMask, frameCount,
buffer, 0, 0, getuid(), flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
@@ -1989,29 +1995,30 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) :
((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE),
type),
- mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
- // See real initialization of mRsmpInFront at RecordThread::start()
- mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
+ mOverflow(false),
+ mFramesToDrop(0)
{
if (mCblk == NULL) {
return;
}
+ mRecordBufferConverter = new RecordBufferConverter(
+ thread->mChannelMask, thread->mFormat, thread->mSampleRate,
+ channelMask, format, sampleRate);
+ // Check if the RecordBufferConverter construction was successful.
+ // If not, don't continue with construction.
+ //
+ // NOTE: It would be extremely rare that the record track cannot be created
+ // for the current device, but a pending or future device change would make
+ // the record track configuration valid.
+ if (mRecordBufferConverter->initCheck() != NO_ERROR) {
+ ALOGE("RecordTrack unable to create record buffer converter");
+ return;
+ }
+
mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack());
-
- uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
- // FIXME I don't understand either of the channel count checks
- if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
- channelCount <= FCC_2) {
- // sink SR
- mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
- thread->mChannelCount, sampleRate);
- // source SR
- mResampler->setSampleRate(thread->mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
- mResamplerBufferProvider = new ResamplerBufferProvider(this);
- }
+ mResamplerBufferProvider = new ResamplerBufferProvider(this);
if (flags & IAudioFlinger::TRACK_FAST) {
ALOG_ASSERT(thread->mFastTrackAvail);
@@ -2022,11 +2029,19 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
ALOGV("%s", __func__);
- delete mResampler;
- delete[] mRsmpOutBuffer;
+ delete mRecordBufferConverter;
delete mResamplerBufferProvider;
}
+status_t AudioFlinger::RecordThread::RecordTrack::initCheck() const
+{
+ status_t status = TrackBase::initCheck();
+ if (status == NO_ERROR && mServerProxy == 0) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
int64_t pts __unused)
diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk
index 8604ef5..536eb93 100644
--- a/services/audioflinger/tests/Android.mk
+++ b/services/audioflinger/tests/Android.mk
@@ -39,11 +39,13 @@ endif
LOCAL_SRC_FILES:= \
test-mixer.cpp \
../AudioMixer.cpp.arm \
+ ../BufferProviders.cpp
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils) \
- frameworks/av/services/audioflinger
+ frameworks/av/services/audioflinger \
+ external/sonic
LOCAL_STATIC_LIBRARIES := \
libsndfile
@@ -57,7 +59,8 @@ LOCAL_SHARED_LIBRARIES := \
libdl \
libcutils \
libutils \
- liblog
+ liblog \
+ libsonic
LOCAL_MODULE:= test-mixer
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index d6217ba..9e375db 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -48,7 +48,10 @@ void resample(int channels, void *output,
if (thisFrames == 0 || thisFrames > outputFrames - i) {
thisFrames = outputFrames - i;
}
- resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
+ size_t framesResampled = resampler->resample(
+ (int32_t*) output + channels*i, thisFrames, provider);
+ // we should have enough buffer space, so there is no short count.
+ ASSERT_EQ(thisFrames, framesResampled);
i += thisFrames;
}
}
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 116d0d6..48d0e29 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -110,6 +110,7 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ int selectedDeviceId,
const audio_offload_info_t *offloadInfo) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding stream.
virtual status_t startOutput(audio_io_handle_t output,
diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h
index a4cc759..4205589 100755
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -18,6 +18,10 @@
#include <system/audio.h>
#include <utils/Log.h>
+#include <math.h>
+
+// Absolute min volume in dB (can be represented in single precision normal float value)
+#define VOLUME_MIN_DB (-758)
class VolumeCurvePoint
{
@@ -32,7 +36,7 @@ public:
/**
* 4 points to define the volume attenuation curve, each characterized by the volume
* index (from 0 to 100) at which they apply, and the attenuation in dB at that index.
- * we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl()
+ * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
*
* @todo shall become configurable
*/
@@ -134,4 +138,20 @@ public:
}
}
+ static inline float DbToAmpl(float decibels)
+ {
+ if (decibels <= VOLUME_MIN_DB) {
+ return 0.0f;
+ }
+ return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
+ }
+
+ static inline float AmplToDb(float amplification)
+ {
+ if (amplification == 0) {
+ return VOLUME_MIN_DB;
+ }
+ return 20 * log10(amplification);
+ }
+
};
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 71ba1cb..7c265aa 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -25,6 +25,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/include \
$(TOPDIR)frameworks/av/services/audiopolicy/common/include \
+ $(TOPDIR)frameworks/av/services/audiopolicy
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/include
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 7536a37..18bcfdb 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -34,12 +34,11 @@ class AudioInputDescriptor: public AudioPortConfig
public:
AudioInputDescriptor(const sp<IOProfile>& profile);
void setIoHandle(audio_io_handle_t ioHandle);
-
+ audio_port_handle_t getId() const;
audio_module_handle_t getModuleHandle() const;
status_t dump(int fd);
- audio_port_handle_t mId;
audio_io_handle_t mIoHandle; // input handle
audio_devices_t mDevice; // current device this input is routed to
AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
@@ -57,6 +56,9 @@ public:
const struct audio_port_config *srcConfig = NULL) const;
virtual sp<AudioPort> getAudioPort() const { return mProfile; }
void toAudioPort(struct audio_port *port) const;
+
+private:
+ audio_port_handle_t mId;
};
class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 43ee691..f1aee46 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -27,24 +27,36 @@ namespace android {
class IOProfile;
class AudioMix;
+class AudioPolicyClientInterface;
// 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: public AudioPortConfig
{
public:
- AudioOutputDescriptor(const sp<IOProfile>& profile);
+ AudioOutputDescriptor(const sp<AudioPort>& port,
+ AudioPolicyClientInterface *clientInterface);
+ virtual ~AudioOutputDescriptor() {}
status_t dump(int fd);
+ void log(const char* indent);
+
+ audio_port_handle_t getId() const;
+ virtual audio_devices_t device() const;
+ virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+ virtual audio_devices_t supportedDevices();
+ virtual bool isDuplicated() const { return false; }
+ virtual uint32_t latency() { return 0; }
+ virtual bool isFixedVolume(audio_devices_t device);
+ virtual sp<AudioOutputDescriptor> subOutput1() { return 0; }
+ virtual sp<AudioOutputDescriptor> subOutput2() { return 0; }
+ virtual bool setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force);
+ virtual void changeRefCount(audio_stream_type_t stream, int delta);
- audio_devices_t device() const;
- void changeRefCount(audio_stream_type_t stream, int delta);
-
- void setIoHandle(audio_io_handle_t ioHandle);
- bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
- audio_devices_t supportedDevices();
- uint32_t latency();
- bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
bool isActive(uint32_t inPastMs = 0) const;
bool isStreamActive(audio_stream_type_t stream,
uint32_t inPastMs = 0,
@@ -52,32 +64,69 @@ public:
virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig = NULL) const;
- virtual sp<AudioPort> getAudioPort() const { return mProfile; }
- void toAudioPort(struct audio_port *port) const;
+ virtual sp<AudioPort> getAudioPort() const { return mPort; }
+ virtual void toAudioPort(struct audio_port *port) const;
audio_module_handle_t getModuleHandle() const;
- audio_port_handle_t mId;
- audio_io_handle_t mIoHandle; // output handle
- uint32_t mLatency; //
- audio_output_flags_t mFlags; //
+ sp<AudioPort> mPort;
audio_devices_t mDevice; // current device this output is routed to
- AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
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];
- 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
+ float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB
int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter
- const sp<IOProfile> mProfile; // I/O profile this output derives from
bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
// device selection. See checkDeviceMuteStrategies()
+ AudioPolicyClientInterface *mClientInterface;
+
+protected:
+ audio_port_handle_t mId;
+};
+
+// Audio output driven by a software mixer in audio flinger.
+class SwAudioOutputDescriptor: public AudioOutputDescriptor
+{
+public:
+ SwAudioOutputDescriptor(const sp<IOProfile>& profile,
+ AudioPolicyClientInterface *clientInterface);
+ virtual ~SwAudioOutputDescriptor() {}
+
+ status_t dump(int fd);
+
+ void setIoHandle(audio_io_handle_t ioHandle);
+
+ virtual audio_devices_t device() const;
+ virtual bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc);
+ virtual audio_devices_t supportedDevices();
+ virtual uint32_t latency();
+ virtual bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); }
+ virtual bool isFixedVolume(audio_devices_t device);
+ virtual sp<AudioOutputDescriptor> subOutput1() { return mOutput1; }
+ virtual sp<AudioOutputDescriptor> subOutput2() { return mOutput2; }
+ virtual void changeRefCount(audio_stream_type_t stream, int delta);
+ virtual bool setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force);
+
+ virtual void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
+ virtual void toAudioPort(struct audio_port *port) const;
+
+ const sp<IOProfile> mProfile; // I/O profile this output derives from
+ audio_io_handle_t mIoHandle; // output handle
+ uint32_t mLatency; //
+ audio_output_flags_t mFlags; //
+ AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
+ sp<SwAudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output
+ sp<SwAudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
};
-class AudioOutputCollection :
- public DefaultKeyedVector< audio_io_handle_t, sp<AudioOutputDescriptor> >
+class SwAudioOutputCollection :
+ public DefaultKeyedVector< audio_io_handle_t, sp<SwAudioOutputDescriptor> >
{
public:
bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
@@ -96,9 +145,9 @@ public:
*/
audio_io_handle_t getA2dpOutput() const;
- sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
+ sp<SwAudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const;
- sp<AudioOutputDescriptor> getPrimaryOutput() const;
+ sp<SwAudioOutputDescriptor> getPrimaryOutput() const;
/**
* return true if any output is playing anything besides the stream to ignore
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 988aed6..d51f4e1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -24,7 +24,7 @@
namespace android {
-class AudioOutputDescriptor;
+class SwAudioOutputDescriptor;
/**
* custom mix entry in mPolicyMixes
@@ -33,19 +33,19 @@ class AudioPolicyMix : public RefBase {
public:
AudioPolicyMix() {}
- const sp<AudioOutputDescriptor> &getOutput() const;
+ const sp<SwAudioOutputDescriptor> &getOutput() const;
- void setOutput(sp<AudioOutputDescriptor> &output);
+ void setOutput(sp<SwAudioOutputDescriptor> &output);
void clearOutput();
- android::AudioMix &getMix();
+ android::AudioMix *getMix();
void setMix(AudioMix &mix);
private:
AudioMix mMix; // Audio policy mix descriptor
- sp<AudioOutputDescriptor> mOutput; // Corresponding output stream
+ sp<SwAudioOutputDescriptor> mOutput; // Corresponding output stream
};
@@ -58,24 +58,24 @@ public:
status_t unregisterMix(String8 address);
- void closeOutput(sp<AudioOutputDescriptor> &desc);
+ void closeOutput(sp<SwAudioOutputDescriptor> &desc);
/**
* Try to find an output descriptor for the given attributes.
*
- * @param[in] attributes to consider for the research of output descriptor.
+ * @param[in] attributes to consider fowr the research of output descriptor.
* @param[out] desc to return if an output could be found.
*
* @return NO_ERROR if an output was found for the given attribute (in this case, the
* descriptor output param is initialized), error code otherwise.
*/
- status_t getOutputForAttr(audio_attributes_t attributes, sp<AudioOutputDescriptor> &desc);
+ status_t getOutputForAttr(audio_attributes_t attributes, sp<SwAudioOutputDescriptor> &desc);
audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource,
audio_devices_t availableDeviceTypes,
AudioMix **policyMix);
- status_t getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix);
+ status_t getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix);
};
}; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 4f7f2bc..1c2c27e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -32,13 +32,11 @@ class AudioPort : public virtual RefBase
{
public:
AudioPort(const String8& name, audio_port_type_t type,
- audio_port_role_t role, const sp<HwModule>& module);
+ audio_port_role_t role);
virtual ~AudioPort() {}
- audio_port_handle_t getHandle() { return mId; }
-
- void attach(const sp<HwModule>& module);
- bool isAttached() { return mId != 0; }
+ virtual void attach(const sp<HwModule>& module);
+ bool isAttached() { return mModule != 0; }
static audio_port_handle_t getNextUniqueId();
@@ -64,8 +62,12 @@ public:
// searches for an exact match
status_t checkExactChannelMask(audio_channel_mask_t channelMask) const;
// searches for a compatible match, currently implemented for input channel masks only
- status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const;
- status_t checkFormat(audio_format_t format) const;
+ status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask) const;
+
+ status_t checkExactFormat(audio_format_t format) const;
+ // searches for a compatible match, currently implemented for input formats only
+ status_t checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat) const;
status_t checkGain(const struct audio_gain_config *gainConfig, int index) const;
uint32_t pickSamplingRate() const;
@@ -73,11 +75,19 @@ public:
audio_format_t pickFormat() const;
static const audio_format_t sPcmFormatCompareTable[];
+ static int compareFormatsGoodToBad(
+ const audio_format_t *format1, const audio_format_t *format2) {
+ // compareFormats sorts from bad to good, we reverse it here
+ return compareFormats(*format2, *format1);
+ }
static int compareFormats(audio_format_t format1, audio_format_t format2);
audio_module_handle_t getModuleHandle() const;
+ uint32_t getModuleVersion() const;
+ const char *getModuleName() const;
void dump(int fd, int spaces) const;
+ void log(const char* indent) const;
String8 mName;
audio_port_type_t mType;
@@ -94,13 +104,6 @@ public:
uint32_t mFlags; // attribute flags (e.g primary output,
// direct output...).
-
-protected:
- //TODO - clarify the role of mId in this case, both an "attached" indicator
- // and a unique ID for identifying a port to the (upcoming) selection API,
- // and its relationship to the mId in AudioOutputDescriptor and AudioInputDescriptor.
- audio_port_handle_t mId;
-
private:
static volatile int32_t mNextUniqueId;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
index 14a7d36..f8c4d08 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -39,11 +39,12 @@ struct StringToEnum {
};
#define STRING_TO_ENUM(string) { #string, string }
+#define NAME_TO_ENUM(name, value) { name, value }
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
-const StringToEnum sDeviceNameToEnumTable[] = {
+const StringToEnum sDeviceTypeToEnumTable[] = {
STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
@@ -94,6 +95,57 @@ const StringToEnum sDeviceNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
};
+const StringToEnum sDeviceNameToEnumTable[] = {
+ NAME_TO_ENUM("Earpiece", AUDIO_DEVICE_OUT_EARPIECE),
+ NAME_TO_ENUM("Speaker", AUDIO_DEVICE_OUT_SPEAKER),
+ NAME_TO_ENUM("Speaker Protected", AUDIO_DEVICE_OUT_SPEAKER_SAFE),
+ NAME_TO_ENUM("Wired Headset", AUDIO_DEVICE_OUT_WIRED_HEADSET),
+ NAME_TO_ENUM("Wired Headphones", AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
+ NAME_TO_ENUM("BT SCO", AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
+ NAME_TO_ENUM("BT SCO Headset", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET),
+ NAME_TO_ENUM("BT SCO Car Kit", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_SCO),
+ NAME_TO_ENUM("BT A2DP Out", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP),
+ NAME_TO_ENUM("BT A2DP Headphones", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES),
+ NAME_TO_ENUM("BT A2DP Speaker", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_A2DP),
+ NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_AUX_DIGITAL),
+ NAME_TO_ENUM("HDMI Out", AUDIO_DEVICE_OUT_HDMI),
+ NAME_TO_ENUM("Analog Dock Out", AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
+ NAME_TO_ENUM("Digital Dock Out", AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
+ NAME_TO_ENUM("USB Host Out", AUDIO_DEVICE_OUT_USB_ACCESSORY),
+ NAME_TO_ENUM("USB Device Out", AUDIO_DEVICE_OUT_USB_DEVICE),
+ NAME_TO_ENUM("", AUDIO_DEVICE_OUT_ALL_USB),
+ NAME_TO_ENUM("Reroute Submix Out", AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
+ NAME_TO_ENUM("Telephony Tx", AUDIO_DEVICE_OUT_TELEPHONY_TX),
+ NAME_TO_ENUM("Line Out", AUDIO_DEVICE_OUT_LINE),
+ NAME_TO_ENUM("HDMI ARC Out", AUDIO_DEVICE_OUT_HDMI_ARC),
+ NAME_TO_ENUM("S/PDIF Out", AUDIO_DEVICE_OUT_SPDIF),
+ NAME_TO_ENUM("FM transceiver Out", AUDIO_DEVICE_OUT_FM),
+ NAME_TO_ENUM("Aux Line Out", AUDIO_DEVICE_OUT_AUX_LINE),
+ NAME_TO_ENUM("Ambient Mic", AUDIO_DEVICE_IN_AMBIENT),
+ NAME_TO_ENUM("Built-In Mic", AUDIO_DEVICE_IN_BUILTIN_MIC),
+ NAME_TO_ENUM("BT SCO Headset Mic", AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
+ NAME_TO_ENUM("", AUDIO_DEVICE_IN_ALL_SCO),
+ NAME_TO_ENUM("Wired Headset Mic", AUDIO_DEVICE_IN_WIRED_HEADSET),
+ NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_AUX_DIGITAL),
+ NAME_TO_ENUM("HDMI In", AUDIO_DEVICE_IN_HDMI),
+ NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_TELEPHONY_RX),
+ NAME_TO_ENUM("Telephony Rx", AUDIO_DEVICE_IN_VOICE_CALL),
+ NAME_TO_ENUM("Built-In Back Mic", AUDIO_DEVICE_IN_BACK_MIC),
+ NAME_TO_ENUM("Reroute Submix In", AUDIO_DEVICE_IN_REMOTE_SUBMIX),
+ NAME_TO_ENUM("Analog Dock In", AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
+ NAME_TO_ENUM("Digital Dock In", AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
+ NAME_TO_ENUM("USB Host In", AUDIO_DEVICE_IN_USB_ACCESSORY),
+ NAME_TO_ENUM("USB Device In", AUDIO_DEVICE_IN_USB_DEVICE),
+ NAME_TO_ENUM("FM Tuner In", AUDIO_DEVICE_IN_FM_TUNER),
+ NAME_TO_ENUM("TV Tuner In", AUDIO_DEVICE_IN_TV_TUNER),
+ NAME_TO_ENUM("Line In", AUDIO_DEVICE_IN_LINE),
+ NAME_TO_ENUM("S/PDIF In", AUDIO_DEVICE_IN_SPDIF),
+ NAME_TO_ENUM("BT A2DP In", AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
+ NAME_TO_ENUM("Loopback In", AUDIO_DEVICE_IN_LOOPBACK),
+};
+
const StringToEnum sOutputFlagNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT),
STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY),
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index d15f6b4..aa37eec 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -41,19 +41,22 @@ public:
const struct audio_port_config *srcConfig = NULL) const;
// AudioPort
+ virtual void attach(const sp<HwModule>& module);
virtual void loadGains(cnode *root);
virtual void toAudioPort(struct audio_port *port) const;
+ audio_port_handle_t getId() const;
audio_devices_t type() const { return mDeviceType; }
status_t dump(int fd, int spaces, int index) const;
+ void log() const;
String8 mAddress;
- audio_port_handle_t mId;
static String8 emptyNameStr;
private:
- audio_devices_t mDeviceType;
+ audio_devices_t mDeviceType;
+ audio_port_handle_t mId;
friend class DeviceVector;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
index 095e759..ab6fcc1 100644
--- a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
+++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h
@@ -33,7 +33,7 @@ class HwModule;
class IOProfile : public AudioPort
{
public:
- IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module);
+ IOProfile(const String8& name, audio_port_role_t role);
virtual ~IOProfile();
// This method is used for both output and input.
@@ -45,7 +45,9 @@ public:
uint32_t samplingRate,
uint32_t *updatedSamplingRate,
audio_format_t format,
+ audio_format_t *updatedFormat,
audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask,
uint32_t flags) const;
void dump(int fd);
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index fa66728..937160b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -27,9 +27,9 @@
namespace android {
AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
- : mId(0), mIoHandle(0),
+ : mIoHandle(0),
mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0),
- mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false)
+ mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false), mId(0)
{
if (profile != NULL) {
mSamplingRate = profile->pickSamplingRate();
@@ -49,9 +49,17 @@ void AudioInputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
audio_module_handle_t AudioInputDescriptor::getModuleHandle() const
{
+ if (mProfile == 0) {
+ return 0;
+ }
return mProfile->getModuleHandle();
}
+audio_port_handle_t AudioInputDescriptor::getId() const
+{
+ return mId;
+}
+
void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
@@ -68,7 +76,7 @@ void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig
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.hw_module = getModuleHandle();
dstConfig->ext.mix.handle = mIoHandle;
dstConfig->ext.mix.usecase.source = mInputSource;
}
@@ -80,7 +88,7 @@ void 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.hw_module = getModuleHandle();
port->ext.mix.handle = mIoHandle;
port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
}
@@ -91,7 +99,7 @@ status_t AudioInputDescriptor::dump(int fd)
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, " ID: %d\n", mId);
+ snprintf(buffer, SIZE, " ID: %d\n", getId());
result.append(buffer);
snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
result.append(buffer);
@@ -130,7 +138,7 @@ sp<AudioInputDescriptor> AudioInputCollection::getInputFromId(audio_port_handle_
sp<AudioInputDescriptor> inputDesc = NULL;
for (size_t i = 0; i < size(); i++) {
inputDesc = valueAt(i);
- if (inputDesc->mId == id) {
+ if (inputDesc->getId() == id) {
break;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index cdb5b51..596aa1d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -17,9 +17,11 @@
#define LOG_TAG "APM::AudioOutputDescriptor"
//#define LOG_NDEBUG 0
+#include <AudioPolicyInterface.h>
#include "AudioOutputDescriptor.h"
#include "IOProfile.h"
#include "AudioGain.h"
+#include "Volume.h"
#include "HwModule.h"
#include <media/AudioPolicy.h>
@@ -29,11 +31,10 @@
namespace android {
-AudioOutputDescriptor::AudioOutputDescriptor(const sp<IOProfile>& profile)
- : mId(0), mIoHandle(0), mLatency(0),
- mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL),
- mPatchHandle(0),
- mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0)
+AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
+ AudioPolicyClientInterface *clientInterface)
+ : mPort(port), mDevice(AUDIO_DEVICE_NONE),
+ mPatchHandle(0), mClientInterface(clientInterface), mId(0)
{
// clear usage count for all stream types
for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
@@ -45,66 +46,50 @@ AudioOutputDescriptor::AudioOutputDescriptor(const sp<IOProfile>& profile)
for (int i = 0; i < NUM_STRATEGIES; i++) {
mStrategyMutedByDevice[i] = false;
}
- if (profile != NULL) {
- mFlags = (audio_output_flags_t)profile->mFlags;
- mSamplingRate = profile->pickSamplingRate();
- mFormat = profile->pickFormat();
- mChannelMask = profile->pickChannelMask();
- if (profile->mGains.size() > 0) {
- profile->mGains[0]->getDefaultConfig(&mGain);
+ if (port != NULL) {
+ mSamplingRate = port->pickSamplingRate();
+ mFormat = port->pickFormat();
+ mChannelMask = port->pickChannelMask();
+ if (port->mGains.size() > 0) {
+ port->mGains[0]->getDefaultConfig(&mGain);
}
}
}
audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const
{
- return mProfile->getModuleHandle();
+ return mPort->getModuleHandle();
}
-audio_devices_t AudioOutputDescriptor::device() const
+audio_port_handle_t AudioOutputDescriptor::getId() const
{
- if (isDuplicated()) {
- return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
- } else {
- return mDevice;
- }
+ return mId;
}
-void AudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+audio_devices_t AudioOutputDescriptor::device() const
{
- mId = AudioPort::getNextUniqueId();
- mIoHandle = ioHandle;
+ return mDevice;
}
-uint32_t AudioOutputDescriptor::latency()
+audio_devices_t AudioOutputDescriptor::supportedDevices()
{
- if (isDuplicated()) {
- return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
- } else {
- return mLatency;
- }
+ return mDevice;
}
bool AudioOutputDescriptor::sharesHwModuleWith(
const sp<AudioOutputDescriptor> outputDesc)
{
- if (isDuplicated()) {
- return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
- } else if (outputDesc->isDuplicated()){
- return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2);
+ if (outputDesc->isDuplicated()) {
+ return sharesHwModuleWith(outputDesc->subOutput1()) ||
+ sharesHwModuleWith(outputDesc->subOutput2());
} else {
- return (mProfile->mModule == outputDesc->mProfile->mModule);
+ return (getModuleHandle() == outputDesc->getModuleHandle());
}
}
void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
int delta)
{
- // forward usage count change to attached outputs
- if (isDuplicated()) {
- mOutput1->changeRefCount(stream, delta);
- mOutput2->changeRefCount(stream, delta);
- }
if ((delta + (int)mRefCount[stream]) < 0) {
ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d",
delta, stream, mRefCount[stream]);
@@ -115,15 +100,6 @@ void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
}
-audio_devices_t AudioOutputDescriptor::supportedDevices()
-{
- if (isDuplicated()) {
- return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
- } else {
- return mProfile->mSupportedDevices.types() ;
- }
-}
-
bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
{
nsecs_t sysTime = 0;
@@ -160,12 +136,33 @@ bool AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream,
return false;
}
+
+bool AudioOutputDescriptor::isFixedVolume(audio_devices_t device __unused)
+{
+ return false;
+}
+
+bool AudioOutputDescriptor::setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device __unused,
+ uint32_t delayMs,
+ bool force)
+{
+ // We actually change the volume if:
+ // - the float value returned by computeVolume() changed
+ // - the force flag is set
+ if (volume != mCurVolume[stream] || force) {
+ ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
+ mCurVolume[stream] = volume;
+ return true;
+ }
+ return false;
+}
+
void AudioOutputDescriptor::toAudioPortConfig(
struct audio_port_config *dstConfig,
const struct audio_port_config *srcConfig) const
{
- ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
-
dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN;
if (srcConfig != NULL) {
@@ -176,22 +173,16 @@ void AudioOutputDescriptor::toAudioPortConfig(
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.hw_module = getModuleHandle();
dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
}
void AudioOutputDescriptor::toAudioPort(
struct audio_port *port) const
{
- ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
- mProfile->toAudioPort(port);
+ mPort->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;
+ port->ext.mix.hw_module = getModuleHandle();
}
status_t AudioOutputDescriptor::dump(int fd)
@@ -208,10 +199,6 @@ status_t AudioOutputDescriptor::dump(int fd)
result.append(buffer);
snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask);
result.append(buffer);
- snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
- result.append(buffer);
- snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
- result.append(buffer);
snprintf(buffer, SIZE, " Devices %08x\n", device());
result.append(buffer);
snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
@@ -226,11 +213,165 @@ status_t AudioOutputDescriptor::dump(int fd)
return NO_ERROR;
}
-bool AudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+void AudioOutputDescriptor::log(const char* indent)
+{
+ ALOGI("%sID: %d,0x%X, [rt:%d fmt:0x%X ch:0x%X]",
+ indent, mId, mId, mSamplingRate, mFormat, mChannelMask);
+}
+
+// SwAudioOutputDescriptor implementation
+SwAudioOutputDescriptor::SwAudioOutputDescriptor(
+ const sp<IOProfile>& profile, AudioPolicyClientInterface *clientInterface)
+ : AudioOutputDescriptor(profile, clientInterface),
+ mProfile(profile), mIoHandle(0), mLatency(0),
+ mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
+ mOutput1(0), mOutput2(0), mDirectOpenCount(0)
+{
+ if (profile != NULL) {
+ mFlags = (audio_output_flags_t)profile->mFlags;
+ }
+}
+
+void SwAudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle)
+{
+ mId = AudioPort::getNextUniqueId();
+ mIoHandle = ioHandle;
+}
+
+
+status_t SwAudioOutputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ AudioOutputDescriptor::dump(fd);
+
+ return NO_ERROR;
+}
+
+audio_devices_t SwAudioOutputDescriptor::device() const
+{
+ if (isDuplicated()) {
+ return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice);
+ } else {
+ return mDevice;
+ }
+}
+
+bool SwAudioOutputDescriptor::sharesHwModuleWith(
+ const sp<AudioOutputDescriptor> outputDesc)
+{
+ if (isDuplicated()) {
+ return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc);
+ } else if (outputDesc->isDuplicated()){
+ return sharesHwModuleWith(outputDesc->subOutput1()) ||
+ sharesHwModuleWith(outputDesc->subOutput2());
+ } else {
+ return AudioOutputDescriptor::sharesHwModuleWith(outputDesc);
+ }
+}
+
+audio_devices_t SwAudioOutputDescriptor::supportedDevices()
+{
+ if (isDuplicated()) {
+ return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices());
+ } else {
+ return mProfile->mSupportedDevices.types() ;
+ }
+}
+
+uint32_t SwAudioOutputDescriptor::latency()
+{
+ if (isDuplicated()) {
+ return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency;
+ } else {
+ return mLatency;
+ }
+}
+
+void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+ int delta)
+{
+ // forward usage count change to attached outputs
+ if (isDuplicated()) {
+ mOutput1->changeRefCount(stream, delta);
+ mOutput2->changeRefCount(stream, delta);
+ }
+ AudioOutputDescriptor::changeRefCount(stream, delta);
+}
+
+
+bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
+{
+ // unit gain if rerouting to external policy
+ if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
+ if (mPolicyMix != NULL) {
+ ALOGV("max gain when rerouting for output=%d", mIoHandle);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwAudioOutputDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+
+ ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle);
+ AudioOutputDescriptor::toAudioPortConfig(dstConfig, srcConfig);
+
+ dstConfig->ext.mix.handle = mIoHandle;
+}
+
+void SwAudioOutputDescriptor::toAudioPort(
+ struct audio_port *port) const
+{
+ ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle);
+
+ AudioOutputDescriptor::toAudioPort(port);
+
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.handle = mIoHandle;
+ port->ext.mix.latency_class =
+ mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+}
+
+bool SwAudioOutputDescriptor::setVolume(float volume,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t delayMs,
+ bool force)
+{
+ bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);
+
+ if (changed) {
+ // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
+ // enabled
+ float volume = Volume::DbToAmpl(mCurVolume[stream]);
+ if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
+ mClientInterface->setStreamVolume(
+ AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
+ }
+ mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
+ }
+ return changed;
+}
+
+// SwAudioOutputCollection implementation
+
+bool SwAudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < this->size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = this->valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
return true;
}
@@ -238,12 +379,12 @@ bool AudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t
return false;
}
-bool AudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
+bool SwAudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
uint32_t inPastMs) const
{
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) &&
outputDesc->isStreamActive(stream, inPastMs, sysTime)) {
// do not consider re routing (when the output is going to a dynamic policy)
@@ -256,10 +397,10 @@ bool AudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream,
return false;
}
-audio_io_handle_t AudioOutputCollection::getA2dpOutput() const
+audio_io_handle_t SwAudioOutputCollection::getA2dpOutput() const
{
for (size_t i = 0; i < size(); i++) {
- sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) {
return this->keyAt(i);
}
@@ -267,10 +408,10 @@ audio_io_handle_t AudioOutputCollection::getA2dpOutput() const
return 0;
}
-sp<AudioOutputDescriptor> AudioOutputCollection::getPrimaryOutput() const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getPrimaryOutput() const
{
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
return outputDesc;
}
@@ -278,26 +419,26 @@ sp<AudioOutputDescriptor> AudioOutputCollection::getPrimaryOutput() const
return NULL;
}
-sp<AudioOutputDescriptor> AudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
+sp<SwAudioOutputDescriptor> SwAudioOutputCollection::getOutputFromId(audio_port_handle_t id) const
{
- sp<AudioOutputDescriptor> outputDesc = NULL;
+ sp<SwAudioOutputDescriptor> outputDesc = NULL;
for (size_t i = 0; i < size(); i++) {
outputDesc = valueAt(i);
- if (outputDesc->mId == id) {
+ if (outputDesc->getId() == id) {
break;
}
}
return outputDesc;
}
-bool AudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
+bool SwAudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const
{
for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) {
if (s == (size_t) streamToIgnore) {
continue;
}
for (size_t i = 0; i < size(); i++) {
- const sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
if (outputDesc->mRefCount[s] != 0) {
return true;
}
@@ -306,15 +447,15 @@ bool AudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore
return false;
}
-audio_devices_t AudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
+audio_devices_t SwAudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const
{
- sp<AudioOutputDescriptor> outputDesc = valueFor(handle);
+ sp<SwAudioOutputDescriptor> outputDesc = valueFor(handle);
audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types();
return devices;
}
-status_t AudioOutputCollection::dump(int fd) const
+status_t SwAudioOutputCollection::dump(int fd) const
{
const size_t SIZE = 256;
char buffer[SIZE];
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
index 3a317fa..a06d867 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp
@@ -54,8 +54,8 @@ status_t AudioPatch::dump(int fd, int spaces, int index) const
for (size_t i = 0; i < mPatch.num_sources; i++) {
if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) {
snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
- mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mPatch.sources[i].ext.device.type));
} else {
snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
@@ -68,8 +68,8 @@ status_t AudioPatch::dump(int fd, int spaces, int index) const
for (size_t i = 0; i < mPatch.num_sinks; i++) {
if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) {
snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "",
- mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mPatch.sinks[i].ext.device.type));
} else {
snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "",
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 84a53eb..77fc0b9 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -26,12 +26,12 @@
namespace android {
-void AudioPolicyMix::setOutput(sp<AudioOutputDescriptor> &output)
+void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
{
mOutput = output;
}
-const sp<AudioOutputDescriptor> &AudioPolicyMix::getOutput() const
+const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
{
return mOutput;
}
@@ -46,9 +46,9 @@ void AudioPolicyMix::setMix(AudioMix &mix)
mMix = mix;
}
-android::AudioMix &AudioPolicyMix::getMix()
+android::AudioMix *AudioPolicyMix::getMix()
{
- return mMix;
+ return &mMix;
}
status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix)
@@ -88,7 +88,7 @@ status_t AudioPolicyMixCollection::getAudioPolicyMix(String8 address,
return NO_ERROR;
}
-void AudioPolicyMixCollection::closeOutput(sp<AudioOutputDescriptor> &desc)
+void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
@@ -99,40 +99,40 @@ void AudioPolicyMixCollection::closeOutput(sp<AudioOutputDescriptor> &desc)
}
status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes,
- sp<AudioOutputDescriptor> &desc)
+ sp<SwAudioOutputDescriptor> &desc)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
- AudioMix mix = policyMix->getMix();
-
- if (mix.mMixType == MIX_TYPE_PLAYERS) {
- for (size_t j = 0; j < mix.mCriteria.size(); j++) {
- if ((RULE_MATCH_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mUsage == attributes.usage) ||
- (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mUsage != attributes.usage)) {
+ AudioMix *mix = policyMix->getMix();
+
+ if (mix->mMixType == MIX_TYPE_PLAYERS) {
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ if ((RULE_MATCH_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mUsage == attributes.usage) ||
+ (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mUsage != attributes.usage)) {
desc = policyMix->getOutput();
break;
}
if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
- mix.mRegistrationId.string(),
+ mix->mRegistrationId.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
desc = policyMix->getOutput();
break;
}
}
- } else if (mix.mMixType == MIX_TYPE_RECORDERS) {
+ } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
strncmp(attributes.tags + strlen("addr="),
- mix.mRegistrationId.string(),
+ mix->mRegistrationId.string(),
AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
desc = policyMix->getOutput();
}
}
if (desc != 0) {
- desc->mPolicyMix = &mix;
+ desc->mPolicyMix = mix;
return NO_ERROR;
}
}
@@ -144,19 +144,19 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so
AudioMix **policyMix)
{
for (size_t i = 0; i < size(); i++) {
- AudioMix mix = valueAt(i)->getMix();
+ AudioMix *mix = valueAt(i)->getMix();
- if (mix.mMixType != MIX_TYPE_RECORDERS) {
+ if (mix->mMixType != MIX_TYPE_RECORDERS) {
continue;
}
- for (size_t j = 0; j < mix.mCriteria.size(); j++) {
- if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mSource == inputSource) ||
- (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule &&
- mix.mCriteria[j].mAttr.mSource != inputSource)) {
+ for (size_t j = 0; j < mix->mCriteria.size(); j++) {
+ if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mSource == inputSource) ||
+ (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
+ mix->mCriteria[j].mAttr.mSource != inputSource)) {
if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
if (policyMix != NULL) {
- *policyMix = &mix;
+ *policyMix = mix;
}
return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
}
@@ -167,7 +167,7 @@ audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_so
return AUDIO_DEVICE_NONE;
}
-status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix)
+status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
{
if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
return BAD_VALUE;
@@ -180,13 +180,13 @@ status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, A
return BAD_VALUE;
}
sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
- AudioMix mix = audioPolicyMix->getMix();
+ AudioMix *mix = audioPolicyMix->getMix();
- if (mix.mMixType != MIX_TYPE_PLAYERS) {
+ if (mix->mMixType != MIX_TYPE_PLAYERS) {
ALOGW("getInputForAttr() bad policy mix type for address %s", address.string());
return BAD_VALUE;
}
- policyMix = &mix;
+ *policyMix = mix;
return NO_ERROR;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 46a119e..f3978ec 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "APM::AudioPort"
//#define LOG_NDEBUG 0
-
+#include <media/AudioResamplerPublic.h>
#include "AudioPort.h"
#include "HwModule.h"
#include "AudioGain.h"
@@ -31,8 +31,8 @@ int32_t volatile AudioPort::mNextUniqueId = 1;
// --- AudioPort class implementation
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), mFlags(0), mId(0)
+ audio_port_role_t role) :
+ mName(name), mType(type), mRole(role), mFlags(0)
{
mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) ||
((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK));
@@ -40,7 +40,6 @@ AudioPort::AudioPort(const String8& name, audio_port_type_t type,
void AudioPort::attach(const sp<HwModule>& module)
{
- mId = getNextUniqueId();
mModule = module;
}
@@ -51,9 +50,28 @@ audio_port_handle_t AudioPort::getNextUniqueId()
audio_module_handle_t AudioPort::getModuleHandle() const
{
+ if (mModule == 0) {
+ return 0;
+ }
return mModule->mHandle;
}
+uint32_t AudioPort::getModuleVersion() const
+{
+ if (mModule == 0) {
+ return 0;
+ }
+ return mModule->mHalVersion;
+}
+
+const char *AudioPort::getModuleName() const
+{
+ if (mModule == 0) {
+ return "";
+ }
+ return mModule->mName;
+}
+
void AudioPort::toAudioPort(struct audio_port *port) const
{
port->role = mRole;
@@ -198,6 +216,7 @@ void AudioPort::loadFormats(char *name)
}
str = strtok(NULL, "|");
}
+ mFormats.sort(compareFormatsGoodToBad);
}
void AudioPort::loadInChannels(char *name)
@@ -340,6 +359,9 @@ status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate,
uint32_t *updatedSamplingRate) const
{
if (mSamplingRates.isEmpty()) {
+ if (updatedSamplingRate != NULL) {
+ *updatedSamplingRate = samplingRate;
+ }
return NO_ERROR;
}
@@ -369,16 +391,11 @@ status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate,
}
}
}
- // This uses hard-coded knowledge about AudioFlinger resampling ratios.
- // TODO Move these assumptions out.
- static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs
- static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur
- // due to approximation by an int32_t of the
- // phase increments
+
// Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
if (minAbove >= 0) {
candidate = mSamplingRates[minAbove];
- if (candidate / kMaxDownSampleRatio <= samplingRate) {
+ if (candidate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = candidate;
}
@@ -388,7 +405,7 @@ status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate,
// But if we have to up-sample from a lower sampling rate, that's OK.
if (maxBelow >= 0) {
candidate = mSamplingRates[maxBelow];
- if (candidate * kMaxUpSampleRatio >= samplingRate) {
+ if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = candidate;
}
@@ -413,10 +430,13 @@ status_t AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) cons
return BAD_VALUE;
}
-status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
- const
+status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask) const
{
if (mChannelMasks.isEmpty()) {
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = channelMask;
+ }
return NO_ERROR;
}
@@ -425,6 +445,9 @@ status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
// FIXME Does not handle multi-channel automatic conversions yet
audio_channel_mask_t supported = mChannelMasks[i];
if (supported == channelMask) {
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = channelMask;
+ }
return NO_ERROR;
}
if (isRecordThread) {
@@ -434,6 +457,9 @@ status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
&& channelMask == AUDIO_CHANNEL_IN_MONO) ||
(supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
|| channelMask == AUDIO_CHANNEL_IN_STEREO))) {
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = supported;
+ }
return NO_ERROR;
}
}
@@ -441,7 +467,7 @@ status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask)
return BAD_VALUE;
}
-status_t AudioPort::checkFormat(audio_format_t format) const
+status_t AudioPort::checkExactFormat(audio_format_t format) const
{
if (mFormats.isEmpty()) {
return NO_ERROR;
@@ -455,6 +481,33 @@ status_t AudioPort::checkFormat(audio_format_t format) const
return BAD_VALUE;
}
+status_t AudioPort::checkCompatibleFormat(audio_format_t format, audio_format_t *updatedFormat)
+ const
+{
+ if (mFormats.isEmpty()) {
+ if (updatedFormat != NULL) {
+ *updatedFormat = format;
+ }
+ return NO_ERROR;
+ }
+
+ const bool checkInexact = // when port is input and format is linear pcm
+ mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK
+ && audio_is_linear_pcm(format);
+
+ for (size_t i = 0; i < mFormats.size(); ++i) {
+ if (mFormats[i] == format ||
+ (checkInexact && audio_is_linear_pcm(mFormats[i]))) {
+ // for inexact checks we take the first linear pcm format since
+ // mFormats is sorted from best PCM format to worst PCM format.
+ if (updatedFormat != NULL) {
+ *updatedFormat = mFormats[i];
+ }
+ return NO_ERROR;
+ }
+ }
+ return BAD_VALUE;
+}
uint32_t AudioPort::pickSamplingRate() const
{
@@ -629,7 +682,7 @@ void AudioPort::dump(int fd, int spaces) const
char buffer[SIZE];
String8 result;
- if (mName.size() != 0) {
+ if (mName.length() != 0) {
snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
result.append(buffer);
}
@@ -687,13 +740,16 @@ void AudioPort::dump(int fd, int spaces) const
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);
}
}
}
+void AudioPort::log(const char* indent) const
+{
+ ALOGI("%s Port[nm:%s, type:%d, role:%d]", indent, mName.string(), mType, mRole);
+}
// --- AudioPortConfig class implementation
@@ -735,7 +791,7 @@ status_t AudioPortConfig::applyAudioPortConfig(
mChannelMask = config->channel_mask;
}
if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
- status = audioport->checkFormat(config->format);
+ status = audioport->checkExactFormat(config->format);
if (status != NO_ERROR) {
goto exit;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
index fe5bc5f..9ab1d61 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp
@@ -113,8 +113,8 @@ audio_devices_t ConfigParsingUtils::parseDeviceNames(char *name)
char *devName = strtok(name, "|");
while (devName != NULL) {
if (strlen(devName) != 0) {
- device |= stringToEnum(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ device |= stringToEnum(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
devName);
}
devName = strtok(NULL, "|");
@@ -224,8 +224,8 @@ void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& modul
availableOutputDevices.types());
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
audio_devices_t device = (audio_devices_t)stringToEnum(
- sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
(char *)node->value);
if (device != AUDIO_DEVICE_NONE) {
defaultOutputDevice = new DeviceDescriptor(String8("default-output"), device);
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 7df7d75..9573583 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -29,13 +29,23 @@ String8 DeviceDescriptor::emptyNameStr = String8("");
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),
- mAddress(""), mDeviceType(type)
+ AUDIO_PORT_ROLE_SOURCE),
+ mAddress(""), mDeviceType(type), mId(0)
{
}
+audio_port_handle_t DeviceDescriptor::getId() const
+{
+ return mId;
+}
+
+void DeviceDescriptor::attach(const sp<HwModule>& module)
+{
+ AudioPort::attach(module);
+ mId = getNextUniqueId();
+}
+
bool DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const
{
// Devices are considered equal if they:
@@ -139,11 +149,14 @@ void DeviceVector::loadDevicesFromName(char *name,
char *devName = strtok(name, "|");
while (devName != NULL) {
if (strlen(devName) != 0) {
- audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
devName);
if (type != AUDIO_DEVICE_NONE) {
- sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type);
+ devName = (char *)ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
+ ARRAY_SIZE(sDeviceNameToEnumTable),
+ type);
+ sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(devName), type);
if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX ||
type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) {
dev->mAddress = String8("0");
@@ -183,7 +196,7 @@ sp<DeviceDescriptor> DeviceVector::getDeviceFromId(audio_port_handle_t id) const
{
sp<DeviceDescriptor> device;
for (size_t i = 0; i < size(); i++) {
- if (itemAt(i)->getHandle() == id) {
+ if (itemAt(i)->getId() == id) {
device = itemAt(i);
break;
}
@@ -303,8 +316,8 @@ status_t DeviceDescriptor::dump(int fd, int spaces, int index) const
result.append(buffer);
}
snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
- ConfigParsingUtils::enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
+ ConfigParsingUtils::enumToString(sDeviceTypeToEnumTable,
+ ARRAY_SIZE(sDeviceTypeToEnumTable),
mDeviceType));
result.append(buffer);
if (mAddress.size() != 0) {
@@ -317,4 +330,16 @@ status_t DeviceDescriptor::dump(int fd, int spaces, int index) const
return NO_ERROR;
}
+void DeviceDescriptor::log() const
+{
+ ALOGI("Device id:%d type:0x%X:%s, addr:%s",
+ mId,
+ mDeviceType,
+ ConfigParsingUtils::enumToString(
+ sDeviceNameToEnumTable, ARRAY_SIZE(sDeviceNameToEnumTable), mDeviceType),
+ mAddress.string());
+
+ AudioPort::log(" ");
+}
+
}; // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 0097d69..e955447 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -48,7 +48,7 @@ status_t HwModule::loadInput(cnode *root)
{
cnode *node = root->first_child;
- sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK);
while (node) {
if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -83,6 +83,7 @@ status_t HwModule::loadInput(cnode *root)
ALOGV("loadInput() adding input Supported Devices %04x",
profile->mSupportedDevices.types());
+ profile->attach(this);
mInputProfiles.add(profile);
return NO_ERROR;
} else {
@@ -94,7 +95,7 @@ status_t HwModule::loadOutput(cnode *root)
{
cnode *node = root->first_child;
- sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE);
while (node) {
if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
@@ -128,7 +129,7 @@ status_t HwModule::loadOutput(cnode *root)
ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
profile->mSupportedDevices.types(), profile->mFlags);
-
+ profile->attach(this);
mOutputProfiles.add(profile);
return NO_ERROR;
} else {
@@ -154,7 +155,6 @@ status_t HwModule::loadDevice(cnode *root)
return BAD_VALUE;
}
sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
- deviceDesc->mModule = this;
node = root->first_child;
while (node) {
@@ -183,7 +183,7 @@ status_t HwModule::loadDevice(cnode *root)
status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config,
audio_devices_t device, String8 address)
{
- sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this);
+ sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE);
profile->mSamplingRates.add(config->sample_rate);
profile->mChannelMasks.add(config->channel_mask);
@@ -193,6 +193,7 @@ status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config,
devDesc->mAddress = address;
profile->mSupportedDevices.add(devDesc);
+ profile->attach(this);
mOutputProfiles.add(profile);
return NO_ERROR;
@@ -213,7 +214,7 @@ status_t HwModule::removeOutputProfile(String8 name)
status_t HwModule::addInputProfile(String8 name, const audio_config_t *config,
audio_devices_t device, String8 address)
{
- sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this);
+ sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK);
profile->mSamplingRates.add(config->sample_rate);
profile->mChannelMasks.add(config->channel_mask);
@@ -225,6 +226,7 @@ status_t HwModule::addInputProfile(String8 name, const audio_config_t *config,
ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask);
+ profile->attach(this);
mInputProfiles.add(profile);
return NO_ERROR;
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index 376dd22..7b6d51d 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -23,9 +23,8 @@
namespace android {
-IOProfile::IOProfile(const String8& name, audio_port_role_t role,
- const sp<HwModule>& module)
- : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module)
+IOProfile::IOProfile(const String8& name, audio_port_role_t role)
+ : AudioPort(name, AUDIO_PORT_TYPE_MIX, role)
{
}
@@ -41,7 +40,9 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device,
uint32_t samplingRate,
uint32_t *updatedSamplingRate,
audio_format_t format,
+ audio_format_t *updatedFormat,
audio_channel_mask_t channelMask,
+ audio_channel_mask_t *updatedChannelMask,
uint32_t flags) const
{
const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE;
@@ -72,7 +73,14 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device,
return false;
}
- if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) {
+ if (!audio_is_valid_format(format)) {
+ return false;
+ }
+ if (isPlaybackThread && checkExactFormat(format) != NO_ERROR) {
+ return false;
+ }
+ audio_format_t myUpdatedFormat = format;
+ if (isRecordThread && checkCompatibleFormat(format, &myUpdatedFormat) != NO_ERROR) {
return false;
}
@@ -80,8 +88,9 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device,
checkExactChannelMask(channelMask) != NO_ERROR)) {
return false;
}
+ audio_channel_mask_t myUpdatedChannelMask = channelMask;
if (isRecordThread && (!audio_is_input_channel(channelMask) ||
- checkCompatibleChannelMask(channelMask) != NO_ERROR)) {
+ checkCompatibleChannelMask(channelMask, &myUpdatedChannelMask) != NO_ERROR)) {
return false;
}
@@ -100,6 +109,12 @@ bool IOProfile::isCompatibleProfile(audio_devices_t device,
if (updatedSamplingRate != NULL) {
*updatedSamplingRate = myUpdatedSamplingRate;
}
+ if (updatedFormat != NULL) {
+ *updatedFormat = myUpdatedFormat;
+ }
+ if (updatedChannelMask != NULL) {
+ *updatedChannelMask = myUpdatedChannelMask;
+ }
return true;
}
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index eadaa77..db0573f 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -134,16 +134,16 @@ public:
audio_policy_dev_state_t state) = 0;
/**
- * Translate a volume index given by the UI to an amplification value for a stream type
+ * Translate a volume index given by the UI to an amplification value in dB for a stream type
* and a device category.
*
* @param[in] deviceCategory for which the conversion is requested.
* @param[in] stream type for which the conversion is requested.
* @param[in] indexInUi index received from the UI to be translated.
*
- * @return amplification value matching the UI index for this given device and stream.
+ * @return amplification value in dB matching the UI index for this given device and stream.
*/
- virtual float volIndexToAmpl(Volume::device_category deviceCategory, audio_stream_type_t stream,
+ virtual float volIndexToDb(Volume::device_category deviceCategory, audio_stream_type_t stream,
int indexInUi) = 0;
/**
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
index 4f5427e..6d43df2 100755
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h
@@ -43,7 +43,7 @@ public:
virtual const AudioPolicyMixCollection &getAudioPolicyMixCollection() const = 0;
- virtual const AudioOutputCollection &getOutputs() const = 0;
+ virtual const SwAudioOutputCollection &getOutputs() const = 0;
virtual const AudioInputCollection &getInputs() const = 0;
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 1fd3341..50f1609 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -63,13 +63,14 @@ status_t Engine::initCheck()
return (mApmObserver != NULL) ? NO_ERROR : NO_INIT;
}
-float Engine::volIndexToAmpl(Volume::device_category category, audio_stream_type_t streamType,
+float Engine::volIndexToDb(Volume::device_category category, audio_stream_type_t streamType,
int indexInUi)
{
const StreamDescriptor &streamDesc = mApmObserver->getStreamDescriptors().valueAt(streamType);
- return Gains::volIndexToAmpl(category, streamDesc, indexInUi);
+ return Gains::volIndexToDb(category, streamDesc, indexInUi);
}
+
status_t Engine::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax)
{
ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
@@ -243,7 +244,7 @@ routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream)
routing_strategy Engine::getStrategyForUsage(audio_usage_t usage)
{
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
// usage to strategy mapping
switch (usage) {
@@ -291,7 +292,7 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
uint32_t device = AUDIO_DEVICE_NONE;
uint32_t availableOutputDevicesType = availableOutputDevices.types();
@@ -358,7 +359,7 @@ audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
if (((availableInputDevices.types() &
AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) ||
(((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
- (primaryOutput->getAudioPort()->mModule->mHalVersion <
+ (primaryOutput->getAudioPort()->getModuleVersion() <
AUDIO_DEVICE_API_VERSION_3_0))) {
availableOutputDevicesType = availPrimaryOutputDevices;
}
@@ -582,7 +583,7 @@ audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) cons
{
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
- const AudioOutputCollection &outputs = mApmObserver->getOutputs();
+ const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
uint32_t device = AUDIO_DEVICE_NONE;
diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h
index f44556c..56a4748 100755
--- a/services/audiopolicy/enginedefault/src/Engine.h
+++ b/services/audiopolicy/enginedefault/src/Engine.h
@@ -101,10 +101,10 @@ private:
{
return mPolicyEngine->initializeVolumeCurves(isSpeakerDrcEnabled);
}
- virtual float volIndexToAmpl(Volume::device_category deviceCategory,
+ virtual float volIndexToDb(Volume::device_category deviceCategory,
audio_stream_type_t stream,int indexInUi)
{
- return mPolicyEngine->volIndexToAmpl(deviceCategory, stream, indexInUi);
+ return mPolicyEngine->volIndexToDb(deviceCategory, stream, indexInUi);
}
private:
Engine *mPolicyEngine;
@@ -141,7 +141,7 @@ private:
audio_devices_t getDeviceForStrategy(routing_strategy strategy) const;
audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const;
- float volIndexToAmpl(Volume::device_category category,
+ float volIndexToDb(Volume::device_category category,
audio_stream_type_t stream, int indexInUi);
status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax);
void initializeVolumeCurves(bool isSpeakerDrcEnabled);
diff --git a/services/audiopolicy/enginedefault/src/Gains.cpp b/services/audiopolicy/enginedefault/src/Gains.cpp
index a684fdd..78f2909 100644
--- a/services/audiopolicy/enginedefault/src/Gains.cpp
+++ b/services/audiopolicy/enginedefault/src/Gains.cpp
@@ -197,10 +197,10 @@ const VolumeCurvePoint *Gains::sVolumeProfiles[AUDIO_STREAM_CNT]
};
//static
-float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
- int indexInUi)
+float Gains::volIndexToDb(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi)
{
- Volume::device_category deviceCategory = Volume::getDeviceCategory(device);
const VolumeCurvePoint *curve = streamDesc.getVolumeCurvePoint(deviceCategory);
// the volume index in the UI is relative to the min and max volume indices for this stream type
@@ -212,7 +212,7 @@ float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& stre
// find what part of the curve this index volume belongs to, or if it's out of bounds
int segment = 0;
if (volIdx < curve[Volume::VOLMIN].mIndex) { // out of bounds
- return 0.0f;
+ return VOLUME_MIN_DB;
} else if (volIdx < curve[Volume::VOLKNEE1].mIndex) {
segment = 0;
} else if (volIdx < curve[Volume::VOLKNEE2].mIndex) {
@@ -220,7 +220,7 @@ float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& stre
} else if (volIdx <= curve[Volume::VOLMAX].mIndex) {
segment = 2;
} else { // out of bounds
- return 1.0f;
+ return 0.0f;
}
// linear interpolation in the attenuation table in dB
@@ -231,17 +231,25 @@ float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& stre
((float)(curve[segment+1].mIndex -
curve[segment].mIndex)) );
- float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
-
- ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
+ ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
curve[segment].mIndex, volIdx,
curve[segment+1].mIndex,
curve[segment].mDBAttenuation,
decibels,
- curve[segment+1].mDBAttenuation,
- amplification);
+ curve[segment+1].mDBAttenuation);
+
+ return decibels;
+}
- return amplification;
+
+//static
+float Gains::volIndexToAmpl(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi)
+{
+ return Volume::DbToAmpl(volIndexToDb(deviceCategory, streamDesc, indexInUi));
}
+
+
}; // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Gains.h b/services/audiopolicy/enginedefault/src/Gains.h
index b5601ca..7620b7d 100644
--- a/services/audiopolicy/enginedefault/src/Gains.h
+++ b/services/audiopolicy/enginedefault/src/Gains.h
@@ -29,8 +29,13 @@ class StreamDescriptor;
class Gains
{
public :
- static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
- int indexInUi);
+ static float volIndexToAmpl(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi);
+
+ static float volIndexToDb(Volume::device_category deviceCategory,
+ const StreamDescriptor& streamDesc,
+ int indexInUi);
// default volume curve
static const VolumeCurvePoint sDefaultVolumeCurve[Volume::VOLCNT];
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 797a2b4..ba9f996 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -157,7 +157,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
// outputs must be closed after checkOutputForAllStrategies() is executed
if (!outputs.isEmpty()) {
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> 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) ||
@@ -176,18 +176,17 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device,
updateCallRouting(newDevice);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t output = mOutputs.keyAt(i);
- if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
- audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i),
- true /*fromCache*/);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
+ audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
// do not force device change on duplicated output because if device is 0, it will
// also force a device 0 for the two outputs it is duplicated to which may override
// a valid device selection on those outputs.
- bool force = !mOutputs.valueAt(i)->isDuplicated()
+ bool force = !desc->isDuplicated()
&& (!device_distinguishes_on_address(device)
// always force when disconnecting (a non-duplicated device)
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
- setOutputDevice(output, newDevice, force, 0);
+ setOutputDevice(desc, newDevice, force, 0);
}
}
@@ -349,10 +348,11 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs
AUDIO_OUTPUT_FLAG_NONE,
AUDIO_FORMAT_INVALID);
if (output != AUDIO_IO_HANDLE_NONE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
ALOG_ASSERT(!outputDesc->isDuplicated(),
"updateCallRouting() RX device output is duplicated");
outputDesc->toAudioPortConfig(&patch.sources[1]);
+ patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
patch.num_sources = 2;
}
@@ -395,6 +395,7 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs
ALOG_ASSERT(!outputDesc->isDuplicated(),
"updateCallRouting() RX device output is duplicated");
outputDesc->toAudioPortConfig(&patch.sources[1]);
+ patch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
patch.num_sources = 2;
}
@@ -448,13 +449,13 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state)
checkOutputForAllStrategies();
updateDevicesAndOutputs();
- sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<SwAudioOutputDescriptor> hwOutputDesc = mPrimaryOutput;
int delayMs = 0;
if (isStateInCall(state)) {
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> 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.
@@ -464,14 +465,14 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state)
isStrategyActive(desc, STRATEGY_SONIFICATION,
SONIFICATION_HEADSET_MUSIC_DELAY,
sysTime)) &&
- (delayMs < (int)desc->mLatency*2)) {
- delayMs = desc->mLatency*2;
+ (delayMs < (int)desc->latency()*2)) {
+ delayMs = desc->latency()*2;
}
- setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i));
- setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+ setStrategyMute(STRATEGY_MEDIA, true, desc);
+ setStrategyMute(STRATEGY_MEDIA, false, desc, MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/));
- setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i));
- setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+ setStrategyMute(STRATEGY_SONIFICATION, true, desc);
+ setStrategyMute(STRATEGY_SONIFICATION, false, desc, MUTE_TIME_MS,
getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/));
}
}
@@ -547,13 +548,13 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage,
updateCallRouting(newDevice);
}
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t output = mOutputs.keyAt(i);
- audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
- if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
- setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);
+ if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
+ setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));
}
if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
- applyStreamVolumes(output, newDevice, 0, true);
+ applyStreamVolumes(outputDesc, newDevice, 0, true);
}
}
@@ -584,8 +585,10 @@ sp<IOProfile> AudioPolicyManager::getProfileForDirectOutput(
}
for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
- bool found = profile->isCompatibleProfile(device, String8(""), samplingRate,
- NULL /*updatedSamplingRate*/, format, channelMask,
+ bool found = profile->isCompatibleProfile(device, String8(""),
+ samplingRate, NULL /*updatedSamplingRate*/,
+ format, NULL /*updatedFormat*/,
+ channelMask, NULL /*updatedChannelMask*/,
flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ?
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT);
if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) {
@@ -621,6 +624,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
audio_attributes_t attributes;
@@ -639,7 +643,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
}
stream_type_to_audio_attributes(*stream, &attributes);
}
- sp<AudioOutputDescriptor> desc;
+ sp<SwAudioOutputDescriptor> desc;
if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_is_linear_pcm(format)) {
@@ -675,6 +679,17 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
if (*output == AUDIO_IO_HANDLE_NONE) {
return INVALID_OPERATION;
}
+
+ // Explicit routing?
+ sp<DeviceDescriptor> deviceDesc;
+
+ for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
+ if (mAvailableOutputDevices[i]->getId() == selectedDeviceId) {
+ deviceDesc = mAvailableOutputDevices[i];
+ break;
+ }
+ }
+ mOutputRoutes.addRoute(session, *stream, deviceDesc);
return NO_ERROR;
}
@@ -699,7 +714,8 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
if (mTestOutputs[mCurOutput] == 0) {
ALOGV("getOutput() opening test output");
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+ sp<AudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(NULL,
+ mpClientInterface);
outputDesc->mDevice = mTestDevice;
outputDesc->mLatency = mTestLatencyMs;
outputDesc->mFlags =
@@ -775,10 +791,10 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
}
if (profile != 0) {
- sp<AudioOutputDescriptor> outputDesc = NULL;
+ sp<SwAudioOutputDescriptor> outputDesc = NULL;
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (!desc->isDuplicated() && (profile == desc->mProfile)) {
outputDesc = desc;
// reuse direct output if currently open and configured with same parameters
@@ -795,7 +811,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
if (outputDesc != NULL) {
closeOutput(outputDesc->mIoHandle);
}
- outputDesc = new AudioOutputDescriptor(profile);
+ outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);
outputDesc->mDevice = device;
outputDesc->mLatency = 0;
outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
@@ -806,7 +822,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
if (offloadInfo != NULL) {
config.offload_info = *offloadInfo;
}
- status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&outputDesc->mDevice,
@@ -856,7 +872,6 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice(
}
non_direct_output:
-
// ignoring channel mask due to downmix capability in mixer
// open a non direct output
@@ -874,7 +889,7 @@ non_direct_output:
ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
"format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
- ALOGV("getOutput() returns output %d", output);
+ ALOGV(" getOutputForDevice() returns output %d", output);
return output;
}
@@ -902,7 +917,7 @@ audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_h
audio_io_handle_t outputPrimary = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
if (!outputDesc->isDuplicated()) {
// if a valid format is specified, skip output if not compatible
if (format != AUDIO_FORMAT_INVALID) {
@@ -941,15 +956,59 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
audio_session_t session)
{
- ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
+ ALOGV("startOutput() output %d, stream %d, session %d",
+ output, stream, session);
ssize_t index = mOutputs.indexOfKey(output);
if (index < 0) {
ALOGW("startOutput() unknown output %d", output);
return BAD_VALUE;
}
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+
+ audio_devices_t newDevice;
+ if (outputDesc->mPolicyMix != NULL) {
+ newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ } else {
+ newDevice = AUDIO_DEVICE_NONE;
+ }
+
+ uint32_t delayMs = 0;
+
+ // Routing?
+ mOutputRoutes.incRouteActivity(session);
+
+ status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+
+ if (status != NO_ERROR) {
+ mOutputRoutes.decRouteActivity(session);
+ }
+ // Automatically enable the remote submix input when output is started on a re routing mix
+ // of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ outputDesc->mPolicyMix->mRegistrationId,
+ "remote-submix");
+ }
+
+ if (delayMs != 0) {
+ usleep(delayMs * 1000);
+ }
+
+ return status;
+}
+
+status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t *delayMs)
+{
// cannot start playback of STREAM_TTS if any other output is being used
uint32_t beaconMuteLatency = 0;
+
+ *delayMs = 0;
if (stream == AUDIO_STREAM_TTS) {
ALOGV("\t found BEACON stream");
if (mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -962,8 +1021,6 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
}
- 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
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
@@ -971,11 +1028,8 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
if (outputDesc->mRefCount[stream] == 1) {
// starting an output being rerouted?
- audio_devices_t newDevice;
- if (outputDesc->mPolicyMix != NULL) {
- newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
- } else {
- newDevice = getNewOutputDevice(output, false /*fromCache*/);
+ if (device == AUDIO_DEVICE_NONE) {
+ device = getNewOutputDevice(outputDesc, false /*fromCache*/);
}
routing_strategy strategy = getStrategy(stream);
bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
@@ -991,7 +1045,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
// In this case, the audio HAL must receive the new device selection so that it can
// change the device currently selected by the other active output.
if (outputDesc->sharesHwModuleWith(desc) &&
- desc->device() != newDevice) {
+ desc->device() != device) {
force = true;
}
// wait for audio on other active outputs to be presented when starting
@@ -1003,7 +1057,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
}
}
}
- uint32_t muteWaitMs = setOutputDevice(output, newDevice, force);
+ uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force);
// handle special case for sonification while in call
if (isInCall()) {
@@ -1012,32 +1066,18 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output,
// apply volume rules for current stream and device if necessary
checkAndSetVolume(stream,
- mStreams[stream].getVolumeIndex(newDevice),
- output,
- newDevice);
+ mStreams.valueFor(stream).getVolumeIndex(device),
+ outputDesc,
+ device);
// update the outputs if starting an output with a stream that can affect notification
// routing
handleNotificationRoutingForStream(stream);
- // Automatically enable the remote submix input when output is started on a re routing mix
- // of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- outputDesc->mPolicyMix->mRegistrationId,
- "remote-submix");
- }
-
// force reevaluating accessibility routing when ringtone or alarm starts
if (strategy == STRATEGY_SONIFICATION) {
mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY);
}
-
- if (waitMs > muteWaitMs) {
- usleep((waitMs - muteWaitMs) * 2 * 1000);
- }
}
return NO_ERROR;
}
@@ -1054,8 +1094,32 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output,
return BAD_VALUE;
}
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+
+ if (outputDesc->mRefCount[stream] == 1) {
+ // Automatically disable the remote submix input when output is stopped on a
+ // re routing mix of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+ outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ outputDesc->mPolicyMix->mRegistrationId,
+ "remote-submix");
+ }
+ }
+
+ // Routing?
+ if (outputDesc->mRefCount[stream] > 0) {
+ mOutputRoutes.decRouteActivity(session);
+ }
+
+ return stopSource(outputDesc, stream);
+}
+status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream)
+{
// always handle stream stop, check which stream type is stopping
handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
@@ -1067,41 +1131,31 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output,
if (outputDesc->mRefCount[stream] > 0) {
// decrement usage count of this stream on the output
outputDesc->changeRefCount(stream, -1);
+
// store time at which the stream was stopped - see isStreamActive()
if (outputDesc->mRefCount[stream] == 0) {
- // Automatically disable the remote submix input when output is stopped on a
- // re routing mix of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(outputDesc->mDevice) &&
- outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- outputDesc->mPolicyMix->mRegistrationId,
- "remote-submix");
- }
-
outputDesc->mStopTime[stream] = systemTime();
- audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
+ audio_devices_t newDevice = getNewOutputDevice(outputDesc, 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
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
- setOutputDevice(output, newDevice, false, outputDesc->mLatency*2);
+ setOutputDevice(outputDesc, newDevice, false, outputDesc->latency()*2);
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
for (size_t i = 0; i < mOutputs.size(); i++) {
audio_io_handle_t curOutput = mOutputs.keyAt(i);
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if (curOutput != output &&
+ if (desc != outputDesc &&
desc->isActive() &&
outputDesc->sharesHwModuleWith(desc) &&
(newDevice != desc->device())) {
- setOutputDevice(curOutput,
- getNewOutputDevice(curOutput, false /*fromCache*/),
+ setOutputDevice(desc,
+ getNewOutputDevice(desc, false /*fromCache*/),
true,
- outputDesc->mLatency*2);
+ outputDesc->latency()*2);
}
}
// update the outputs if stopping one with a stream that can affect notification routing
@@ -1109,7 +1163,7 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output,
}
return NO_ERROR;
} else {
- ALOGW("stopOutput() refcount is already 0 for output %d", output);
+ ALOGW("stopOutput() refcount is already 0");
return INVALID_OPERATION;
}
}
@@ -1138,7 +1192,10 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output,
}
#endif //AUDIO_POLICY_TEST
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index);
+ // Routing
+ mOutputRoutes.removeRoute(session);
+
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(index);
if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (desc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1150,8 +1207,9 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output,
// If effects where present on the output, audioflinger moved them to the primary
// output by default: move them back to the appropriate output.
audio_io_handle_t dstOutput = getOutputForEffect();
- if (dstOutput != mPrimaryOutput) {
- mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
+ if (dstOutput != mPrimaryOutput->mIoHandle) {
+ mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX,
+ mPrimaryOutput->mIoHandle, dstOutput);
}
mpClientInterface->onAudioPortListUpdate();
}
@@ -1189,7 +1247,7 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
- status_t ret = mPolicyMixes.getInputMixForAttr(*attr, policyMix);
+ status_t ret = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
if (ret != NO_ERROR) {
return ret;
}
@@ -1247,48 +1305,54 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
}
}
- sp<IOProfile> profile = getInputProfile(device, address,
- samplingRate, format, channelMask,
- flags);
- if (profile == 0) {
- //retry without flags
- audio_input_flags_t log_flags = flags;
- flags = AUDIO_INPUT_FLAG_NONE;
+ // find a compatible input profile (not necessarily identical in parameters)
+ sp<IOProfile> profile;
+ // samplingRate and flags may be updated by getInputProfile
+ uint32_t profileSamplingRate = samplingRate;
+ audio_format_t profileFormat = format;
+ audio_channel_mask_t profileChannelMask = channelMask;
+ audio_input_flags_t profileFlags = flags;
+ for (;;) {
profile = getInputProfile(device, address,
- samplingRate, format, channelMask,
- flags);
- if (profile == 0) {
+ profileSamplingRate, profileFormat, profileChannelMask,
+ profileFlags);
+ if (profile != 0) {
+ break; // success
+ } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
+ profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
+ } else { // fail
ALOGW("getInputForAttr() could not find profile for device 0x%X, samplingRate %u,"
"format %#x, channelMask 0x%X, flags %#x",
- device, samplingRate, format, channelMask, log_flags);
+ device, samplingRate, format, channelMask, flags);
return BAD_VALUE;
}
}
- if (profile->mModule->mHandle == 0) {
- ALOGE("getInputForAttr(): HW module %s not opened", profile->mModule->mName);
+ if (profile->getModuleHandle() == 0) {
+ ALOGE("getInputForAttr(): HW module %s not opened", profile->getModuleName());
return NO_INIT;
}
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
- config.sample_rate = samplingRate;
- config.channel_mask = channelMask;
- config.format = format;
+ config.sample_rate = profileSamplingRate;
+ config.channel_mask = profileChannelMask;
+ config.format = profileFormat;
- status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
input,
&config,
&device,
address,
halInputSource,
- flags);
+ profileFlags);
// only accept input with the exact requested set of parameters
if (status != NO_ERROR || *input == AUDIO_IO_HANDLE_NONE ||
- (samplingRate != config.sample_rate) ||
- (format != config.format) ||
- (channelMask != config.channel_mask)) {
- ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d, channelMask %x",
+ (profileSamplingRate != config.sample_rate) ||
+ (profileFormat != config.format) ||
+ (profileChannelMask != config.channel_mask)) {
+ ALOGW("getInputForAttr() failed opening input: samplingRate %d, format %d,"
+ " channelMask %x",
samplingRate, format, channelMask);
if (*input != AUDIO_IO_HANDLE_NONE) {
mpClientInterface->closeInput(*input);
@@ -1300,15 +1364,15 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
inputDesc->mInputSource = inputSource;
inputDesc->mRefCount = 0;
inputDesc->mOpenRefCount = 1;
- inputDesc->mSamplingRate = samplingRate;
- inputDesc->mFormat = format;
- inputDesc->mChannelMask = channelMask;
+ inputDesc->mSamplingRate = profileSamplingRate;
+ inputDesc->mFormat = profileFormat;
+ inputDesc->mChannelMask = profileChannelMask;
inputDesc->mDevice = device;
inputDesc->mSessions.add(session);
inputDesc->mIsSoundTrigger = isSoundTrigger;
inputDesc->mPolicyMix = policyMix;
- ALOGV("getInputForAttr() returns input type = %d", inputType);
+ ALOGV("getInputForAttr() returns input type = %d", *inputType);
addInput(*input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
@@ -1505,8 +1569,8 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
audio_devices_t device)
{
- if ((index < mStreams[stream].getVolumeIndexMin()) ||
- (index > mStreams[stream].getVolumeIndexMax())) {
+ if ((index < mStreams.valueFor(stream).getVolumeIndexMin()) ||
+ (index > mStreams.valueFor(stream).getVolumeIndexMax())) {
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
@@ -1514,7 +1578,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
}
// Force max volume if stream cannot be muted
- if (!mStreams.canBeMuted(stream)) index = mStreams[stream].getVolumeIndexMax();
+ if (!mStreams.canBeMuted(stream)) index = mStreams.valueFor(stream).getVolumeIndexMax();
ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d",
stream, device, index);
@@ -1543,16 +1607,17 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
}
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_devices_t curDevice = Volume::getDeviceForVolume(mOutputs.valueAt(i)->device());
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) {
- status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
+ status_t volStatus = checkAndSetVolume(stream, index, desc, curDevice);
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) {
status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY,
- index, mOutputs.keyAt(i), curDevice);
+ index, desc, curDevice);
}
}
return status;
@@ -1575,7 +1640,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream,
}
device = Volume::getDeviceForVolume(device);
- *index = mStreams[stream].getVolumeIndex(device);
+ *index = mStreams.valueFor(stream).getVolumeIndex(device);
ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
return NO_ERROR;
}
@@ -1599,7 +1664,7 @@ audio_io_handle_t AudioPolicyManager::selectOutputForEffects(
audio_io_handle_t outputDeepBuffer = 0;
for (size_t i = 0; i < outputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+ sp<SwAudioOutputDescriptor> 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];
@@ -1653,6 +1718,16 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc,
return mEffects.registerEffect(desc, io, strategy, session, id);
}
+bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+ return mOutputs.isStreamActive(stream, inPastMs);
+}
+
+bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+ return mOutputs.isStreamActiveRemotely(stream, inPastMs);
+}
+
bool AudioPolicyManager::isSourceActive(audio_source_t source) const
{
for (size_t i = 0; i < mInputs.size(); i++) {
@@ -1803,7 +1878,7 @@ status_t AudioPolicyManager::dump(int fd)
snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
result.append(buffer);
- snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput);
+ snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput->mIoHandle);
result.append(buffer);
snprintf(buffer, SIZE, " Phone state: %d\n", mEngine->getPhoneState());
result.append(buffer);
@@ -2021,7 +2096,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
}
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
if (outputDesc == NULL) {
ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
return BAD_VALUE;
@@ -2055,9 +2130,12 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
patch->sources[0].sample_rate,
NULL, // updatedSamplingRate
patch->sources[0].format,
+ NULL, // updatedFormat
patch->sources[0].channel_mask,
+ NULL, // updatedChannelMask
AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
- ALOGV("createAudioPatch() profile not supported for device %08x", devDesc->type());
+ ALOGV("createAudioPatch() profile not supported for device %08x",
+ devDesc->type());
return INVALID_OPERATION;
}
devices.add(devDesc);
@@ -2069,7 +2147,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
// TODO: reconfigure output format and channels here
ALOGV("createAudioPatch() setting device %08x on output %d",
devices.types(), outputDesc->mIoHandle);
- setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
+ setOutputDevice(outputDesc, devices.types(), true, 0, handle);
index = mAudioPatches.indexOfKey(*handle);
if (index >= 0) {
if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2109,7 +2187,9 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
patch->sinks[0].sample_rate,
NULL, /*updatedSampleRate*/
patch->sinks[0].format,
+ NULL, /*updatedFormat*/
patch->sinks[0].channel_mask,
+ NULL, /*updatedChannelMask*/
// FIXME for the parameter type,
// and the NONE
(audio_output_flags_t)
@@ -2163,8 +2243,12 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
}
sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
- if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
- // only one sink supported when connected devices across HW modules
+ // create a software bridge in PatchPanel if:
+ // - source and sink devices are on differnt HW modules OR
+ // - audio HAL version is < 3.0
+ if ((srcDeviceDesc->getModuleHandle() != sinkDeviceDesc->getModuleHandle()) ||
+ (srcDeviceDesc->mModule->mHalVersion < AUDIO_DEVICE_API_VERSION_3_0)) {
+ // support only one sink device for now to simplify output selection logic
if (patch->num_sinks > 1) {
return INVALID_OPERATION;
}
@@ -2181,6 +2265,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
return INVALID_OPERATION;
}
outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
+ newPatch.sources[1].ext.mix.usecase.stream = AUDIO_STREAM_PATCH;
newPatch.num_sources = 2;
}
}
@@ -2242,14 +2327,14 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle,
struct audio_patch *patch = &patchDesc->mPatch;
patchDesc->mUid = mUidCached;
if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.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*/),
+ setOutputDevice(outputDesc,
+ getNewOutputDevice(outputDesc, true /*fromCache*/),
true,
0,
NULL);
@@ -2308,7 +2393,7 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *
sp<AudioPortConfig> audioPortConfig;
if (config->type == AUDIO_PORT_TYPE_MIX) {
if (config->role == AUDIO_PORT_ROLE_SOURCE) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
if (outputDesc == NULL) {
return BAD_VALUE;
}
@@ -2390,7 +2475,6 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPrimaryOutput((audio_io_handle_t)0),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
mSpeakerDrcEnabled(false),
@@ -2474,7 +2558,8 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
if ((profileType & outputDeviceTypes) == 0) {
continue;
}
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
+ sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+ mpClientInterface);
outputDesc->mDevice = profileType;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -2482,7 +2567,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
config.channel_mask = outputDesc->mChannelMask;
config.format = outputDesc->mFormat;
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle,
+ status_t status = mpClientInterface->openOutput(outProfile->getModuleHandle(),
&output,
&config,
&outputDesc->mDevice,
@@ -2510,10 +2595,10 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
}
if (mPrimaryOutput == 0 &&
outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
- mPrimaryOutput = output;
+ mPrimaryOutput = outputDesc;
}
addOutput(output, outputDesc);
- setOutputDevice(output,
+ setOutputDevice(outputDesc,
outputDesc->mDevice,
true);
}
@@ -2558,7 +2643,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
config.channel_mask = inputDesc->mChannelMask;
config.format = inputDesc->mFormat;
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(inProfile->getModuleHandle(),
&input,
&config,
&inputDesc->mDevice,
@@ -2620,7 +2705,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
if (mPrimaryOutput != 0) {
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
+ mpClientInterface->setParameters(mPrimaryOutput->mIoHandle, outputCmd.toString());
mTestDevice = AUDIO_DEVICE_OUT_SPEAKER;
mTestSamplingRate = 44100;
@@ -2760,20 +2845,21 @@ bool AudioPolicyManager::threadLoop()
if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
param.remove(String8("test_cmd_policy_reopen"));
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
- mpClientInterface->closeOutput(mPrimaryOutput);
+ mpClientInterface->closeOutput(mpClientInterface->closeOutput(mPrimaryOutput););
- audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle;
+ audio_module_handle_t moduleHandle = mPrimaryOutput->getModuleHandle();
- removeOutput(mPrimaryOutput);
- sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+ removeOutput(mPrimaryOutput->mIoHandle);
+ sp<SwAudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL,
+ mpClientInterface);
outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = outputDesc->mSamplingRate;
config.channel_mask = outputDesc->mChannelMask;
config.format = outputDesc->mFormat;
+ audio_io_handle_t handle;
status_t status = mpClientInterface->openOutput(moduleHandle,
- &mPrimaryOutput,
+ &handle,
&config,
&outputDesc->mDevice,
String8(""),
@@ -2787,10 +2873,11 @@ bool AudioPolicyManager::threadLoop()
outputDesc->mSamplingRate = config.sample_rate;
outputDesc->mChannelMask = config.channel_mask;
outputDesc->mFormat = config.format;
+ mPrimaryOutput = outputDesc;
AudioParameter outputCmd = AudioParameter();
outputCmd.addInt(String8("set_id"), 0);
- mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
- addOutput(mPrimaryOutput, outputDesc);
+ mpClientInterface->setParameters(handle, outputCmd.toString());
+ addOutput(handle, outputDesc);
}
}
@@ -2822,7 +2909,7 @@ int AudioPolicyManager::testOutputIndex(audio_io_handle_t output)
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc)
{
outputDesc->setIoHandle(output);
mOutputs.add(output, outputDesc);
@@ -2841,7 +2928,7 @@ void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescript
nextAudioPortGeneration();
}
-void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+void AudioPolicyManager::findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
const audio_devices_t device /*in*/,
const String8 address /*in*/,
SortedVector<audio_io_handle_t>& outputs /*out*/) {
@@ -2860,7 +2947,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
const String8 address)
{
audio_devices_t device = devDesc->type();
- sp<AudioOutputDescriptor> desc;
+ sp<SwAudioOutputDescriptor> desc;
// erase all current sample rates, formats and channel masks
devDesc->clearCapabilities();
@@ -2868,7 +2955,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
// first list already open outputs that can be routed to this device
for (size_t i = 0; i < mOutputs.size(); i++) {
desc = mOutputs.valueAt(i);
- if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) {
+ if (!desc->isDuplicated() && (desc->supportedDevices() & device)) {
if (!device_distinguishes_on_address(device)) {
ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
@@ -2927,7 +3014,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
ALOGV("opening output for device %08x with params %s profile %p",
device, address.string(), profile.get());
- desc = new AudioOutputDescriptor(profile);
+ desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
desc->mDevice = device;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = desc->mSamplingRate;
@@ -2937,7 +3024,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
config.offload_info.channel_mask = desc->mChannelMask;
config.offload_info.format = desc->mFormat;
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
@@ -3007,7 +3094,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
config.offload_info.sample_rate = config.sample_rate;
config.offload_info.channel_mask = config.channel_mask;
config.offload_info.format = config.format;
- status = mpClientInterface->openOutput(profile->mModule->mHandle,
+ status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&desc->mDevice,
@@ -3032,7 +3119,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
address.string());
}
policyMix->setOutput(desc);
- desc->mPolicyMix = &(policyMix->getMix());
+ desc->mPolicyMix = policyMix->getMix();
} else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) {
// no duplicated output for direct outputs and
@@ -3040,28 +3127,29 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
// set initial stream volume for device
- applyStreamVolumes(output, device, 0, true);
+ applyStreamVolumes(desc, device, 0, true);
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
- duplicatedOutput = mpClientInterface->openDuplicateOutput(output,
- mPrimaryOutput);
+ duplicatedOutput =
+ mpClientInterface->openDuplicateOutput(output,
+ mPrimaryOutput->mIoHandle);
if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
// add duplicated output descriptor
- sp<AudioOutputDescriptor> dupOutputDesc =
- new AudioOutputDescriptor(NULL);
- dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
- dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> dupOutputDesc =
+ new SwAudioOutputDescriptor(NULL, mpClientInterface);
+ dupOutputDesc->mOutput1 = mPrimaryOutput;
+ dupOutputDesc->mOutput2 = desc;
dupOutputDesc->mSamplingRate = desc->mSamplingRate;
dupOutputDesc->mFormat = desc->mFormat;
dupOutputDesc->mChannelMask = desc->mChannelMask;
dupOutputDesc->mLatency = desc->mLatency;
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(duplicatedOutput, device, 0, true);
+ applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
- mPrimaryOutput, output);
+ mPrimaryOutput->mIoHandle, output);
mpClientInterface->closeOutput(output);
removeOutput(output);
nextAudioPortGeneration();
@@ -3083,7 +3171,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
if (device_distinguishes_on_address(device)) {
ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)",
device, address.string());
- setOutputDevice(output, device, true/*force*/, 0/*delay*/,
+ setOutputDevice(desc, device, true/*force*/, 0/*delay*/,
NULL/*patch handle*/, address.string());
}
ALOGV("checkOutputsForDevice(): adding output %d", output);
@@ -3101,10 +3189,9 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de
if (!desc->isDuplicated()) {
// exact match on device
if (device_distinguishes_on_address(device) &&
- (desc->mProfile->mSupportedDevices.types() == device)) {
+ (desc->supportedDevices() == device)) {
findIoHandlesByAddress(desc, device, address, outputs);
- } else if (!(desc->mProfile->mSupportedDevices.types()
- & mAvailableOutputDevices.types())) {
+ } else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) {
ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
mOutputs.keyAt(i));
outputs.add(mOutputs.keyAt(i));
@@ -3212,7 +3299,7 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device,
config.channel_mask = desc->mChannelMask;
config.format = desc->mFormat;
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
- status_t status = mpClientInterface->openInput(profile->mModule->mHandle,
+ status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
&input,
&config,
&desc->mDevice,
@@ -3339,7 +3426,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output)
{
ALOGV("closeOutput(%d)", output);
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (outputDesc == NULL) {
ALOGW("closeOutput() unknown output %d", output);
return;
@@ -3348,7 +3435,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output)
// look for duplicated outputs connected to the output being removed.
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
if (dupOutputDesc->isDuplicated() &&
(dupOutputDesc->mOutput1 == outputDesc ||
dupOutputDesc->mOutput2 == outputDesc)) {
@@ -3417,8 +3504,9 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input)
mInputs.removeItem(input);
}
-SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
- AudioOutputCollection openOutputs)
+SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(
+ audio_devices_t device,
+ SwAudioOutputCollection openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
@@ -3459,14 +3547,14 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy)
// associated with policies in the "before" and "after" output vectors
ALOGVV("checkOutputForStrategy(): policy related outputs");
for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
- const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
+ const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
srcOutputs.add(desc->mIoHandle);
ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
}
}
for (size_t i = 0 ; i < mOutputs.size() ; i++) {
- const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != 0 && desc->mPolicyMix != NULL) {
dstOutputs.add(desc->mIoHandle);
ALOGVV(" new outputs: adding %d", desc->mIoHandle);
@@ -3478,10 +3566,10 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy)
strategy, srcOutputs[0], dstOutputs[0]);
// mute strategy while moving tracks from one output to another
for (size_t i = 0; i < srcOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
if (isStrategyActive(desc, strategy)) {
- setStrategyMute(strategy, true, srcOutputs[i]);
- setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
+ setStrategyMute(strategy, true, desc);
+ setStrategyMute(strategy, false, desc, MUTE_TIME_MS, newDevice);
}
}
@@ -3578,12 +3666,11 @@ void AudioPolicyManager::checkA2dpSuspend()
}
}
-audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+ bool fromCache)
{
audio_devices_t device = AUDIO_DEVICE_NONE;
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -3761,9 +3848,9 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) {
ALOGV("\t muting %d", mute);
uint32_t maxLatency = 0;
for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
setStreamMute(AUDIO_STREAM_TTS, mute/*on*/,
- desc->mIoHandle,
+ desc,
0 /*delay*/, AUDIO_DEVICE_NONE);
const uint32_t latency = desc->latency() * 2;
if (latency > maxLatency) {
@@ -3779,6 +3866,21 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) {
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
+ // Routing
+ // see if we have an explicit route
+ // scan the whole RouteMap, for each entry, convert the stream type to a strategy
+ // (getStrategy(stream)).
+ // if the strategy from the stream type in the RouteMap is the same as the argument above,
+ // and activity count is non-zero
+ // the device = the device from the descriptor in the RouteMap, and exit.
+ for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
+ sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
+ routing_strategy strat = getStrategy(route->mStreamType);
+ if (strat == strategy && route->mDeviceDescriptor != 0 /*&& route->mActivityCount != 0*/) {
+ return route->mDeviceDescriptor->type();
+ }
+ }
+
if (fromCache) {
ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
strategy, mDeviceForStrategy[strategy]);
@@ -3812,7 +3914,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor>
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
- curDevice = curDevice & outputDesc->mProfile->mSupportedDevices.types();
+ curDevice = curDevice & outputDesc->supportedDevices();
bool mute = shouldMute && (curDevice & device) && (curDevice != device);
bool doMute = false;
@@ -3831,10 +3933,9 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor>
== AUDIO_DEVICE_NONE) {
continue;
}
- audio_io_handle_t curOutput = mOutputs.keyAt(j);
- ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d",
- mute ? "muting" : "unmuting", i, curDevice, curOutput);
- setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs);
+ ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x)",
+ mute ? "muting" : "unmuting", i, curDevice);
+ setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
if (isStrategyActive(desc, (routing_strategy)i)) {
if (mute) {
// FIXME: should not need to double latency if volume could be applied
@@ -3859,9 +3960,9 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor>
}
for (size_t i = 0; i < NUM_STRATEGIES; i++) {
if (isStrategyActive(outputDesc, (routing_strategy)i)) {
- setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
+ setStrategyMute((routing_strategy)i, true, outputDesc);
// do tempMute unmute after twice the mute wait time
- setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
+ setStrategyMute((routing_strategy)i, false, outputDesc,
muteWaitMs *2, device);
}
}
@@ -3876,36 +3977,35 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor>
return 0;
}
-uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
+uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
bool force,
int delayMs,
audio_patch_handle_t *patchHandle,
const char* address)
{
- ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
AudioParameter param;
uint32_t muteWaitMs;
if (outputDesc->isDuplicated()) {
- muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
- muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
+ muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
+ muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
return muteWaitMs;
}
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
// output profile
if ((device != AUDIO_DEVICE_NONE) &&
- ((device & outputDesc->mProfile->mSupportedDevices.types()) == 0)) {
+ ((device & outputDesc->supportedDevices()) == 0)) {
return 0;
}
// filter devices according to output selected
- device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types());
+ device = (audio_devices_t)(device & outputDesc->supportedDevices());
audio_devices_t prevDevice = outputDesc->mDevice;
- ALOGV("setOutputDevice() prevDevice %04x", prevDevice);
+ ALOGV("setOutputDevice() prevDevice 0x%04x", prevDevice);
if (device != AUDIO_DEVICE_NONE) {
outputDesc->mDevice = device;
@@ -3918,10 +4018,10 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
// AND force is not specified
// AND the output is connected by a valid audio patch.
// Doing this check here allows the caller to call setOutputDevice() without conditions
- if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force &&
- outputDesc->mPatchHandle != 0) {
- ALOGV("setOutputDevice() setting same device %04x or null device for output %d",
- device, output);
+ if ((device == AUDIO_DEVICE_NONE || device == prevDevice) &&
+ !force &&
+ outputDesc->mPatchHandle != 0) {
+ ALOGV("setOutputDevice() setting same device 0x%04x or null device", device);
return muteWaitMs;
}
@@ -3929,7 +4029,7 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
// do the routing
if (device == AUDIO_DEVICE_NONE) {
- resetOutputDevice(output, delayMs, NULL);
+ resetOutputDevice(outputDesc, delayMs, NULL);
} else {
DeviceVector deviceList = (address == NULL) ?
mAvailableOutputDevices.getDevicesFromType(device)
@@ -3996,16 +4096,15 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
}
// update stream volumes according to new device
- applyStreamVolumes(output, device, delayMs);
+ applyStreamVolumes(outputDesc, device, delayMs);
return muteWaitMs;
}
-status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+status_t AudioPolicyManager::resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs,
audio_patch_handle_t *patchHandle)
{
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
ssize_t index;
if (patchHandle) {
index = mAudioPatches.indexOfKey(*patchHandle);
@@ -4115,12 +4214,15 @@ status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
String8 address,
uint32_t& samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
+ audio_format_t& format,
+ audio_channel_mask_t& channelMask,
audio_input_flags_t flags)
{
// Choose an input profile based on the requested capture parameters: select the first available
// profile supporting all requested parameters.
+ //
+ // TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
+ // the best matching profile, not the first one.
for (size_t i = 0; i < mHwModules.size(); i++)
{
@@ -4133,7 +4235,11 @@ sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
// profile->log();
if (profile->isCompatibleProfile(device, address, samplingRate,
&samplingRate /*updatedSamplingRate*/,
- format, channelMask, (audio_output_flags_t) flags)) {
+ format,
+ &format /*updatedFormat*/,
+ channelMask,
+ &channelMask /*updatedChannelMask*/,
+ (audio_output_flags_t) flags)) {
return profile;
}
@@ -4162,17 +4268,10 @@ audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t input
}
float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
- int index,
- audio_io_handle_t output,
- audio_devices_t device)
+ int index,
+ audio_devices_t device)
{
- float volume = 1.0;
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
- if (device == AUDIO_DEVICE_NONE) {
- device = outputDesc->device();
- }
- volume = mEngine->volIndexToAmpl(Volume::getDeviceCategory(device), stream, index);
+ float volumeDb = mEngine->volIndexToDb(Volume::getDeviceCategory(device), stream, index);
// if a headset is connected, apply the following rules to ring tones and notifications
// to avoid sound level bursts in user's ears:
@@ -4190,41 +4289,39 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
|| ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) &&
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
mStreams.canBeMuted(stream)) {
- volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+ volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
// when the phone is ringing we must consider that music could have been paused just before
// by the music application and behave as if music was active if the last music track was
// just stopped
if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) ||
mLimitRingtoneVolume) {
audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
- float musicVol = computeVolume(AUDIO_STREAM_MUSIC,
- mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice),
- output,
+ float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
+ mStreams.valueFor(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice),
musicDevice);
- float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ?
- musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
- if (volume > minVol) {
- volume = minVol;
- ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+ float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
+ musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB;
+ if (volumeDb > minVolDB) {
+ volumeDb = minVolDB;
+ ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB);
}
}
}
- return volume;
+ return volumeDb;
}
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
- int index,
- audio_io_handle_t output,
- audio_devices_t device,
- int delayMs,
- bool force)
+ int index,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device,
+ int delayMs,
+ bool force)
{
-
// do not change actual stream volume if the stream is muted
- if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+ if (outputDesc->mMuteCount[stream] != 0) {
ALOGVV("checkAndSetVolume() stream %d muted count %d",
- stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+ stream, outputDesc->mMuteCount[stream]);
return NO_ERROR;
}
audio_policy_forced_cfg_t forceUseForComm =
@@ -4237,45 +4334,28 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
return INVALID_OPERATION;
}
- float volume = computeVolume(stream, index, output, device);
- // unit gain if rerouting to external policy
- if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
- ssize_t index = mOutputs.indexOfKey(output);
- if (index >= 0) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
- if (outputDesc->mPolicyMix != NULL) {
- ALOGV("max gain when rerouting for output=%d", output);
- volume = 1.0f;
- }
- }
-
+ if (device == AUDIO_DEVICE_NONE) {
+ device = outputDesc->device();
}
- // We actually change the volume if:
- // - the float value returned by computeVolume() changed
- // - the force flag is set
- if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
- force) {
- mOutputs.valueFor(output)->mCurVolume[stream] = volume;
- ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
- // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
- // enabled
- if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
- mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs);
- }
- mpClientInterface->setStreamVolume(stream, volume, output, delayMs);
+
+ float volumeDb = computeVolume(stream, index, device);
+ if (outputDesc->isFixedVolume(device)) {
+ volumeDb = 0.0f;
}
+ outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
+
if (stream == AUDIO_STREAM_VOICE_CALL ||
stream == AUDIO_STREAM_BLUETOOTH_SCO) {
float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AUDIO_STREAM_VOICE_CALL) {
- voiceVolume = (float)index/(float)mStreams[stream].getVolumeIndexMax();
+ voiceVolume = (float)index/(float)mStreams.valueFor(stream).getVolumeIndexMax();
} else {
voiceVolume = 1.0;
}
- if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) {
+ if (voiceVolume != mLastVoiceVolume && outputDesc == mPrimaryOutput) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
@@ -4284,20 +4364,20 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
return NO_ERROR;
}
-void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output,
- audio_devices_t device,
- int delayMs,
- bool force)
+void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device,
+ int delayMs,
+ bool force)
{
- ALOGVV("applyStreamVolumes() for output %d and device %x", output, device);
+ ALOGVV("applyStreamVolumes() for device %08x", device);
for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
if (stream == AUDIO_STREAM_PATCH) {
continue;
}
checkAndSetVolume((audio_stream_type_t)stream,
- mStreams[stream].getVolumeIndex(device),
- output,
+ mStreams.valueFor((audio_stream_type_t)stream).getVolumeIndex(device),
+ outputDesc,
device,
delayMs,
force);
@@ -4305,10 +4385,10 @@ void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output,
}
void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
- bool on,
- audio_io_handle_t output,
- int delayMs,
- audio_devices_t device)
+ bool on,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ int delayMs,
+ audio_devices_t device)
{
ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
@@ -4316,32 +4396,31 @@ void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
continue;
}
if (getStrategy((audio_stream_type_t)stream) == strategy) {
- setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device);
+ setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
}
}
}
void AudioPolicyManager::setStreamMute(audio_stream_type_t stream,
- bool on,
- audio_io_handle_t output,
- int delayMs,
- audio_devices_t device)
+ bool on,
+ const sp<AudioOutputDescriptor>& outputDesc,
+ int delayMs,
+ audio_devices_t device)
{
- const StreamDescriptor &streamDesc = mStreams[stream];
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+ const StreamDescriptor& streamDesc = mStreams.valueFor(stream);
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->device();
}
- ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x",
- stream, on, output, outputDesc->mMuteCount[stream], device);
+ ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
+ stream, on, outputDesc->mMuteCount[stream], device);
if (on) {
if (outputDesc->mMuteCount[stream] == 0) {
if (streamDesc.canBeMuted() &&
((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
- checkAndSetVolume(stream, 0, output, device, delayMs);
+ checkAndSetVolume(stream, 0, outputDesc, device, delayMs);
}
}
// increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
@@ -4354,7 +4433,7 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream,
if (--outputDesc->mMuteCount[stream] == 0) {
checkAndSetVolume(stream,
streamDesc.getVolumeIndex(device),
- output,
+ outputDesc,
device,
delayMs);
}
@@ -4373,7 +4452,7 @@ void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream,
const routing_strategy stream_strategy = getStrategy(stream);
if ((stream_strategy == STRATEGY_SONIFICATION) ||
((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) {
- sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
+ sp<SwAudioOutputDescriptor> outputDesc = mPrimaryOutput;
ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
stream, starting, outputDesc->mDevice, stateChange);
if (outputDesc->mRefCount[stream]) {
@@ -4406,6 +4485,70 @@ void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream,
}
}
+// --- SessionRoute class implementation
+void AudioPolicyManager::SessionRoute::log(const char* prefix) {
+ ALOGI("%s[SessionRoute strm:0x%X, sess:0x%X, dev:0x%X refs:%d act:%d",
+ prefix, mStreamType, mSession,
+ mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
+ mRefCount, mActivityCount);
+}
+
+// --- SessionRouteMap class implementation
+bool AudioPolicyManager::SessionRouteMap::hasRoute(audio_session_t session)
+{
+ return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
+}
+
+void AudioPolicyManager::SessionRouteMap::addRoute(audio_session_t session,
+ audio_stream_type_t streamType,
+ sp<DeviceDescriptor> deviceDescriptor)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ if (route != NULL) {
+ route->mRefCount++;
+ route->mDeviceDescriptor = deviceDescriptor;
+ } else {
+ route = new AudioPolicyManager::SessionRoute(session, streamType, deviceDescriptor);
+ route->mRefCount++;
+ add(session, route);
+ }
+}
+
+void AudioPolicyManager::SessionRouteMap::removeRoute(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ if (route != 0) {
+ ALOG_ASSERT(route->mRefCount > 0);
+ --route->mRefCount;
+ if (route->mRefCount <= 0) {
+ removeItem(session);
+ }
+ }
+}
+
+int AudioPolicyManager::SessionRouteMap::incRouteActivity(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ return route != 0 ? ++(route->mActivityCount) : -1;
+}
+
+int AudioPolicyManager::SessionRouteMap::decRouteActivity(audio_session_t session)
+{
+ sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
+ if (route != 0 && route->mActivityCount > 0) {
+ return --(route->mActivityCount);
+ } else {
+ return -1;
+ }
+}
+
+void AudioPolicyManager::SessionRouteMap::log(const char* caption) {
+ ALOGI("%s ----", caption);
+ for(size_t index = 0; index < size(); index++) {
+ valueAt(index)->log(" ");
+ }
+}
+
void AudioPolicyManager::defaultAudioPolicyConfig(void)
{
sp<HwModule> module;
@@ -4417,7 +4560,8 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void)
module = new HwModule("primary");
- profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE);
+ profile->attach(module);
profile->mSamplingRates.add(44100);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4425,7 +4569,8 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void)
profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
module->mOutputProfiles.add(profile);
- profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK);
+ profile->attach(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/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 02b678a..fe6b986 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -49,8 +49,11 @@ namespace android {
// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB
#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5
+#define SONIFICATION_HEADSET_VOLUME_FACTOR_DB (-6)
// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB
#define SONIFICATION_HEADSET_VOLUME_MIN 0.016
+#define SONIFICATION_HEADSET_VOLUME_MIN_DB (-36)
+
// Time in milliseconds during which we consider that music is still active after a music
// track was stopped - see computeVolume()
#define SONIFICATION_HEADSET_MUSIC_DELAY 5000
@@ -110,6 +113,7 @@ public:
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ audio_port_handle_t selectedDeviceId,
const audio_offload_info_t *offloadInfo);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
@@ -172,19 +176,15 @@ public:
return mEffects.setEffectEnabled(id, enabled);
}
- virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const
- {
- return mOutputs.isStreamActive(stream, inPastMs);
- }
+ virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
// return whether a stream is playing remotely, override to change the definition of
// local/remote playback, used for instance by notification manager to not make
// media players lose audio focus when not playing locally
// For the base implementation, "remotely" means playing during screen mirroring which
// uses an output for playback with a non-empty, non "0" address.
- virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const
- {
- return mOutputs.isStreamActiveRemotely(stream, inPastMs);
- }
+ virtual bool isStreamActiveRemotely(audio_stream_type_t stream,
+ uint32_t inPastMs = 0) const;
+
virtual bool isSourceActive(audio_source_t source) const;
virtual status_t dump(int fd);
@@ -227,6 +227,46 @@ public:
// return the strategy corresponding to a given stream type
routing_strategy getStrategy(audio_stream_type_t stream) const;
+protected:
+ class SessionRoute : public RefBase
+ {
+ public:
+ friend class SessionRouteMap;
+ SessionRoute(audio_session_t session,
+ audio_stream_type_t streamType,
+ sp<DeviceDescriptor> deviceDescriptor)
+ : mSession(session),
+ mStreamType(streamType),
+ mDeviceDescriptor(deviceDescriptor),
+ mRefCount(0),
+ mActivityCount(0) {}
+
+ audio_session_t mSession;
+ audio_stream_type_t mStreamType;
+
+ sp<DeviceDescriptor> mDeviceDescriptor;
+
+ // "reference" counting
+ int mRefCount; // +/- on references
+ int mActivityCount; // +/- on start/stop
+
+ void log(const char* prefix);
+ };
+
+ class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute>>
+ {
+ public:
+ bool hasRoute(audio_session_t session);
+ void addRoute(audio_session_t session, audio_stream_type_t streamType,
+ sp<DeviceDescriptor> deviceDescriptor);
+ void removeRoute(audio_session_t session);
+
+ int incRouteActivity(audio_session_t session);
+ int decRouteActivity(audio_session_t session);
+
+ void log(const char* caption);
+ };
+
// From AudioPolicyManagerObserver
virtual const AudioPatchCollection &getAudioPatches() const
{
@@ -240,7 +280,7 @@ public:
{
return mPolicyMixes;
}
- virtual const AudioOutputCollection &getOutputs() const
+ virtual const SwAudioOutputCollection &getOutputs() const
{
return mOutputs;
}
@@ -265,7 +305,7 @@ public:
return mDefaultOutputDevice;
}
protected:
- void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc);
+ void addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc);
void removeOutput(audio_io_handle_t output);
void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc);
@@ -288,13 +328,13 @@ protected:
// change the route of the specified output. Returns the number of ms we have slept to
// allow new routing to take effect in certain cases.
- virtual uint32_t setOutputDevice(audio_io_handle_t output,
+ virtual uint32_t setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
bool force = false,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL,
const char* address = NULL);
- status_t resetOutputDevice(audio_io_handle_t output,
+ status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL);
status_t setInputDevice(audio_io_handle_t input,
@@ -309,29 +349,31 @@ protected:
// compute the actual volume for a given stream according to the requested index and a particular
// device
- virtual float computeVolume(audio_stream_type_t stream, int index,
- audio_io_handle_t output, audio_devices_t device);
+ virtual float computeVolume(audio_stream_type_t stream,
+ int index,
+ audio_devices_t device);
// check that volume change is permitted, compute and send new volume to audio hardware
virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
int delayMs = 0, bool force = false);
// apply all stream volumes to the specified output and device
- void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false);
+ void applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+ audio_devices_t device, int delayMs = 0, bool force = false);
// Mute or unmute all streams handled by the specified strategy on the specified output
void setStrategyMute(routing_strategy strategy,
bool on,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_devices_t device = (audio_devices_t)0);
// Mute or unmute the stream on the specified output
void setStreamMute(audio_stream_type_t stream,
bool on,
- audio_io_handle_t output,
+ const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_devices_t device = (audio_devices_t)0);
@@ -384,7 +426,8 @@ protected:
// 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 getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+ 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
@@ -412,7 +455,7 @@ protected:
#endif //AUDIO_POLICY_TEST
SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device,
- AudioOutputCollection openOutputs);
+ SwAudioOutputCollection openOutputs);
bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1,
SortedVector<audio_io_handle_t>& outputs2);
@@ -427,12 +470,12 @@ protected:
audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs,
audio_output_flags_t flags,
audio_format_t format);
- // samplingRate parameter is an in/out and so may be modified
+ // samplingRate, format, channelMask are in/out and so may be modified
sp<IOProfile> getInputProfile(audio_devices_t device,
String8 address,
uint32_t& samplingRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
+ audio_format_t& format,
+ audio_channel_mask_t& channelMask,
audio_input_flags_t flags);
sp<IOProfile> getProfileForDirectOutput(audio_devices_t device,
uint32_t samplingRate,
@@ -453,28 +496,39 @@ protected:
audio_devices_t availablePrimaryOutputDevices() const
{
- return mOutputs.getSupportedDevices(mPrimaryOutput) & mAvailableOutputDevices.types();
+ return mPrimaryOutput->supportedDevices() & mAvailableOutputDevices.types();
}
audio_devices_t availablePrimaryInputDevices() const
{
- return mAvailableInputDevices.getDevicesFromHwModule(
- mOutputs.valueFor(mPrimaryOutput)->getModuleHandle());
+ return mAvailableInputDevices.getDevicesFromHwModule(mPrimaryOutput->getModuleHandle());
}
void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0);
+ status_t startSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream,
+ audio_devices_t device,
+ uint32_t *delayMs);
+ status_t stopSource(sp<AudioOutputDescriptor> outputDesc,
+ audio_stream_type_t stream);
+
uid_t mUidCached;
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
- audio_io_handle_t mPrimaryOutput; // primary output handle
+ sp<SwAudioOutputDescriptor> mPrimaryOutput; // primary output descriptor
// list of descriptors for outputs currently opened
- AudioOutputCollection mOutputs;
+
+ SwAudioOutputCollection mOutputs;
// copy of mOutputs before setDeviceConnectionState() opens new outputs
// reset to mOutputs when updateDevicesAndOutputs() is called.
- AudioOutputCollection mPreviousOutputs;
+ SwAudioOutputCollection mPreviousOutputs;
AudioInputCollection mInputs; // list of input descriptors
+
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
+ SessionRouteMap mOutputRoutes;
+ SessionRouteMap mInputRoutes;
+
StreamDescriptorCollection mStreams; // stream descriptors for volume control
bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected
audio_devices_t mDeviceForStrategy[NUM_STRATEGIES];
@@ -539,7 +593,7 @@ private:
// in mProfile->mSupportedDevices) matches the device whose address is to be matched.
// see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one
// where addresses are used to distinguish between one connected device and another.
- void findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+ void findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
const audio_devices_t device /*in*/,
const String8 address /*in*/,
SortedVector<audio_io_handle_t>& outputs /*out*/);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index e9ff838..9510727 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -150,6 +150,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ int mSelectedDeviceId,
const audio_offload_info_t *offloadInfo)
{
if (mAudioPolicyManager == NULL) {
@@ -158,7 +159,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
return mAudioPolicyManager->getOutputForAttr(attr, output, session, stream, samplingRate,
- format, channelMask, flags, offloadInfo);
+ format, channelMask, flags, mSelectedDeviceId, offloadInfo);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -261,8 +262,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
return BAD_VALUE;
}
- if (((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
- ((attr->source == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+ if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
return BAD_VALUE;
}
sp<AudioPolicyEffects>audioPolicyEffects;
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 5a91192..e4ca5dc 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -255,8 +255,7 @@ status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
inputSource = AUDIO_SOURCE_MIC;
}
- if (((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) ||
- ((inputSource == AUDIO_SOURCE_FM_TUNER) && !captureFmTunerAllowed())) {
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
return BAD_VALUE;
}
@@ -569,6 +568,7 @@ status_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr,
audio_format_t format,
audio_channel_mask_t channelMask,
audio_output_flags_t flags,
+ int selectedDeviceId __unused,
const audio_offload_info_t *offloadInfo)
{
if (attr != NULL) {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 0378384..f8dabd3 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -84,6 +84,7 @@ public:
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ int selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
const audio_offload_info_t *offloadInfo = NULL);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e9c96c6..414d563 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -122,7 +122,7 @@ static void torch_mode_status_change(
// should be ok for now.
static CameraService *gCameraService;
-CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH),
+CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this;
@@ -242,6 +242,8 @@ void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId,
}
if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+ logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
sp<BasicClient> clientToDisconnect;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
@@ -274,6 +276,10 @@ void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId,
}
} else {
+ if (oldStatus == ICameraServiceListener::Status::STATUS_NOT_PRESENT) {
+ logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
+ }
updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
}
@@ -765,8 +771,8 @@ status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/
} else {
// We only trust our own process to forward client UIDs
if (callingPid != getpid()) {
- ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
- callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
+ callingPid, clientUid);
return PERMISSION_DENIED;
}
}
@@ -796,10 +802,12 @@ status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/
return -EACCES;
}
- // Only allow clients who are being used by the current foreground device user.
- if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) {
- ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground "
- "device user)", callingPid);
+ // Only allow clients who are being used by the current foreground device user, unless calling
+ // from our own process.
+ if (callingPid != getpid() &&
+ (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) {
+ ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous "
+ "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId);
return PERMISSION_DENIED;
}
@@ -858,7 +866,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
status_t ret = NO_ERROR;
- std::vector<sp<BasicClient>> evictedClients;
+ std::vector<DescriptorPtr> evictedClients;
DescriptorPtr clientDescriptor;
{
if (effectiveApiLevel == API_1) {
@@ -934,7 +942,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien
mActiveClientManager.getIncompatibleClients(clientDescriptor);
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
- "(PID %d, priority %d)", curTime.string(),
+ "(PID %d, priority %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
@@ -946,6 +954,7 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien
}
// Log the client's attempt
+ Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
return -EBUSY;
@@ -965,14 +974,12 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien
ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
i->getKey().string());
- evictedClients.push_back(clientSp);
-
- String8 curTime = getFormattedCurrentTime();
+ evictedClients.push_back(i);
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
- PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for "
- "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+ logEvent(String8::format("EVICT device %s client held by package %s (PID"
+ " %" PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for"
+ " package %s (PID %d, priority %" PRId32 ")",
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority(), cameraId.string(),
packageName.string(), clientPid,
@@ -994,12 +1001,31 @@ status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clien
// Destroy evicted clients
for (auto& i : evictedClients) {
// Disconnect is blocking, and should only have returned when HAL has cleaned up
- i->disconnect(); // Clients will remove themselves from the active client list here
+ i->getValue()->disconnect(); // Clients will remove themselves from the active client list
}
- evictedClients.clear();
IPCThreadState::self()->restoreCallingIdentity(token);
+ for (const auto& i : evictedClients) {
+ ALOGV("%s: Waiting for disconnect to complete for client for device %s (PID %" PRId32 ")",
+ __FUNCTION__, i->getKey().string(), i->getOwnerId());
+ ret = mActiveClientManager.waitUntilRemoved(i, DEFAULT_DISCONNECT_TIMEOUT_NS);
+ if (ret == TIMED_OUT) {
+ ALOGE("%s: Timed out waiting for client for device %s to disconnect, "
+ "current clients:\n%s", __FUNCTION__, i->getKey().string(),
+ mActiveClientManager.toString().string());
+ return -EBUSY;
+ }
+ if (ret != NO_ERROR) {
+ ALOGE("%s: Received error waiting for client for device %s to disconnect: %s (%d), "
+ "current clients:\n%s", __FUNCTION__, i->getKey().string(), strerror(-ret),
+ ret, mActiveClientManager.toString().string());
+ return ret;
+ }
+ }
+
+ evictedClients.clear();
+
// Once clients have been disconnected, relock
mServiceLock.lock();
@@ -1027,6 +1053,8 @@ status_t CameraService::connect(
clientPackageName, clientUid, API_1, false, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1042,6 +1070,7 @@ status_t CameraService::connectLegacy(
/*out*/
sp<ICamera>& device) {
+ String8 id = String8::format("%d", cameraId);
int apiVersion = mModule->getModuleApiVersion();
if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
@@ -1053,16 +1082,19 @@ status_t CameraService::connectLegacy(
*/
ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!",
__FUNCTION__, apiVersion);
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8("HAL module version doesn't support legacy HAL connections"));
return INVALID_OPERATION;
}
status_t ret = NO_ERROR;
- String8 id = String8::format("%d", cameraId);
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
clientUid, API_1, true, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1086,6 +1118,8 @@ status_t CameraService::connectDevice(
/*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1426,6 +1460,8 @@ void CameraService::doUserSwitch(int newUserId) {
newUserId = DEFAULT_LAST_USER_ID;
}
+ logUserSwitch(mLastUserId, newUserId);
+
mLastUserId = newUserId;
// Current user has switched, evict all current clients.
@@ -1444,12 +1480,12 @@ void CameraService::doUserSwitch(int newUserId) {
ALOGE("Evicting conflicting client for camera ID %s due to user change",
i->getKey().string());
+
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+ logEvent(String8::format("EVICT device %s client held by package %s (PID %"
PRId32 ", priority %" PRId32 ")\n - Evicted due to user switch.",
- curTime.string(), i->getKey().string(),
- String8{clientSp->getPackageName()}.string(), i->getOwnerId(),
- i->getPriority()));
+ i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+ i->getOwnerId(), i->getPriority()));
}
@@ -1470,22 +1506,52 @@ void CameraService::doUserSwitch(int newUserId) {
mServiceLock.lock();
}
-void CameraService::logDisconnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
-
+void CameraService::logEvent(const char* event) {
String8 curTime = getFormattedCurrentTime();
- // Log the clients evicted
- mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ Mutex::Autolock l(mLogLock);
+ mEventLog.add(String8::format("%s : %s", curTime.string(), event));
}
-void CameraService::logConnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
+void CameraService::logDisconnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
+ // Log the clients evicted
+ logEvent(String8::format("DISCONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
- String8 curTime = getFormattedCurrentTime();
+void CameraService::logConnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
// Log the clients evicted
- mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ logEvent(String8::format("CONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
+void CameraService::logRejected(const char* cameraId, int clientPid,
+ const char* clientPackage, const char* reason) {
+ // Log the client rejected
+ logEvent(String8::format("REJECT device %s client for package %s (PID %d), reason: (%s)",
+ cameraId, clientPackage, clientPid, reason));
+}
+
+void CameraService::logUserSwitch(int oldUserId, int newUserId) {
+ // Log the new and old users
+ logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId,
+ newUserId));
+}
+
+void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("REMOVE device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logDeviceAdded(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("ADD device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logClientDied(int clientPid, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("DIED client(s) with PID %d, reason: (%s)", clientPid, reason));
}
status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
@@ -1911,7 +1977,7 @@ static bool tryLock(Mutex& mutex)
}
status_t CameraService::dump(int fd, const Vector<String16>& args) {
- String8 result;
+ String8 result("Dump of the Camera Service:\n");
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.appendFormat("Permission Denial: "
"can't dump CameraService from pid=%d, uid=%d\n",
@@ -1957,12 +2023,15 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
result = String8("Prior client events (most recent at top):\n");
- for (const auto& msg : mEventLog) {
- result.appendFormat("%s\n", msg.string());
- }
+ {
+ Mutex::Autolock l(mLogLock);
+ for (const auto& msg : mEventLog) {
+ result.appendFormat("%s\n", msg.string());
+ }
- if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
- result.append("...\n");
+ if (mEventLog.size() == DEFAULT_EVENT_LOG_LENGTH) {
+ result.append("...\n");
+ }
}
write(fd, result.string(), result.size());
@@ -2094,10 +2163,12 @@ void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
/**
- * While tempting to promote the wp<IBinder> into a sp,
- * it's actually not supported by the binder driver
+ * While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
+ * binder driver
*/
+ logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
+
// check torch client
handleTorchClientBinderDied(who);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index ca1c504..91c7d59 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -90,8 +90,11 @@ public:
// 3 second busy timeout when other clients are connecting
static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
+ // 1 second busy timeout when other clients are disconnecting
+ static const nsecs_t DEFAULT_DISCONNECT_TIMEOUT_NS = 1000000000;
+
// Default number of messages to store in eviction log
- static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+ static const size_t DEFAULT_EVENT_LOG_LENGTH = 100;
enum {
// Default last user id
@@ -492,6 +495,7 @@ private:
// Circular buffer for storing event logging for dumps
RingBuffer<String8> mEventLog;
+ Mutex mLogLock;
// UID of last user.
int mLastUserId;
@@ -546,14 +550,45 @@ private:
void doUserSwitch(int newUserId);
/**
- * Add a event log message that a client has been disconnected.
+ * Add an event log message.
+ */
+ void logEvent(const char* event);
+
+ /**
+ * Add an event log message that a client has been disconnected.
+ */
+ void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client has been connected.
+ */
+ void logConnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client's connect attempt has been rejected.
*/
- void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logRejected(const char* cameraId, int clientPid, const char* clientPackage,
+ const char* reason);
/**
- * Add a event log message that a client has been connected.
+ * Add an event log message that the current device user has been switched.
*/
- void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logUserSwitch(int oldUserId, int newUserId);
+
+ /**
+ * Add an event log message that a device has been removed by the HAL
+ */
+ void logDeviceRemoved(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a device has been added by the HAL
+ */
+ void logDeviceAdded(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a client has unexpectedly died.
+ */
+ void logClientDied(int clientPid, const char* reason);
int mNumberOfCameras;
@@ -714,9 +749,10 @@ status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String
String8 clientName8(clientPackageName);
int clientPid = getCallingPid();
- ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+ ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
- halVersion, static_cast<int>(effectiveApiLevel));
+ (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
+ static_cast<int>(effectiveApiLevel));
sp<CLIENT> client = nullptr;
{
@@ -734,7 +770,15 @@ status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String
if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
return ret;
}
- mLastUserId = multiuser_get_user_id(clientUid);
+ int userId = multiuser_get_user_id(clientUid);
+
+ if (userId != mLastUserId && clientPid != getpid() ) {
+ // If no previous user ID had been set, set to the user of the caller.
+ logUserSwitch(mLastUserId, userId);
+ LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID,
+ "Invalid state: Should never update user ID here unless was default");
+ mLastUserId = userId;
+ }
// Check the shim parameters after acquiring lock, if they have already been updated and
// we were doing a shim update, return immediately
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6f44aee..f53f425 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -121,7 +121,8 @@ status_t Camera2Client::initialize(CameraModule *module)
}
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2: {
+ case CAMERA_DEVICE_API_VERSION_3_2:
+ case CAMERA_DEVICE_API_VERSION_3_3: {
sp<ZslProcessor3> zslProc =
new ZslProcessor3(this, mCaptureSequencer);
mZslProcessor = zslProc;
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index ad5486d..aa40a2d 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -17,7 +17,9 @@
#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+#include <utils/Condition.h>
#include <utils/Mutex.h>
+#include <utils/Timers.h>
#include <algorithm>
#include <utility>
@@ -263,6 +265,16 @@ public:
*/
std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
+ /**
+ * Block until the given client is no longer in the active clients list, or the timeout
+ * occurred.
+ *
+ * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
+ * failure.
+ */
+ status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+ nsecs_t timeout) const;
+
protected:
~ClientManager();
@@ -284,6 +296,7 @@ private:
int64_t getCurrentCostLocked() const;
mutable Mutex mLock;
+ mutable Condition mRemovedCondition;
int32_t mMaxCost;
// LRU ordered, most recent at end
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
@@ -430,6 +443,7 @@ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VA
}), mClients.end());
mClients.push_back(client);
+ mRemovedCondition.broadcast();
return evicted;
}
@@ -487,6 +501,7 @@ template<class KEY, class VALUE>
void ClientManager<KEY, VALUE>::removeAll() {
Mutex::Autolock lock(mLock);
mClients.clear();
+ mRemovedCondition.broadcast();
}
template<class KEY, class VALUE>
@@ -505,6 +520,39 @@ std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(
return false;
}), mClients.end());
+ mRemovedCondition.broadcast();
+ return ret;
+}
+
+template<class KEY, class VALUE>
+status_t ClientManager<KEY, VALUE>::waitUntilRemoved(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
+ nsecs_t timeout) const {
+ status_t ret = NO_ERROR;
+ Mutex::Autolock lock(mLock);
+
+ bool isRemoved = false;
+
+ // Figure out what time in the future we should hit the timeout
+ nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;
+
+ while (!isRemoved) {
+ isRemoved = true;
+ for (const auto& i : mClients) {
+ if (i == client) {
+ isRemoved = false;
+ }
+ }
+
+ if (!isRemoved) {
+ ret = mRemovedCondition.waitRelative(mLock, timeout);
+ if (ret != NO_ERROR) {
+ break;
+ }
+ timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+ }
+
return ret;
}
@@ -520,6 +568,7 @@ void ClientManager<KEY, VALUE>::remove(
}
return false;
}), mClients.end());
+ mRemovedCondition.broadcast();
}
template<class KEY, class VALUE>