Merge "Add support for scaling mode parameter"
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index b81fe86..d43cb0b 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -116,13 +116,13 @@
return cs->getCameraInfo(cameraId, cameraInfo);
}
-sp<Camera> Camera::connect(int cameraId, bool force, bool keep)
+sp<Camera> Camera::connect(int cameraId)
{
ALOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
- c->mCamera = cs->connect(c, cameraId, force, keep);
+ c->mCamera = cs->connect(c, cameraId);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index c74298a..f2d367e 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -56,15 +56,12 @@
}
// connect to camera service
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
- bool force, bool keep)
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
data.writeInt32(cameraId);
- data.writeInt32(force);
- data.writeInt32(keep);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
@@ -96,10 +93,7 @@
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
- const int cameraId = data.readInt32();
- const int force = data.readInt32();
- const int keep = data.readInt32();
- sp<ICamera> camera = connect(cameraClient, cameraId, force, keep);
+ sp<ICamera> camera = connect(cameraClient, data.readInt32());
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index ece3c09..ebb5d58 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -39,8 +39,7 @@
fprintf(stderr, "usage: %s [-a] use audio\n"
"\t\t[-v] use video\n"
"\t\t[-p] playback\n"
- "\t\t[-S] allocate buffers from a surface\n"
- "\t\t[-D] decrypt input buffers\n",
+ "\t\t[-S] allocate buffers from a surface\n",
me);
exit(1);
@@ -61,33 +60,6 @@
bool mIsAudio;
};
-static sp<ICrypto> makeCrypto(
- const uint8_t uuid[16], const void *data, size_t size) {
- sp<IServiceManager> sm = defaultServiceManager();
-
- sp<IBinder> binder =
- sm->getService(String16("media.player"));
-
- sp<IMediaPlayerService> service =
- interface_cast<IMediaPlayerService>(binder);
-
- CHECK(service != NULL);
-
- sp<ICrypto> crypto = service->makeCrypto();
-
- if (crypto == NULL || crypto->initCheck() != OK) {
- return NULL;
- }
-
- status_t err = crypto->createPlugin(uuid, data, size);
-
- if (err != OK) {
- return NULL;
- }
-
- return crypto;
-}
-
} // namespace android
static int decode(
@@ -95,8 +67,7 @@
const char *path,
bool useAudio,
bool useVideo,
- const android::sp<android::Surface> &surface,
- bool decryptInputBuffers) {
+ const android::sp<android::Surface> &surface) {
using namespace android;
static int64_t kTimeout = 500ll;
@@ -107,8 +78,6 @@
return 1;
}
- sp<ICrypto> crypto;
-
KeyedVector<size_t, CodecState> stateByTrack;
bool haveAudio = false;
@@ -144,62 +113,14 @@
state->mNumBuffersDecoded = 0;
state->mIsAudio = isAudio;
- if (decryptInputBuffers && crypto == NULL) {
- sp<ABuffer> emm;
- CHECK(format->findBuffer("emm", &emm));
-
- sp<ABuffer> ecm;
- CHECK(format->findBuffer("ecm", &ecm));
-
- struct WVOpaqueInitData {
- uint8_t mEMM[16];
- uint8_t mECM[32];
-
- } opaque;
-
- CHECK_EQ(emm->size(), sizeof(opaque.mEMM));
- memcpy(opaque.mEMM, emm->data(), emm->size());
-
- CHECK_EQ(ecm->size(), 80u);
- // bytes 16..47 of the original ecm stream data.
- memcpy(opaque.mECM, ecm->data() + 16, 32);
-
- static const uint8_t kUUIDWidevine[16] = {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
- };
-
- crypto = makeCrypto(kUUIDWidevine, &opaque, sizeof(opaque));
- CHECK(crypto != NULL);
- CHECK_EQ(crypto->initCheck(), (status_t)OK);
- }
-
- if (decryptInputBuffers
- && crypto->requiresSecureDecoderComponent(mime.c_str())) {
- static const MediaCodecList *list = MediaCodecList::getInstance();
-
- ssize_t index =
- list->findCodecByType(mime.c_str(), false /* encoder */);
-
- CHECK_GE(index, 0);
-
- const char *componentName = list->getCodecName(index);
-
- AString fullName = componentName;
- fullName.append(".secure");
-
- state->mCodec = MediaCodec::CreateByComponentName(
- looper, fullName.c_str());
- } else {
- state->mCodec = MediaCodec::CreateByType(
- looper, mime.c_str(), false /* encoder */);
- }
+ state->mCodec = MediaCodec::CreateByType(
+ looper, mime.c_str(), false /* encoder */);
CHECK(state->mCodec != NULL);
err = state->mCodec->configure(
format, isVideo ? surface : NULL,
- crypto,
+ NULL /* crypto */,
0 /* flags */);
CHECK_EQ(err, (status_t)OK);
@@ -289,35 +210,12 @@
uint32_t bufferFlags = 0;
- uint32_t sampleFlags;
- err = extractor->getSampleFlags(&sampleFlags);
- CHECK_EQ(err, (status_t)OK);
-
- if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
- CHECK(decryptInputBuffers);
-
- CryptoPlugin::SubSample ss;
- ss.mNumBytesOfClearData = 0;
- ss.mNumBytesOfEncryptedData = buffer->size();
-
- err = state->mCodec->queueSecureInputBuffer(
- index,
- 0 /* offset */,
- &ss,
- 1 /* numSubSamples */,
- NULL /* key */,
- NULL /* iv */,
- CryptoPlugin::kMode_AES_WV,
- timeUs,
- bufferFlags);
- } else {
- err = state->mCodec->queueInputBuffer(
- index,
- 0 /* offset */,
- buffer->size(),
- timeUs,
- bufferFlags);
- }
+ err = state->mCodec->queueInputBuffer(
+ index,
+ 0 /* offset */,
+ buffer->size(),
+ timeUs,
+ bufferFlags);
CHECK_EQ(err, (status_t)OK);
@@ -451,7 +349,6 @@
bool useVideo = false;
bool playback = false;
bool useSurface = false;
- bool decryptInputBuffers = false;
int res;
while ((res = getopt(argc, argv, "havpSD")) >= 0) {
@@ -480,12 +377,6 @@
break;
}
- case 'D':
- {
- decryptInputBuffers = true;
- break;
- }
-
case '?':
case 'h':
default:
@@ -557,8 +448,7 @@
player->stop();
player->reset();
} else {
- decode(looper, argv[0],
- useAudio, useVideo, surface, decryptInputBuffers);
+ decode(looper, argv[0], useAudio, useVideo, surface);
}
if (playback || (useSurface && useVideo)) {
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index 8ba0203..746f506 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -34,21 +34,7 @@
static Vector<uid_t> trustedUids;
static bool isProtectedCallAllowed() {
- // TODO
- // Following implementation is just for reference.
- // Each OEM manufacturer should implement/replace with their own solutions.
- bool result = false;
-
- IPCThreadState* ipcState = IPCThreadState::self();
- uid_t uid = ipcState->getCallingUid();
-
- for (unsigned int i = 0; i < trustedUids.size(); ++i) {
- if (trustedUids[i] == uid) {
- result = true;
- break;
- }
- }
- return result;
+ return true;
}
void DrmManagerService::instantiate() {
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 3fedea0..234e165 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -72,7 +72,7 @@
static int32_t getNumberOfCameras();
static status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
- static sp<Camera> connect(int cameraId, bool force, bool keep);
+ static sp<Camera> connect(int cameraId);
virtual ~Camera();
void init();
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 97e3169..7d70c1e 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -42,7 +42,7 @@
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) = 0;
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient,
- int cameraId, bool force, bool keep) = 0;
+ int cameraId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 471f462..f73a317 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -185,7 +185,7 @@
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = AUDIO_CHANNEL_OUT_STEREO,
- audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE);
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE);
static status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 6de6486..4906bd3 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -139,7 +139,7 @@
* latency of the track. The actual size selected by the AudioTrack could be
* larger if the requested size is not compatible with current audio HAL
* latency.
- * flags: See comments on audio_policy_output_flags_t in <system/audio_policy.h>.
+ * flags: See comments on audio_output_flags_t in <system/audio.h>.
* cbf: Callback function. If not null, this function is called periodically
* to request new PCM data.
* user: Context for use by the callback receiver.
@@ -155,7 +155,7 @@
audio_format_t format = AUDIO_FORMAT_DEFAULT,
int channelMask = 0,
int frameCount = 0,
- audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
int notificationFrames = 0,
@@ -167,7 +167,7 @@
int format = AUDIO_FORMAT_DEFAULT,
int channelMask = 0,
int frameCount = 0,
- uint32_t flags = (uint32_t) AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ uint32_t flags = (uint32_t) AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = 0,
void* user = 0,
int notificationFrames = 0,
@@ -187,7 +187,7 @@
audio_format_t format = AUDIO_FORMAT_DEFAULT,
int channelMask = 0,
const sp<IMemory>& sharedBuffer = 0,
- audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
int notificationFrames = 0,
@@ -211,7 +211,7 @@
audio_format_t format = AUDIO_FORMAT_DEFAULT,
int channelMask = 0,
int frameCount = 0,
- audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
void* user = NULL,
int notificationFrames = 0,
@@ -476,7 +476,7 @@
audio_format_t format,
uint32_t channelMask,
int frameCount,
- audio_policy_output_flags_t flags,
+ audio_output_flags_t flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output);
void flush_l();
@@ -517,7 +517,7 @@
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
bool mFlushed; // FIXME will be made obsolete by making flush() synchronous
- audio_policy_output_flags_t mFlags;
+ audio_output_flags_t mFlags;
int mSessionId;
int mAuxEffectId;
mutable Mutex mLock;
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 9e938d1..04ac3ee 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -133,7 +133,7 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags) = 0;
+ audio_output_flags_t flags) = 0;
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2) = 0;
virtual status_t closeOutput(audio_io_handle_t output) = 0;
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 04c927a..e160d70 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -52,7 +52,7 @@
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE) = 0;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE) = 0;
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0) = 0;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index d0db98e..8a87d83 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -129,10 +129,6 @@
kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
- kKeyScrambling = 'scrm', // int32_t
- kKeyEMM = 'emm ', // raw data
- kKeyECM = 'ecm ', // raw data
-
kKeyIsADTS = 'adts', // bool (int32_t)
// If a MediaBuffer's data represents (at least partially) encrypted
@@ -156,6 +152,7 @@
kKeyPlainSizes = 'plai', // size_t[]
kKeyCryptoKey = 'cryK', // uint8_t[16]
kKeyCryptoIV = 'cryI', // uint8_t[16]
+ kKeyCryptoMode = 'cryM', // int32_t
};
enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 07c7be5..9c61113 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -19,7 +19,9 @@
#include <media/stagefright/foundation/ABase.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
+#include <utils/String8.h>
#include <utils/Vector.h>
namespace android {
@@ -29,6 +31,7 @@
struct MediaBuffer;
struct MediaExtractor;
struct MediaSource;
+struct MetaData;
struct NuMediaExtractor : public RefBase {
enum SampleFlags {
@@ -38,7 +41,11 @@
NuMediaExtractor();
- status_t setDataSource(const char *path);
+ status_t setDataSource(
+ const char *path,
+ const KeyedVector<String8, String8> *headers = NULL);
+
+ status_t setDataSource(int fd, off64_t offset, off64_t size);
size_t countTracks() const;
status_t getTrackFormat(size_t index, sp<AMessage> *format) const;
@@ -51,7 +58,7 @@
status_t readSampleData(const sp<ABuffer> &buffer);
status_t getSampleTrackIndex(size_t *trackIndex);
status_t getSampleTime(int64_t *sampleTimeUs);
- status_t getSampleFlags(uint32_t *sampleFlags);
+ status_t getSampleMeta(sp<MetaData> *sampleMeta);
protected:
virtual ~NuMediaExtractor();
@@ -67,7 +74,6 @@
status_t mFinalResult;
MediaBuffer *mSample;
int64_t mSampleTimeUs;
- uint32_t mSampleFlags;
uint32_t mTrackFlags; // bitmask of "TrackFlags"
};
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index e3ca536..1c5fd36 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -40,18 +40,24 @@
status_t start();
status_t pause();
- status_t selectTrack(int32_t index);
- status_t unselectTrack(int32_t index);
+ status_t selectTrack(size_t index);
+ status_t unselectTrack(size_t index);
status_t seekToAsync(int64_t timeUs);
- status_t addInBandTextSource(const sp<MediaSource>& source);
- status_t addOutOfBandTextSource(const char *uri, const char *mimeType);
+ status_t addInBandTextSource(
+ size_t trackIndex, const sp<MediaSource>& source);
+
+ status_t addOutOfBandTextSource(
+ size_t trackIndex, const char *uri, const char *mimeType);
+
// Caller owns the file desriptor and caller is responsible for closing it.
status_t addOutOfBandTextSource(
- int fd, off64_t offset, off64_t length, const char *mimeType);
+ size_t trackIndex, int fd, off64_t offset,
+ off64_t length, const char *mimeType);
- void getTrackInfo(Parcel *parcel);
+ void getExternalTrackInfo(Parcel *parcel);
+ size_t countExternalTracks() const;
private:
Mutex mLock;
@@ -68,13 +74,17 @@
// Variables to be guarded by mLock.
State mState;
- int32_t mCurrentTrackIndex;
- Vector<sp<TimedTextSource> > mTextSourceVector;
+ size_t mCurrentTrackIndex;
+ KeyedVector<size_t, sp<TimedTextSource> > mTextSourceVector;
+ Vector<bool> mTextSourceTypeVector;
+
// -- End of variables to be guarded by mLock
- status_t selectTrack_l(int32_t index);
+ status_t selectTrack_l(size_t index);
+
status_t createOutOfBandTextSource(
- const char *mimeType, const sp<DataSource>& dataSource);
+ size_t trackIndex, const char* mimeType,
+ const sp<DataSource>& dataSource);
DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
};
diff --git a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
index d0ee51b..797686c 100755
--- a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
@@ -537,7 +537,7 @@
(numChannels == 2)
? AUDIO_CHANNEL_OUT_STEREO
: AUDIO_CHANNEL_OUT_MONO,
- 0, AUDIO_POLICY_OUTPUT_FLAG_NONE, &AudioCallback, this, 0);
+ 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0);
if ((err = mAudioTrack->initCheck()) != OK) {
delete mAudioTrack;
diff --git a/libvideoeditor/lvpp/VideoEditorPlayer.cpp b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
index 1ba1f44..c9cff81 100755
--- a/libvideoeditor/lvpp/VideoEditorPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorPlayer.cpp
@@ -442,7 +442,7 @@
format,
channelMask,
frameCount,
- AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ AUDIO_OUTPUT_FLAG_NONE,
CallbackWrapper,
this);
} else {
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 2596f07..4c41ba5 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -589,7 +589,7 @@
uint32_t samplingRate,
audio_format_t format,
uint32_t channels,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return 0;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 6dc6c41..092b516 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -92,7 +92,7 @@
audio_format_t format,
int channelMask,
int frameCount,
- audio_policy_output_flags_t flags,
+ audio_output_flags_t flags,
callback_t cbf,
void* user,
int notificationFrames,
@@ -124,7 +124,7 @@
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
{
mStatus = set((audio_stream_type_t)streamType, sampleRate, (audio_format_t)format, channelMask,
- frameCount, (audio_policy_output_flags_t)flags, cbf, user, notificationFrames,
+ frameCount, (audio_output_flags_t)flags, cbf, user, notificationFrames,
0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId);
}
@@ -134,7 +134,7 @@
audio_format_t format,
int channelMask,
const sp<IMemory>& sharedBuffer,
- audio_policy_output_flags_t flags,
+ audio_output_flags_t flags,
callback_t cbf,
void* user,
int notificationFrames,
@@ -174,7 +174,7 @@
audio_format_t format,
int channelMask,
int frameCount,
- audio_policy_output_flags_t flags,
+ audio_output_flags_t flags,
callback_t cbf,
void* user,
int notificationFrames,
@@ -222,8 +222,8 @@
// force direct flag if format is not linear PCM
if (!audio_is_linear_pcm(format)) {
- flags = (audio_policy_output_flags_t)
- ((flags | AUDIO_POLICY_OUTPUT_FLAG_DIRECT) & ~AUDIO_POLICY_OUTPUT_FLAG_FAST);
+ flags = (audio_output_flags_t)
+ ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
}
if (!audio_is_output_channel(channelMask)) {
@@ -735,7 +735,7 @@
audio_format_t format,
uint32_t channelMask,
int frameCount,
- audio_policy_output_flags_t flags,
+ audio_output_flags_t flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output)
{
@@ -761,14 +761,14 @@
// Client decides whether the track is TIMED (see below), but can only express a preference
// for FAST. Server will perform additional tests.
- if ((flags & AUDIO_POLICY_OUTPUT_FLAG_FAST) && !(
+ if ((flags & AUDIO_OUTPUT_FLAG_FAST) && !(
// either of these use cases:
// use case 1: shared buffer
(sharedBuffer != 0) ||
// use case 2: callback handler
(mCbf != NULL))) {
- ALOGW("AUDIO_POLICY_OUTPUT_FLAG_FAST denied");
- flags = (audio_policy_output_flags_t) (flags & ~AUDIO_POLICY_OUTPUT_FLAG_FAST);
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied");
+ flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
}
ALOGV("createTrack_l() output %d afFrameCount %d afLatency %d", output, afFrameCount, afLatency);
@@ -796,7 +796,7 @@
if (mNotificationFramesAct > (uint32_t)frameCount/2) {
mNotificationFramesAct = frameCount/2;
}
- if (frameCount < minFrameCount && !(flags & AUDIO_POLICY_OUTPUT_FLAG_FAST)) {
+ if (frameCount < minFrameCount && !(flags & AUDIO_OUTPUT_FLAG_FAST)) {
// not ALOGW because it happens all the time when playing key clicks over A2DP
ALOGV("Minimum buffer size corrected from %d to %d",
frameCount, minFrameCount);
@@ -817,7 +817,7 @@
if (mIsTimed) {
trackFlags |= IAudioFlinger::TRACK_TIMED;
}
- if (flags & AUDIO_POLICY_OUTPUT_FLAG_FAST) {
+ if (flags & AUDIO_OUTPUT_FLAG_FAST) {
trackFlags |= IAudioFlinger::TRACK_FAST;
}
@@ -1033,7 +1033,7 @@
size_t toWrite;
- if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// Divide capacity by 2 to take expansion into account
toWrite = audioBuffer.size>>1;
memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) src, toWrite);
@@ -1190,7 +1190,7 @@
// Divide buffer size by 2 to take into account the expansion
// due to 8 to 16 bit conversion: the callback must fill only half
// of the destination buffer
- if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
audioBuffer.size >>= 1;
}
@@ -1209,7 +1209,7 @@
}
if (writtenSize > reqSize) writtenSize = reqSize;
- if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT)) {
+ if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
// 8 to 16 bit conversion, note that source and destination are the same address
memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize);
writtenSize <<= 1;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 81e259a..2b5126f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -362,7 +362,7 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
Parcel data, reply;
audio_devices_t devices = pDevices ? *pDevices : (audio_devices_t)0;
@@ -855,7 +855,7 @@
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = (audio_channel_mask_t)data.readInt32();
uint32_t latency = data.readInt32();
- audio_policy_output_flags_t flags = (audio_policy_output_flags_t) data.readInt32();
+ audio_output_flags_t flags = (audio_output_flags_t) data.readInt32();
audio_io_handle_t output = openOutput(module,
&devices,
&samplingRate,
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 5040bd9..7aab8d6 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -124,7 +124,7 @@
uint32_t samplingRate,
audio_format_t format,
uint32_t channels,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -418,8 +418,8 @@
uint32_t samplingRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
uint32_t channels = data.readInt32();
- audio_policy_output_flags_t flags =
- static_cast <audio_policy_output_flags_t>(data.readInt32());
+ audio_output_flags_t flags =
+ static_cast <audio_output_flags_t>(data.readInt32());
audio_io_handle_t output = getOutput(stream,
samplingRate,
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index cb5c7f3..58c6d38 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -93,7 +93,10 @@
} break;
case START: {
CHECK_INTERFACE(IAudioRecord, data, reply);
- reply->writeInt32(start(data.readInt32(), data.readInt32(), data.readInt32()));
+ pid_t tid = (pid_t) data.readInt32();
+ int event = data.readInt32();
+ int triggerSession = data.readInt32();
+ reply->writeInt32(start(tid, event, triggerSession));
return NO_ERROR;
} break;
case STOP: {
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 52aee49..59e538f 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -94,7 +94,7 @@
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_out_mask_from_count(pLibConfig->numChannels),
mTrackBufferSize,
- AUDIO_POLICY_OUTPUT_FLAG_NONE);
+ AUDIO_OUTPUT_FLAG_NONE);
// create render and playback thread
{
diff --git a/media/libmedia/SoundPool.cpp b/media/libmedia/SoundPool.cpp
index 3b7b96a..4b318ed 100644
--- a/media/libmedia/SoundPool.cpp
+++ b/media/libmedia/SoundPool.cpp
@@ -608,10 +608,10 @@
// do not create a new audio track if current track is compatible with sample parameters
#ifdef USE_SHARED_MEM_BUFFER
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channels, sample->getIMemory(), AUDIO_POLICY_OUTPUT_FLAG_NONE, callback, userData);
+ channels, sample->getIMemory(), AUDIO_OUTPUT_FLAG_NONE, callback, userData);
#else
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channels, frameCount, AUDIO_POLICY_OUTPUT_FLAG_NONE, callback, userData,
+ channels, frameCount, AUDIO_OUTPUT_FLAG_NONE, callback, userData,
bufferFrames);
#endif
oldTrack = mAudioTrack;
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 717d316..253602d 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -1020,15 +1020,15 @@
ALOGV("Create Track: %p", mpAudioTrack);
mpAudioTrack->set(mStreamType,
- 0,
+ 0, // sampleRate
AUDIO_FORMAT_PCM_16_BIT,
AUDIO_CHANNEL_OUT_MONO,
- 0,
- AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ 0, // frameCount
+ AUDIO_OUTPUT_FLAG_FAST,
audioCallback,
- this,
- 0,
- 0,
+ this, // user
+ 0, // notificationFrames
+ 0, // sharedBuffer
mThreadCanCallJava);
if (mpAudioTrack->initCheck() != NO_ERROR) {
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index 4491f2b..574ae71 100644
--- a/media/libmediaplayerservice/Crypto.cpp
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -32,6 +32,7 @@
Crypto::Crypto()
: mInitCheck(NO_INIT),
mLibHandle(NULL),
+ mFactory(NULL),
mPlugin(NULL) {
mInitCheck = init();
}
@@ -57,6 +58,8 @@
mLibHandle = dlopen("libdrmdecrypt.so", RTLD_NOW);
if (mLibHandle == NULL) {
+ ALOGE("Unable to locate libdrmdecrypt.so");
+
return ERROR_UNSUPPORTED;
}
@@ -66,6 +69,12 @@
if (createCryptoFactory == NULL
|| ((mFactory = createCryptoFactory()) == NULL)) {
+ if (createCryptoFactory == NULL) {
+ ALOGE("Unable to find symbol 'createCryptoFactory'.");
+ } else {
+ ALOGE("createCryptoFactory() failed.");
+ }
+
dlclose(mLibHandle);
mLibHandle = NULL;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8bde8f1..7254599 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1587,7 +1587,7 @@
format,
channelMask,
frameCount,
- AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ AUDIO_OUTPUT_FLAG_NONE,
CallbackWrapper,
mCallbackData,
0, // notification frames
@@ -1599,7 +1599,7 @@
format,
channelMask,
frameCount,
- AUDIO_POLICY_OUTPUT_FLAG_NONE,
+ AUDIO_OUTPUT_FLAG_NONE,
NULL,
NULL,
0,
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 0f816e7..468fe2c 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -151,7 +151,7 @@
mAudioTrack = new AudioTrack(
AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask,
- 0, AUDIO_POLICY_OUTPUT_FLAG_NONE, &AudioCallback, this, 0);
+ 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0);
if ((err = mAudioTrack->initCheck()) != OK) {
delete mAudioTrack;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 120a410..b67476b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -356,6 +356,7 @@
int64_t totalBitRate = 0;
+ mExtractor = extractor;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i);
@@ -443,7 +444,7 @@
}
}
} else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
- addTextSource(extractor->getTrack(i));
+ addTextSource(i, extractor->getTrack(i));
}
}
@@ -507,6 +508,7 @@
mCachedSource.clear();
mAudioTrack.clear();
mVideoTrack.clear();
+ mExtractor.clear();
// Shutdown audio first, so that the respone to the reset request
// appears to happen instantaneously as far as the user is concerned
@@ -1331,7 +1333,7 @@
mAudioTrack = source;
}
-void AwesomePlayer::addTextSource(const sp<MediaSource>& source) {
+void AwesomePlayer::addTextSource(size_t trackIndex, const sp<MediaSource>& source) {
Mutex::Autolock autoLock(mTimedTextLock);
CHECK(source != NULL);
@@ -1339,7 +1341,7 @@
mTextDriver = new TimedTextDriver(mListener);
}
- mTextDriver->addInBandTextSource(source);
+ mTextDriver->addInBandTextSource(trackIndex, source);
}
status_t AwesomePlayer::initAudioDecoder() {
@@ -2254,6 +2256,94 @@
}
}
+status_t AwesomePlayer::getTrackInfo(Parcel *reply) const {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ reply->writeInt32(mTextDriver->countExternalTracks() +
+ mExtractor->countTracks());
+ for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
+ sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+
+ const char *_mime;
+ CHECK(meta->findCString(kKeyMIMEType, &_mime));
+
+ String8 mime = String8(_mime);
+
+ reply->writeInt32(2); // 2 fields
+
+ if (!strncasecmp(mime.string(), "video/", 6)) {
+ reply->writeInt32(MEDIA_TRACK_TYPE_VIDEO);
+ } else if (!strncasecmp(mime.string(), "audio/", 6)) {
+ reply->writeInt32(MEDIA_TRACK_TYPE_AUDIO);
+ } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
+ reply->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
+ } else {
+ reply->writeInt32(MEDIA_TRACK_TYPE_UNKNOWN);
+ }
+
+ const char *lang;
+ if (meta->findCString(kKeyMediaLanguage, &lang)) {
+ reply->writeString16(String16(lang));
+ } else {
+ reply->writeString16(String16(""));
+ }
+ }
+
+ mTextDriver->getExternalTrackInfo(reply);
+ return OK;
+}
+
+// FIXME:
+// At present, only timed text track is able to be selected or unselected.
+status_t AwesomePlayer::selectTrack(size_t trackIndex, bool select) {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ if (trackIndex >= mExtractor->countTracks()
+ + mTextDriver->countExternalTracks()) {
+ return BAD_VALUE;
+ }
+
+ if (trackIndex < mExtractor->countTracks()) {
+ sp<MetaData> meta = mExtractor->getTrackMetaData(trackIndex);
+ const char *_mime;
+ CHECK(meta->findCString(kKeyMIMEType, &_mime));
+ String8 mime = String8(_mime);
+
+ if (strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
+ return ERROR_UNSUPPORTED;
+ }
+ }
+
+ status_t err = OK;
+ if (select) {
+ err = mTextDriver->selectTrack(trackIndex);
+ if (err == OK) {
+ modifyFlags(TEXTPLAYER_INITIALIZED, SET);
+ if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
+ mTextDriver->start();
+ modifyFlags(TEXT_RUNNING, SET);
+ }
+ }
+ } else {
+ err = mTextDriver->unselectTrack(trackIndex);
+ if (err == OK) {
+ modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
+ modifyFlags(TEXT_RUNNING, CLEAR);
+ }
+ }
+ return err;
+}
+
+size_t AwesomePlayer::countTracks() const {
+ return mExtractor->countTracks() + mTextDriver->countExternalTracks();
+}
+
status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) {
if (NULL == reply) {
return android::BAD_VALUE;
@@ -2266,12 +2356,7 @@
switch(methodId) {
case INVOKE_ID_GET_TRACK_INFO:
{
- Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextDriver == NULL) {
- return INVALID_OPERATION;
- }
- mTextDriver->getTrackInfo(reply);
- return OK;
+ return getTrackInfo(reply);
}
case INVOKE_ID_ADD_EXTERNAL_SOURCE:
{
@@ -2282,7 +2367,8 @@
// String values written in Parcel are UTF-16 values.
String8 uri(request.readString16());
String8 mimeType(request.readString16());
- return mTextDriver->addOutOfBandTextSource(uri, mimeType);
+ size_t nTracks = countTracks();
+ return mTextDriver->addOutOfBandTextSource(nTracks, uri, mimeType);
}
case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
{
@@ -2294,40 +2380,19 @@
off64_t offset = request.readInt64();
off64_t length = request.readInt64();
String8 mimeType(request.readString16());
+ size_t nTracks = countTracks();
return mTextDriver->addOutOfBandTextSource(
- fd, offset, length, mimeType);
+ nTracks, fd, offset, length, mimeType);
}
case INVOKE_ID_SELECT_TRACK:
{
- Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextDriver == NULL) {
- return INVALID_OPERATION;
- }
-
- status_t err = mTextDriver->selectTrack(
- request.readInt32());
- if (err == OK) {
- modifyFlags(TEXTPLAYER_INITIALIZED, SET);
- if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
- mTextDriver->start();
- modifyFlags(TEXT_RUNNING, SET);
- }
- }
- return err;
+ int trackIndex = request.readInt32();
+ return selectTrack(trackIndex, true);
}
case INVOKE_ID_UNSELECT_TRACK:
{
- Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextDriver == NULL) {
- return INVALID_OPERATION;
- }
- status_t err = mTextDriver->unselectTrack(
- request.readInt32());
- if (err == OK) {
- modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
- modifyFlags(TEXT_RUNNING, CLEAR);
- }
- return err;
+ int trackIndex = request.readInt32();
+ return selectTrack(trackIndex, false);
}
default:
{
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index fd3f892..3ddad93 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -182,7 +182,7 @@
int32_t cameraId) {
if (camera == 0) {
- mCamera = Camera::connect(cameraId, false, false);
+ mCamera = Camera::connect(cameraId);
if (mCamera == 0) return -EBUSY;
mCameraFlags &= ~FLAGS_HOT_CAMERA;
} else {
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index d0a7880..3400724 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -133,16 +133,46 @@
// static
sp<DataSource> DataSource::CreateFromURI(
const char *uri, const KeyedVector<String8, String8> *headers) {
+ bool isWidevine = !strncasecmp("widevine://", uri, 11);
+
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new FileSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7)
- || !strncasecmp("https://", uri, 8)) {
+ || !strncasecmp("https://", uri, 8)
+ || isWidevine) {
sp<HTTPBase> httpSource = HTTPBase::Create();
+
+ String8 tmp;
+ if (isWidevine) {
+ tmp = String8("http://");
+ tmp.append(uri + 11);
+
+ uri = tmp.string();
+ }
+
if (httpSource->connect(uri, headers) != OK) {
return NULL;
}
- source = new NuCachedSource2(httpSource);
+
+ if (!isWidevine) {
+ String8 cacheConfig;
+ bool disconnectAtHighwatermark;
+ if (headers != NULL) {
+ KeyedVector<String8, String8> copy = *headers;
+ NuCachedSource2::RemoveCacheSpecificHeaders(
+ ©, &cacheConfig, &disconnectAtHighwatermark);
+ }
+
+ source = new NuCachedSource2(
+ httpSource,
+ cacheConfig.isEmpty() ? NULL : cacheConfig.string());
+ } else {
+ // We do not want that prefetching, caching, datasource wrapper
+ // in the widevine:// case.
+ source = httpSource;
+ }
+
# if CHROMIUM_AVAILABLE
} else if (!strncasecmp("data:", uri, 5)) {
source = new DataUriSource(uri);
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 224ec33..00bb74f 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -21,11 +21,13 @@
#include <media/stagefright/NuMediaExtractor.h>
#include "include/ESDS.h"
+#include "include/WVMExtractor.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
@@ -51,14 +53,59 @@
mSelectedTracks.clear();
}
-status_t NuMediaExtractor::setDataSource(const char *path) {
- sp<DataSource> dataSource = DataSource::CreateFromURI(path);
+status_t NuMediaExtractor::setDataSource(
+ const char *path, const KeyedVector<String8, String8> *headers) {
+ if (mImpl != NULL) {
+ return -EINVAL;
+ }
+
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(path, headers);
if (dataSource == NULL) {
return -ENOENT;
}
- mImpl = MediaExtractor::Create(dataSource);
+ if (!strncasecmp("widevine://", path, 11)) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
+
+ if (!success
+ || strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<WVMExtractor> extractor = new WVMExtractor(dataSource);
+ extractor->setAdaptiveStreamingMode(true);
+
+ mImpl = extractor;
+ } else {
+ mImpl = MediaExtractor::Create(dataSource);
+ }
+
+ if (mImpl == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ return OK;
+}
+
+status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
+ if (mImpl != NULL) {
+ return -EINVAL;
+ }
+
+ sp<FileSource> fileSource = new FileSource(dup(fd), offset, size);
+
+ status_t err = fileSource->initCheck();
+ if (err != OK) {
+ return err;
+ }
+
+ mImpl = MediaExtractor::Create(fileSource);
if (mImpl == NULL) {
return ERROR_UNSUPPORTED;
@@ -91,6 +138,11 @@
sp<AMessage> msg = new AMessage;
msg->setString("mime", mime);
+ int64_t durationUs;
+ if (meta->findInt64(kKeyDuration, &durationUs)) {
+ msg->setInt64("durationUs", durationUs);
+ }
+
if (!strncasecmp("video/", mime, 6)) {
int32_t width, height;
CHECK(meta->findInt32(kKeyWidth, &width));
@@ -237,20 +289,6 @@
msg->setBuffer("csd-1", buffer);
}
- if (meta->findData(kKeyEMM, &type, &data, &size)) {
- sp<ABuffer> emm = new ABuffer(size);
- memcpy(emm->data(), data, size);
-
- msg->setBuffer("emm", emm);
- }
-
- if (meta->findData(kKeyECM, &type, &data, &size)) {
- sp<ABuffer> ecm = new ABuffer(size);
- memcpy(ecm->data(), data, size);
-
- msg->setBuffer("ecm", ecm);
- }
-
*format = msg;
return OK;
@@ -286,7 +324,6 @@
info->mFinalResult = OK;
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
- info->mSampleFlags = 0;
info->mTrackFlags = 0;
const char *mime;
@@ -308,7 +345,6 @@
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
- info->mSampleFlags = 0;
}
}
}
@@ -327,7 +363,6 @@
info->mSample->release();
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
- info->mSampleFlags = 0;
}
} else if (info->mFinalResult != OK) {
continue;
@@ -345,25 +380,11 @@
info->mFinalResult = err;
info->mSampleTimeUs = -1ll;
- info->mSampleFlags = 0;
continue;
} else {
CHECK(info->mSample != NULL);
CHECK(info->mSample->meta_data()->findInt64(
kKeyTime, &info->mSampleTimeUs));
-
- info->mSampleFlags = 0;
-
- int32_t val;
- if (info->mSample->meta_data()->findInt32(
- kKeyIsSyncFrame, &val) && val != 0) {
- info->mSampleFlags |= SAMPLE_FLAG_SYNC;
- }
-
- if (info->mSample->meta_data()->findInt32(
- kKeyScrambling, &val) && val != 0) {
- info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED;
- }
}
}
@@ -377,7 +398,13 @@
}
status_t NuMediaExtractor::seekTo(int64_t timeUs) {
- return fetchTrackSamples(timeUs);
+ ssize_t minIndex = fetchTrackSamples(timeUs);
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ return OK;
}
status_t NuMediaExtractor::advance() {
@@ -466,7 +493,9 @@
return OK;
}
-status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
+status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
+ *sampleMeta = NULL;
+
ssize_t minIndex = fetchTrackSamples();
if (minIndex < 0) {
@@ -474,7 +503,7 @@
}
TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
- *sampleFlags = info->mSampleFlags;
+ *sampleMeta = info->mSample->meta_data();
return OK;
}
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index efc71a8..5d72a05 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -55,6 +55,8 @@
mBufferQueue = new BufferQueue(true, MIN_UNDEQUEUED_BUFFERS);
mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight);
mBufferQueue->setSynchronousMode(true);
+ mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER |
+ GRALLOC_USAGE_HW_TEXTURE);
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index dac8106..effe336 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -59,10 +59,14 @@
"_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
if (getInstanceFunc) {
- CHECK(source->DrmInitialization(MEDIA_MIMETYPE_CONTAINER_WVM) != NULL);
- mImpl = (*getInstanceFunc)(source);
- CHECK(mImpl != NULL);
- setDrmFlag(true);
+ if (source->DrmInitialization(
+ MEDIA_MIMETYPE_CONTAINER_WVM) != NULL) {
+ mImpl = (*getInstanceFunc)(source);
+ CHECK(mImpl != NULL);
+ setDrmFlag(true);
+ } else {
+ ALOGE("Drm manager failed to initialize.");
+ }
} else {
ALOGE("Failed to locate GetInstance in libwvm.so");
}
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 5b3d216..2808745 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -12,7 +12,8 @@
frameworks/av/media/libstagefright/include \
frameworks/native/include/media/openmax \
external/aac/libAACdec/include \
- external/aac/libCDK/include \
+ external/aac/libPCMutils/include \
+ external/aac/libFDK/include \
external/aac/libMpegTPDec/include \
external/aac/libSBRdec/include \
external/aac/libSYS/include
@@ -20,7 +21,7 @@
LOCAL_CFLAGS :=
LOCAL_STATIC_LIBRARIES := \
- libAACdec libMpegTPDec libSBRdec libCDK libSYS
+ libAACdec libMpegTPDec libSBRdec libPCMutils libFDK libSYS
LOCAL_SHARED_LIBRARIES := \
libstagefright_omx libstagefright_foundation libutils
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 4589d37..27cf5b9 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -26,23 +26,6 @@
namespace android {
-static Mutex gAACLibraryLock;
-static int gAACLibraryCount = 0;
-
-void initializeAACLibrary() {
- Mutex::Autolock autoLock(gAACLibraryLock);
- if (gAACLibraryCount++ == 0) {
- CDKprolog();
- }
-}
-
-void cleanupAACLibrary() {
- Mutex::Autolock autoLock(gAACLibraryLock);
- if (--gAACLibraryCount == 0) {
- CDKepilog();
- }
-}
-
template<class T>
static void InitOMXParams(T *params) {
params->nSize = sizeof(T);
@@ -63,17 +46,16 @@
mIsADTS(false),
mInputBufferCount(0),
mSignalledError(false),
+ mInputDiscontinuity(false),
mAnchorTimeUs(0),
mNumSamplesOutput(0),
mOutputPortSettingsChange(NONE) {
- initializeAACLibrary();
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
- cleanupAACLibrary();
}
void SoftAAC2::initPorts() {
@@ -102,7 +84,7 @@
def.eDir = OMX_DirOutput;
def.nBufferCountMin = kNumBuffers;
def.nBufferCountActual = def.nBufferCountMin;
- def.nBufferSize = 8192;
+ def.nBufferSize = 8192 * 2;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
def.eDomain = OMX_PortDomainAudio;
@@ -183,6 +165,10 @@
pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+ pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF;
+ pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
+ pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS;
+ pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS;
if (!isConfigured()) {
pcmParams->nChannels = 1;
@@ -360,15 +346,17 @@
INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
bytesValid[0] = inBufferLength[0];
+ int flags = mInputDiscontinuity ? AACDEC_INTR : 0;
int prevSampleRate = mStreamInfo->sampleRate;
decoderErr = aacDecoder_Fill(mAACDecoder,
- inBuffer,
- inBufferLength,
- bytesValid);
+ inBuffer,
+ inBufferLength,
+ bytesValid);
decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
outBuffer,
outHeader->nAllocLen,
- /* flags */ 0);
+ flags);
+ mInputDiscontinuity = false;
/*
* AAC+/eAAC+ streams can be signalled in two ways: either explicitly
@@ -447,13 +435,9 @@
void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) {
if (portIndex == 0) {
-
// Make sure that the next buffer output does not still
// depend on fragments from the last one decoded.
- aacDecoder_DecodeFrame(mAACDecoder,
- NULL,
- 0,
- AACDEC_FLUSH);
+ mInputDiscontinuity = true;
}
}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 828b34e..d93685c 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -52,6 +52,7 @@
bool mIsADTS;
size_t mInputBufferCount;
bool mSignalledError;
+ bool mInputDiscontinuity;
int64_t mAnchorTimeUs;
int64_t mNumSamplesOutput;
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 06e9468..9115f91 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -235,6 +235,7 @@
mutable Mutex mTimedTextLock;
sp<WVMExtractor> mWVMExtractor;
+ sp<MediaExtractor> mExtractor;
status_t setDataSource_l(
const char *uri,
@@ -257,7 +258,7 @@
void setVideoSource(sp<MediaSource> source);
status_t initVideoDecoder(uint32_t flags = 0);
- void addTextSource(const sp<MediaSource>& source);
+ void addTextSource(size_t trackIndex, const sp<MediaSource>& source);
void onStreamDone();
@@ -318,6 +319,14 @@
Vector<TrackStat> mTracks;
} mStats;
+ status_t getTrackInfo(Parcel* reply) const;
+
+ // when select is true, the given track is selected.
+ // otherwise, the given track is unselected.
+ status_t selectTrack(size_t trackIndex, bool select);
+
+ size_t countTracks() const;
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index e1ac53c..d708ba6 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -117,12 +117,6 @@
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
- int32_t scrambling;
- if (buffer->meta()->findInt32("scrambling", &scrambling)
- && scrambling != 0) {
- mediaBuffer->meta_data()->setInt32(kKeyScrambling, scrambling);
- }
-
*out = mediaBuffer;
return OK;
}
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 10713fe..466f521 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -486,6 +486,10 @@
mSTC = new SurfaceTextureClient(iST);
mANW = mSTC;
+ if (mEglSurface != EGL_NO_SURFACE) {
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
+ mEglSurface = EGL_NO_SURFACE;
+ }
mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
mANW.get(), NULL);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
@@ -783,6 +787,11 @@
DummyRecorder writer(mSMS);
writer.start();
+ if (mEglSurface != EGL_NO_SURFACE) {
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface));
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
mANW.get(), NULL);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index a99d882..e26f517 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -52,16 +52,13 @@
TimedTextDriver::~TimedTextDriver() {
mTextSourceVector.clear();
+ mTextSourceTypeVector.clear();
mLooper->stop();
}
-status_t TimedTextDriver::selectTrack_l(int32_t index) {
- if (index >= (int)(mTextSourceVector.size())) {
- return BAD_VALUE;
- }
-
+status_t TimedTextDriver::selectTrack_l(size_t index) {
sp<TimedTextSource> source;
- source = mTextSourceVector.itemAt(index);
+ source = mTextSourceVector.valueFor(index);
mPlayer->setDataSource(source);
if (mState == UNINITIALIZED) {
mState = PAUSED;
@@ -108,7 +105,7 @@
return OK;
}
-status_t TimedTextDriver::selectTrack(int32_t index) {
+status_t TimedTextDriver::selectTrack(size_t index) {
status_t ret = OK;
Mutex::Autolock autoLock(mLock);
switch (mState) {
@@ -130,7 +127,7 @@
return ret;
}
-status_t TimedTextDriver::unselectTrack(int32_t index) {
+status_t TimedTextDriver::unselectTrack(size_t index) {
if (mCurrentTrackIndex != index) {
return INVALID_OPERATION;
}
@@ -149,19 +146,21 @@
}
status_t TimedTextDriver::addInBandTextSource(
- const sp<MediaSource>& mediaSource) {
+ size_t trackIndex, const sp<MediaSource>& mediaSource) {
sp<TimedTextSource> source =
TimedTextSource::CreateTimedTextSource(mediaSource);
if (source == NULL) {
return ERROR_UNSUPPORTED;
}
Mutex::Autolock autoLock(mLock);
- mTextSourceVector.add(source);
+ mTextSourceVector.add(trackIndex, source);
+ mTextSourceTypeVector.add(true);
return OK;
}
status_t TimedTextDriver::addOutOfBandTextSource(
- const char *uri, const char *mimeType) {
+ size_t trackIndex, const char *uri, const char *mimeType) {
+
// To support local subtitle file only for now
if (strncasecmp("file://", uri, 7)) {
ALOGE("uri('%s') is not a file", uri);
@@ -170,11 +169,11 @@
sp<DataSource> dataSource =
DataSource::CreateFromURI(uri);
- return createOutOfBandTextSource(mimeType, dataSource);
+ return createOutOfBandTextSource(trackIndex, mimeType, dataSource);
}
status_t TimedTextDriver::addOutOfBandTextSource(
- int fd, off64_t offset, off64_t length, const char *mimeType) {
+ size_t trackIndex, int fd, off64_t offset, off64_t length, const char *mimeType) {
if (fd < 0) {
ALOGE("Invalid file descriptor: %d", fd);
@@ -182,11 +181,13 @@
}
sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
- return createOutOfBandTextSource(mimeType, dataSource);
+ return createOutOfBandTextSource(trackIndex, mimeType, dataSource);
}
status_t TimedTextDriver::createOutOfBandTextSource(
- const char *mimeType, const sp<DataSource>& dataSource) {
+ size_t trackIndex,
+ const char *mimeType,
+ const sp<DataSource>& dataSource) {
if (dataSource == NULL) {
return ERROR_UNSUPPORTED;
@@ -199,28 +200,40 @@
}
if (source == NULL) {
+ ALOGE("Failed to create timed text source");
return ERROR_UNSUPPORTED;
}
Mutex::Autolock autoLock(mLock);
- mTextSourceVector.add(source);
+ mTextSourceVector.add(trackIndex, source);
+ mTextSourceTypeVector.add(false);
return OK;
}
-void TimedTextDriver::getTrackInfo(Parcel *parcel) {
+size_t TimedTextDriver::countExternalTracks() const {
+ size_t nTracks = 0;
+ for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) {
+ if (!mTextSourceTypeVector[i]) {
+ ++nTracks;
+ }
+ }
+ return nTracks;
+}
+
+void TimedTextDriver::getExternalTrackInfo(Parcel *parcel) {
Mutex::Autolock autoLock(mLock);
- Vector<sp<TimedTextSource> >::const_iterator iter;
- parcel->writeInt32(mTextSourceVector.size());
- for (iter = mTextSourceVector.begin();
- iter != mTextSourceVector.end(); ++iter) {
- sp<MetaData> meta = (*iter)->getFormat();
+ for (size_t i = 0, n = mTextSourceTypeVector.size(); i < n; ++i) {
+ if (mTextSourceTypeVector[i]) {
+ continue;
+ }
+
+ sp<MetaData> meta = mTextSourceVector.valueAt(i)->getFormat();
// There are two fields.
parcel->writeInt32(2);
// track type.
parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
-
const char *lang = "und";
if (meta != NULL) {
meta->findCString(kKeyMediaLanguage, &lang);
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 2cf77ce..d52ed42 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -34,6 +34,8 @@
# AudioResamplerSinc.cpp.arm
# AudioResamplerCubic.cpp.arm
+LOCAL_SRC_FILES += StateQueue.cpp
+
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
@@ -60,4 +62,10 @@
LOCAL_MODULE:= libaudioflinger
+LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp
+
+#LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
+
+LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 9b4fb7a..99dcf45 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -138,25 +138,31 @@
}
#endif
-static int load_audio_interface(const char *if_name, const hw_module_t **mod,
- audio_hw_device_t **dev)
+static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
+ const hw_module_t *mod;
int rc;
- rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, mod);
- if (rc)
+ rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
+ ALOGE_IF(rc, "%s couldn't load audio hw module %s.%s (%s)", __func__,
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ if (rc) {
goto out;
-
- rc = audio_hw_device_open(*mod, dev);
- ALOGE_IF(rc, "couldn't open audio hw device in %s.%s (%s)",
- AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
- if (rc)
+ }
+ rc = audio_hw_device_open(mod, dev);
+ ALOGE_IF(rc, "%s couldn't open audio hw device in %s.%s (%s)", __func__,
+ AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
+ if (rc) {
goto out;
-
+ }
+ if ((*dev)->common.version != AUDIO_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
+ rc = BAD_VALUE;
+ goto out;
+ }
return 0;
out:
- *mod = NULL;
*dev = NULL;
return rc;
}
@@ -914,7 +920,12 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- size_t size = mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
+ struct audio_config config = {
+ sample_rate: sampleRate,
+ channel_mask: audio_channel_in_mask_from_count(channelCount),
+ format: format,
+ };
+ size_t size = mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
return size;
}
@@ -1597,7 +1608,7 @@
// FIXME test that MixerThread for this fast track has a capable output HAL
// FIXME add a permission test also?
) ) {
- ALOGW("AUDIO_POLICY_OUTPUT_FLAG_FAST denied");
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied");
flags &= ~IAudioFlinger::TRACK_FAST;
}
@@ -2653,13 +2664,7 @@
// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask)
{
- int name = mAudioMixer->getTrackName();
- if (name >= 0) {
- mAudioMixer->setParameter(name,
- AudioMixer::TRACK,
- AudioMixer::CHANNEL_MASK, (void *)channelMask);
- }
- return name;
+ return mAudioMixer->getTrackName(channelMask);
}
// deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -3496,14 +3501,12 @@
int8_t *bufferEnd = bufferStart + frames * frameSize;
// Check validity of returned pointer in case the track control block would have been corrupted.
- if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
- ((unsigned long)bufferStart & (unsigned long)(frameSize - 1))) {
- ALOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \
- server %u, serverBase %u, user %u, userBase %u",
+ ALOG_ASSERT(!(bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd),
+ "TrackBase::getBuffer buffer out of range:\n"
+ " start: %p, end %p , mBuffer %p mBufferEnd %p\n"
+ " server %u, serverBase %u, user %u, userBase %u, frameSize %d",
bufferStart, bufferEnd, mBuffer, mBufferEnd,
- cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
- return NULL;
- }
+ cblk->server, cblk->serverBase, cblk->user, cblk->userBase, frameSize);
return bufferStart;
}
@@ -3687,7 +3690,7 @@
mName, IPCThreadState::self()->getCallingPid(), mSessionId, tid);
// check for use case 2 with missing callback
if (isFastTrack() && (mSharedBuffer == 0) && (tid == 0)) {
- ALOGW("AUDIO_POLICY_OUTPUT_FLAG_FAST denied");
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied");
mFlags &= ~IAudioFlinger::TRACK_FAST;
// FIXME the track must be invalidated and moved to another thread or
// attached directly to the normal mixer now
@@ -4045,11 +4048,11 @@
uint32_t bufBytes = buf.buffer()->size();
uint32_t consumedAlready = buf.position();
- ALOG_ASSERT(consumedAlready <= bufFrames,
+ ALOG_ASSERT(consumedAlready <= bufBytes,
"Bad bookkeeping while updating frames pending. Timed buffer is"
" only %u bytes long, but claims to have consumed %u"
" bytes. (update reason: \"%s\")",
- bufFrames, consumedAlready, logTag);
+ bufBytes, consumedAlready, logTag);
uint32_t bufFrames = (bufBytes - consumedAlready) / mCblk->frameSize;
ALOG_ASSERT(mFramesPendingInQueue >= bufFrames,
@@ -5743,10 +5746,9 @@
}
}
- const hw_module_t *mod;
audio_hw_device_t *dev;
- int rc = load_audio_interface(name, &mod, &dev);
+ int rc = load_audio_interface(name, &dev);
if (rc) {
ALOGI("loadHwModule() error %d loading module %s ", rc, name);
return 0;
@@ -5772,7 +5774,7 @@
mAudioHwDevs.add(handle, new AudioHwDevice(name, dev));
ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d",
- name, mod->name, mod->id, handle);
+ name, dev->common.module->name, dev->common.module->id, handle);
return handle;
@@ -5784,23 +5786,24 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
status_t status;
PlaybackThread *thread = NULL;
- uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
- audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT;
- audio_channel_mask_t channelMask = pChannelMask ? *pChannelMask : 0;
- uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
- audio_stream_out_t *outStream;
+ struct audio_config config = {
+ sample_rate: pSamplingRate ? *pSamplingRate : 0,
+ channel_mask: pChannelMask ? *pChannelMask : 0,
+ format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
+ };
+ audio_stream_out_t *outStream = NULL;
audio_hw_device_t *outHwDev;
ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
module,
(pDevices != NULL) ? (int)*pDevices : 0,
- samplingRate,
- format,
- channelMask,
+ config.sample_rate,
+ config.format,
+ config.channel_mask,
flags);
if (pDevices == NULL || *pDevices == 0) {
@@ -5813,24 +5816,31 @@
if (outHwDev == NULL)
return 0;
+ audio_io_handle_t id = nextUniqueId();
+
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status = outHwDev->open_output_stream(outHwDev, *pDevices, &format,
- &channelMask, &samplingRate, &outStream);
+
+ status = outHwDev->open_output_stream(outHwDev,
+ id,
+ *pDevices,
+ (audio_output_flags_t)flags,
+ &config,
+ &outStream);
+
mHardwareStatus = AUDIO_HW_IDLE;
ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
outStream,
- samplingRate,
- format,
- channelMask,
+ config.sample_rate,
+ config.format,
+ config.channel_mask,
status);
- if (outStream != NULL) {
+ if (status == NO_ERROR && outStream != NULL) {
AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
- audio_io_handle_t id = nextUniqueId();
- if ((flags & AUDIO_POLICY_OUTPUT_FLAG_DIRECT) ||
- (format != AUDIO_FORMAT_PCM_16_BIT) ||
- (channelMask != AUDIO_CHANNEL_OUT_STEREO)) {
+ if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+ (config.format != AUDIO_FORMAT_PCM_16_BIT) ||
+ (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
ALOGV("openOutput() created direct output: ID %d thread %p", id, thread);
} else {
@@ -5839,16 +5849,16 @@
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = samplingRate;
- if (pFormat != NULL) *pFormat = format;
- if (pChannelMask != NULL) *pChannelMask = channelMask;
+ if (pSamplingRate != NULL) *pSamplingRate = config.sample_rate;
+ if (pFormat != NULL) *pFormat = config.format;
+ if (pChannelMask != NULL) *pChannelMask = config.channel_mask;
if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
// the first primary output opened designates the primary hw device
- if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_POLICY_OUTPUT_FLAG_PRIMARY)) {
+ if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
ALOGI("Using module %d has the primary audio interface", module);
mPrimaryHardwareDev = outHwDev;
@@ -5995,13 +6005,15 @@
{
status_t status;
RecordThread *thread = NULL;
- uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
- audio_format_t format = pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT;
- audio_channel_mask_t channelMask = pChannelMask ? *pChannelMask : 0;
- uint32_t reqSamplingRate = samplingRate;
- audio_format_t reqFormat = format;
- audio_channel_mask_t reqChannels = channelMask;
- audio_stream_in_t *inStream;
+ struct audio_config config = {
+ sample_rate: pSamplingRate ? *pSamplingRate : 0,
+ channel_mask: pChannelMask ? *pChannelMask : 0,
+ format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
+ };
+ uint32_t reqSamplingRate = config.sample_rate;
+ audio_format_t reqFormat = config.format;
+ audio_channel_mask_t reqChannels = config.channel_mask;
+ audio_stream_in_t *inStream = NULL;
audio_hw_device_t *inHwDev;
if (pDevices == NULL || *pDevices == 0) {
@@ -6014,35 +6026,32 @@
if (inHwDev == NULL)
return 0;
- status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
- &channelMask, &samplingRate,
- (audio_in_acoustics_t)0,
+ audio_io_handle_t id = nextUniqueId();
+
+ status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config,
&inStream);
ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d",
inStream,
- samplingRate,
- format,
- channelMask,
+ config.sample_rate,
+ config.format,
+ config.channel_mask,
status);
// 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.
- if (inStream == NULL && status == BAD_VALUE &&
- reqFormat == format && format == AUDIO_FORMAT_PCM_16_BIT &&
- (samplingRate <= 2 * reqSamplingRate) &&
- (popcount(channelMask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) {
+ if (status == BAD_VALUE &&
+ reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
+ (config.sample_rate <= 2 * reqSamplingRate) &&
+ (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) {
ALOGV("openInput() reopening with proposed sampling rate and channels");
- status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
- &channelMask, &samplingRate,
- (audio_in_acoustics_t)0,
- &inStream);
+ inStream = NULL;
+ status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config, &inStream);
}
- if (inStream != NULL) {
+ if (status == NO_ERROR && inStream != NULL) {
AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
- audio_io_handle_t id = nextUniqueId();
// Start record thread
// RecorThread require both input and output device indication to forward to audio
// pre processing modules
@@ -6056,7 +6065,7 @@
mRecordThreads.add(id, thread);
ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
- if (pFormat != NULL) *pFormat = format;
+ if (pFormat != NULL) *pFormat = config.format;
if (pChannelMask != NULL) *pChannelMask = reqChannels;
input->stream->common.standby(&input->stream->common);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 9e6201a..b1c5554 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -144,7 +144,7 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags);
+ audio_output_flags_t flags);
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2);
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 399d987..0c8b3ce 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -69,7 +69,7 @@
res = (*mDownmixHandle)->process(mDownmixHandle,
&mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
- ALOGV("getNextBuffer is downmixing");
+ //ALOGV("getNextBuffer is downmixing");
}
return res;
} else {
@@ -79,7 +79,7 @@
}
void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
- ALOGV("DownmixerBufferProvider::releaseBuffer()");
+ //ALOGV("DownmixerBufferProvider::releaseBuffer()");
if (this->mTrackBufferProvider != NULL) {
mTrackBufferProvider->releaseBuffer(pBuffer);
} else {
@@ -98,7 +98,7 @@
{
// AudioMixer is not yet capable of multi-channel beyond stereo
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(2 == MAX_NUM_CHANNELS);
-
+
ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u",
maxNumTracks, MAX_NUM_TRACKS);
@@ -119,6 +119,8 @@
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
// FIXME redundant per track
t->localTimeFreq = lc.getLocalFreq();
+ t->resampler = NULL;
+ t->downmixerBufferProvider = NULL;
t++;
}
@@ -150,13 +152,14 @@
track_t* t = mState.tracks;
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
delete t->resampler;
+ delete t->downmixerBufferProvider;
t++;
}
delete [] mState.outputTemp;
delete [] mState.resampleTemp;
}
-int AudioMixer::getTrackName()
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask)
{
uint32_t names = (~mTrackNames) & mConfiguredNames;
if (names != 0) {
@@ -196,7 +199,13 @@
t->mainBuffer = NULL;
t->auxBuffer = NULL;
// see t->localTimeFreq in constructor above
- return TRACK0 + n;
+
+ status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
+ if (status == OK) {
+ return TRACK0 + n;
+ }
+ ALOGE("AudioMixer::getTrackName(0x%x) failed, error preparing track for downmix",
+ channelMask);
}
return -1;
}
@@ -209,16 +218,43 @@
}
}
+status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask)
+{
+ uint32_t channelCount = popcount(mask);
+ ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
+ status_t status = OK;
+ if (channelCount > MAX_NUM_CHANNELS) {
+ pTrack->channelMask = mask;
+ pTrack->channelCount = channelCount;
+ ALOGV("initTrackDownmix(track=%d, mask=0x%x) calls prepareTrackForDownmix()",
+ trackNum, mask);
+ status = prepareTrackForDownmix(pTrack, trackNum);
+ } else {
+ unprepareTrackForDownmix(pTrack, trackNum);
+ }
+ return status;
+}
+
+void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName) {
+ ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName);
+
+ if (pTrack->downmixerBufferProvider != NULL) {
+ // this track had previously been configured with a downmixer, delete it
+ ALOGV(" deleting old downmixer");
+ pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
+ delete pTrack->downmixerBufferProvider;
+ pTrack->downmixerBufferProvider = NULL;
+ } else {
+ ALOGV(" nothing to do, no downmixer to delete");
+ }
+}
+
status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
{
ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask);
- if (pTrack->downmixerBufferProvider != NULL) {
- // this track had previously been configured with a downmixer, reset it
- ALOGV("AudioMixer::prepareTrackForDownmix(%d) deleting old downmixer", trackName);
- pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
- delete pTrack->downmixerBufferProvider;
- }
+ // discard the previous downmixer if there was one
+ unprepareTrackForDownmix(pTrack, trackName);
DownmixerBufferProvider* pDbp = new DownmixerBufferProvider();
int32_t status;
@@ -318,6 +354,7 @@
void AudioMixer::deleteTrackName(int name)
{
+ ALOGV("AudioMixer::deleteTrackName(%d)", name);
name -= TRACK0;
ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
ALOGV("deleteTrackName(%d)", name);
@@ -326,15 +363,12 @@
track.enabled = false;
invalidateState(1<<name);
}
- if (track.resampler != NULL) {
- // delete the resampler
- delete track.resampler;
- track.resampler = NULL;
- track.sampleRate = mSampleRate;
- invalidateState(1<<name);
- }
- track.volumeInc[0] = 0;
- track.volumeInc[1] = 0;
+ // delete the resampler
+ delete track.resampler;
+ track.resampler = NULL;
+ // delete the downmixer
+ unprepareTrackForDownmix(&mState.tracks[name], name);
+
mTrackNames &= ~(1<<name);
}
@@ -358,11 +392,6 @@
track_t& track = mState.tracks[name];
if (track.enabled) {
- if (track.downmixerBufferProvider != NULL) {
- ALOGV("AudioMixer::disable(%d) deleting downmixerBufferProvider", name);
- delete track.downmixerBufferProvider;
- track.downmixerBufferProvider = NULL;
- }
track.enabled = false;
ALOGV("disable(%d)", name);
invalidateState(1 << name);
@@ -389,11 +418,8 @@
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
track.channelMask = mask;
track.channelCount = channelCount;
- if (channelCount > MAX_NUM_CHANNELS) {
- ALOGV("AudioMixer::setParameter(TRACK, CHANNEL_MASK, mask=0x%x count=%d)",
- mask, channelCount);
- status_t status = prepareTrackForDownmix(&mState.tracks[name], name);
- }
+ // the mask has changed, does this track need a downmixer?
+ initTrackDownmix(&mState.tracks[name], name, mask);
ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
invalidateState(1 << name);
}
@@ -438,6 +464,12 @@
track.resetResampler();
invalidateState(1 << name);
break;
+ case REMOVE:
+ delete track.resampler;
+ track.resampler = NULL;
+ track.sampleRate = mSampleRate;
+ invalidateState(1 << name);
+ break;
default:
LOG_FATAL("bad param");
}
@@ -498,12 +530,15 @@
bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
{
- if (value!=devSampleRate || resampler) {
+ if (value != devSampleRate || resampler != NULL) {
if (sampleRate != value) {
sampleRate = value;
if (resampler == NULL) {
resampler = AudioResampler::create(
- format, channelCount, devSampleRate);
+ format,
+ // the resampler sees the number of channels after the downmixer, if any
+ downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
+ devSampleRate);
resampler->setLocalTimeFreq(localTimeFreq);
}
return true;
@@ -629,7 +664,7 @@
resampling = true;
t.hook = track__genericResample;
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
- "Track needs downmix + resample");
+ "Track %d needs downmix + resample", i);
} else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t.hook = track__16BitsMono;
@@ -638,7 +673,7 @@
if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
t.hook = track__16BitsStereo;
ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
- "Track needs downmix");
+ "Track %d needs downmix", i);
}
}
}
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index a04fe95..46deae7 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -70,8 +70,17 @@
AUX_BUFFER = 0x4003,
DOWNMIX_TYPE = 0X4004,
// for target RESAMPLE
- SAMPLE_RATE = 0x4100,
- RESET = 0x4101,
+ SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
+ // parameter 'value' is the new sample rate in Hz.
+ // Only creates a sample rate converter the first time that
+ // the track sample rate is different from the mix sample rate.
+ // If the new sample rate is the same as the mix sample rate,
+ // and a sample rate converter already exists,
+ // then the sample rate converter remains present but is a no-op.
+ RESET = 0x4101, // Reset sample rate converter without changing sample rate.
+ // This clears out the resampler's input buffer.
+ REMOVE = 0x4102, // Remove the sample rate converter on this track name;
+ // the track is restored to the mix sample rate.
// for target RAMP_VOLUME and VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
@@ -82,7 +91,7 @@
// For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
// Allocate a track name. Returns new track name if successful, -1 on failure.
- int getTrackName();
+ int getTrackName(audio_channel_mask_t channelMask);
// Free an allocated track by name
void deleteTrackName(int name);
@@ -237,8 +246,13 @@
// indicates whether a downmix effect has been found and is usable by this mixer
static bool isMultichannelCapable;
+ // Call after changing either the enabled status of a track, or parameters of an enabled track.
+ // OK to call more often than that, but unnecessary.
void invalidateState(uint32_t mask);
+
+ static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask);
static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
+ static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 15f4349..ca25ba9 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -224,7 +224,7 @@
uint32_t samplingRate,
audio_format_t format,
uint32_t channels,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
if (mpAudioPolicy == NULL) {
return 0;
@@ -1349,7 +1349,7 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1368,7 +1368,7 @@
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags)
+ audio_output_flags_t flags)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 9ed905d..fbca000 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -65,8 +65,8 @@
uint32_t samplingRate = 0,
audio_format_t format = AUDIO_FORMAT_DEFAULT,
uint32_t channels = 0,
- audio_policy_output_flags_t flags =
- AUDIO_POLICY_OUTPUT_FLAG_NONE);
+ audio_output_flags_t flags =
+ AUDIO_OUTPUT_FLAG_NONE);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
new file mode 100644
index 0000000..14954f7
--- /dev/null
+++ b/services/audioflinger/FastMixer.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FastMixer"
+//#define LOG_NDEBUG 0
+
+#include <sys/atomics.h>
+#include <time.h>
+#include <utils/Log.h>
+#include <system/audio.h>
+#ifdef FAST_MIXER_STATISTICS
+#include <cpustats/CentralTendencyStatistics.h>
+#endif
+#include "AudioMixer.h"
+#include "FastMixer.h"
+
+#define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling
+#define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep
+
+namespace android {
+
+// Fast mixer thread
+bool FastMixer::threadLoop()
+{
+ static const FastMixerState initial;
+ const FastMixerState *previous = &initial, *current = &initial;
+ FastMixerState preIdle; // copy of state before we went into idle
+ struct timespec oldTs = {0, 0};
+ bool oldTsValid = false;
+ long slopNs = 0; // accumulated time we've woken up too early (> 0) or too late (< 0)
+ long sleepNs = -1; // -1: busy wait, 0: sched_yield, > 0: nanosleep
+ int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks
+ int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration
+ unsigned i;
+ for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+ fastTrackNames[i] = -1;
+ generations[i] = 0;
+ }
+ NBAIO_Sink *outputSink = NULL;
+ int outputSinkGen = 0;
+ AudioMixer* mixer = NULL;
+ short *mixBuffer = NULL;
+ enum {UNDEFINED, MIXED, ZEROED} mixBufferState = UNDEFINED;
+ NBAIO_Format format = Format_Invalid;
+ unsigned sampleRate = 0;
+ int fastTracksGen = 0;
+ long periodNs = 0; // expected period; the time required to render one mix buffer
+ long underrunNs = 0; // an underrun is likely if an actual cycle is greater than this value
+ long overrunNs = 0; // an overrun is likely if an actual cycle if less than this value
+ FastMixerDumpState dummyDumpState, *dumpState = &dummyDumpState;
+ bool ignoreNextOverrun = true; // used to ignore initial overrun and first after an underrun
+#ifdef FAST_MIXER_STATISTICS
+ CentralTendencyStatistics cts; // cycle times in seconds
+ static const unsigned kMaxSamples = 1000;
+#endif
+ unsigned coldGen = 0; // last observed mColdGen
+
+ for (;;) {
+
+ // either nanosleep, sched_yield, or busy wait
+ if (sleepNs >= 0) {
+ if (sleepNs > 0) {
+ ALOG_ASSERT(sleepNs < 1000000000);
+ const struct timespec req = {0, sleepNs};
+ nanosleep(&req, NULL);
+ } else {
+ sched_yield();
+ }
+ }
+ // default to long sleep for next cycle
+ sleepNs = FAST_DEFAULT_NS;
+
+ // poll for state change
+ const FastMixerState *next = mSQ.poll();
+ if (next == NULL) {
+ // continue to use the default initial state until a real state is available
+ ALOG_ASSERT(current == &initial && previous == &initial);
+ next = current;
+ }
+
+ FastMixerState::Command command = next->mCommand;
+ if (next != current) {
+
+ // As soon as possible of learning of a new dump area, start using it
+ dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState;
+
+ // We want to always have a valid reference to the previous (non-idle) state.
+ // However, the state queue only guarantees access to current and previous states.
+ // So when there is a transition from a non-idle state into an idle state, we make a
+ // copy of the last known non-idle state so it is still available on return from idle.
+ // The possible transitions are:
+ // non-idle -> non-idle update previous from current in-place
+ // non-idle -> idle update previous from copy of current
+ // idle -> idle don't update previous
+ // idle -> non-idle don't update previous
+ if (!(current->mCommand & FastMixerState::IDLE)) {
+ if (command & FastMixerState::IDLE) {
+ preIdle = *current;
+ current = &preIdle;
+ oldTsValid = false;
+ ignoreNextOverrun = true;
+ }
+ previous = current;
+ }
+ current = next;
+ }
+#if !LOG_NDEBUG
+ next = NULL; // not referenced again
+#endif
+
+ dumpState->mCommand = command;
+
+ switch (command) {
+ case FastMixerState::INITIAL:
+ case FastMixerState::HOT_IDLE:
+ sleepNs = FAST_HOT_IDLE_NS;
+ continue;
+ case FastMixerState::COLD_IDLE:
+ // only perform a cold idle command once
+ if (current->mColdGen != coldGen) {
+ int32_t *coldFutexAddr = current->mColdFutexAddr;
+ ALOG_ASSERT(coldFutexAddr != NULL);
+ int32_t old = android_atomic_dec(coldFutexAddr);
+ if (old <= 0) {
+ __futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
+ }
+ sleepNs = -1;
+ coldGen = current->mColdGen;
+ } else {
+ sleepNs = FAST_HOT_IDLE_NS;
+ }
+ continue;
+ case FastMixerState::EXIT:
+ delete mixer;
+ delete[] mixBuffer;
+ return false;
+ case FastMixerState::MIX:
+ case FastMixerState::WRITE:
+ case FastMixerState::MIX_WRITE:
+ break;
+ default:
+ LOG_FATAL("bad command %d", command);
+ }
+
+ // there is a non-idle state available to us; did the state change?
+ size_t frameCount = current->mFrameCount;
+ if (current != previous) {
+
+ // handle state change here, but since we want to diff the state,
+ // we're prepared for previous == &initial the first time through
+ unsigned previousTrackMask;
+
+ // check for change in output HAL configuration
+ NBAIO_Format previousFormat = format;
+ if (current->mOutputSinkGen != outputSinkGen) {
+ outputSink = current->mOutputSink;
+ outputSinkGen = current->mOutputSinkGen;
+ if (outputSink == NULL) {
+ format = Format_Invalid;
+ sampleRate = 0;
+ } else {
+ format = outputSink->format();
+ sampleRate = Format_sampleRate(format);
+ ALOG_ASSERT(Format_channelCount(format) == 2);
+ }
+ }
+
+ if ((format != previousFormat) || (frameCount != previous->mFrameCount)) {
+ // FIXME to avoid priority inversion, don't delete here
+ delete mixer;
+ mixer = NULL;
+ delete[] mixBuffer;
+ mixBuffer = NULL;
+ if (frameCount > 0 && sampleRate > 0) {
+ // FIXME new may block for unbounded time at internal mutex of the heap
+ // implementation; it would be better to have normal mixer allocate for us
+ // to avoid blocking here and to prevent possible priority inversion
+ mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
+ mixBuffer = new short[frameCount * 2];
+ periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
+ underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
+ overrunNs = (frameCount * 250000000LL) / sampleRate; // 0.25
+ } else {
+ periodNs = 0;
+ underrunNs = 0;
+ overrunNs = 0;
+ }
+ mixBufferState = UNDEFINED;
+#if !LOG_NDEBUG
+ for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+ fastTrackNames[i] = -1;
+ }
+#endif
+ // we need to reconfigure all active tracks
+ previousTrackMask = 0;
+ fastTracksGen = current->mFastTracksGen - 1;
+ } else {
+ previousTrackMask = previous->mTrackMask;
+ }
+
+ // check for change in active track set
+ unsigned currentTrackMask = current->mTrackMask;
+ if (current->mFastTracksGen != fastTracksGen) {
+ ALOG_ASSERT(mixBuffer != NULL);
+ int name;
+
+ // process removed tracks first to avoid running out of track names
+ unsigned removedTracks = previousTrackMask & ~currentTrackMask;
+ while (removedTracks != 0) {
+ i = __builtin_ctz(removedTracks);
+ removedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = ¤t->mFastTracks[i];
+ if (mixer != NULL) {
+ name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ mixer->deleteTrackName(name);
+ }
+#if !LOG_NDEBUG
+ fastTrackNames[i] = -1;
+#endif
+ generations[i] = fastTrack->mGeneration;
+ }
+
+ // now process added tracks
+ unsigned addedTracks = currentTrackMask & ~previousTrackMask;
+ while (addedTracks != 0) {
+ i = __builtin_ctz(addedTracks);
+ addedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = ¤t->mFastTracks[i];
+ AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
+ ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
+ if (mixer != NULL) {
+ // calling getTrackName with default channel mask
+ name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO);
+ ALOG_ASSERT(name >= 0);
+ fastTrackNames[i] = name;
+ mixer->setBufferProvider(name, bufferProvider);
+ mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+ (void *) mixBuffer);
+ // newly allocated track names default to full scale volume
+ mixer->enable(name);
+ }
+ generations[i] = fastTrack->mGeneration;
+ }
+
+ // finally process modified tracks; these use the same slot
+ // but may have a different buffer provider or volume provider
+ unsigned modifiedTracks = currentTrackMask & previousTrackMask;
+ while (modifiedTracks != 0) {
+ i = __builtin_ctz(modifiedTracks);
+ modifiedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = ¤t->mFastTracks[i];
+ if (fastTrack->mGeneration != generations[i]) {
+ AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
+ ALOG_ASSERT(bufferProvider != NULL);
+ if (mixer != NULL) {
+ name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ mixer->setBufferProvider(name, bufferProvider);
+ if (fastTrack->mVolumeProvider == NULL) {
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
+ (void *)0x1000);
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
+ (void *)0x1000);
+ }
+ // already enabled
+ }
+ generations[i] = fastTrack->mGeneration;
+ }
+ }
+
+ fastTracksGen = current->mFastTracksGen;
+
+ dumpState->mNumTracks = popcount(currentTrackMask);
+ }
+
+#if 1 // FIXME shouldn't need this
+ // only process state change once
+ previous = current;
+#endif
+ }
+
+ // do work using current state here
+ if ((command & FastMixerState::MIX) && (mixer != NULL)) {
+ ALOG_ASSERT(mixBuffer != NULL);
+ // update volumes
+ unsigned volumeTracks = current->mTrackMask;
+ while (volumeTracks != 0) {
+ i = __builtin_ctz(volumeTracks);
+ volumeTracks &= ~(1 << i);
+ const FastTrack* fastTrack = ¤t->mFastTracks[i];
+ int name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ if (fastTrack->mVolumeProvider != NULL) {
+ uint32_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
+ (void *)(vlr & 0xFFFF));
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
+ (void *)(vlr >> 16));
+ }
+ }
+ // process() is CPU-bound
+ mixer->process(AudioBufferProvider::kInvalidPTS);
+ mixBufferState = MIXED;
+ } else if (mixBufferState == MIXED) {
+ mixBufferState = UNDEFINED;
+ }
+ if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
+ if (mixBufferState == UNDEFINED) {
+ memset(mixBuffer, 0, frameCount * 2 * sizeof(short));
+ mixBufferState = ZEROED;
+ }
+ // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
+ // but this code should be modified to handle both non-blocking and blocking sinks
+ dumpState->mWriteSequence++;
+ ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
+ dumpState->mWriteSequence++;
+ if (framesWritten >= 0) {
+ dumpState->mFramesWritten += framesWritten;
+ } else {
+ dumpState->mWriteErrors++;
+ }
+ // FIXME count # of writes blocked excessively, CPU usage, etc. for dump
+ }
+
+ // To be exactly periodic, compute the next sleep time based on current time.
+ // This code doesn't have long-term stability when the sink is non-blocking.
+ // FIXME To avoid drift, use the local audio clock or watch the sink's fill status.
+ struct timespec newTs;
+ int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
+ if (rc == 0) {
+ if (oldTsValid) {
+ time_t sec = newTs.tv_sec - oldTs.tv_sec;
+ long nsec = newTs.tv_nsec - oldTs.tv_nsec;
+ if (nsec < 0) {
+ --sec;
+ nsec += 1000000000;
+ }
+ if (sec > 0 || nsec > underrunNs) {
+ // FIXME only log occasionally
+ ALOGV("underrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mUnderruns++;
+ sleepNs = -1;
+ ignoreNextOverrun = true;
+ } else if (nsec < overrunNs) {
+ if (ignoreNextOverrun) {
+ ignoreNextOverrun = false;
+ } else {
+ // FIXME only log occasionally
+ ALOGV("overrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mOverruns++;
+ }
+ sleepNs = periodNs - overrunNs;
+ } else {
+ sleepNs = -1;
+ ignoreNextOverrun = false;
+ }
+#ifdef FAST_MIXER_STATISTICS
+ // long-term statistics
+ cts.sample(sec + nsec * 1e-9);
+ if (cts.n() >= kMaxSamples) {
+ dumpState->mMean = cts.mean();
+ dumpState->mMinimum = cts.minimum();
+ dumpState->mMaximum = cts.maximum();
+ dumpState->mStddev = cts.stddev();
+ cts.reset();
+ }
+#endif
+ } else {
+ // first time through the loop
+ oldTsValid = true;
+ sleepNs = periodNs;
+ ignoreNextOverrun = true;
+ }
+ oldTs = newTs;
+ } else {
+ // monotonic clock is broken
+ oldTsValid = false;
+ sleepNs = periodNs;
+ }
+
+ } // for (;;)
+
+ // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
+}
+
+FastMixerDumpState::FastMixerDumpState() :
+ mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
+ mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0)
+#ifdef FAST_MIXER_STATISTICS
+ , mMean(0.0), mMinimum(0.0), mMaximum(0.0), mStddev(0.0)
+#endif
+{
+}
+
+FastMixerDumpState::~FastMixerDumpState()
+{
+}
+
+void FastMixerDumpState::dump(int fd)
+{
+#define COMMAND_MAX 32
+ char string[COMMAND_MAX];
+ switch (mCommand) {
+ case FastMixerState::INITIAL:
+ strcpy(string, "INITIAL");
+ break;
+ case FastMixerState::HOT_IDLE:
+ strcpy(string, "HOT_IDLE");
+ break;
+ case FastMixerState::COLD_IDLE:
+ strcpy(string, "COLD_IDLE");
+ break;
+ case FastMixerState::EXIT:
+ strcpy(string, "EXIT");
+ break;
+ case FastMixerState::MIX:
+ strcpy(string, "MIX");
+ break;
+ case FastMixerState::WRITE:
+ strcpy(string, "WRITE");
+ break;
+ case FastMixerState::MIX_WRITE:
+ strcpy(string, "MIX_WRITE");
+ break;
+ default:
+ snprintf(string, COMMAND_MAX, "%d", mCommand);
+ break;
+ }
+ fdprintf(fd, "FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+ " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n",
+ string, mWriteSequence, mFramesWritten,
+ mNumTracks, mWriteErrors, mUnderruns, mOverruns);
+#ifdef FAST_MIXER_STATISTICS
+ fdprintf(fd, " cycle time in ms: mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
+ mMean*1e3, mMinimum*1e3, mMaximum*1e3, mStddev*1e3);
+#endif
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
new file mode 100644
index 0000000..b24f2eb
--- /dev/null
+++ b/services/audioflinger/FastMixer.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_MIXER_H
+#define ANDROID_AUDIO_FAST_MIXER_H
+
+#include <utils/Thread.h>
+extern "C" {
+#include "../private/bionic_futex.h"
+}
+#include "StateQueue.h"
+#include "FastMixerState.h"
+
+namespace android {
+
+typedef StateQueue<FastMixerState> FastMixerStateQueue;
+
+class FastMixer : public Thread {
+
+public:
+ FastMixer() : Thread(false /*canCallJava*/) { }
+ virtual ~FastMixer() { }
+
+ FastMixerStateQueue* sq() { return &mSQ; }
+
+private:
+ virtual bool threadLoop();
+ FastMixerStateQueue mSQ;
+
+}; // class FastMixer
+
+// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys.
+// Since used non-atomically, only POD types are permitted, and the contents can't be trusted.
+// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer.
+struct FastMixerDumpState {
+ FastMixerDumpState();
+ /*virtual*/ ~FastMixerDumpState();
+
+ void dump(int fd);
+
+ FastMixerState::Command mCommand; // current command
+ uint32_t mWriteSequence; // incremented before and after each write()
+ uint32_t mFramesWritten; // total number of frames written successfully
+ uint32_t mNumTracks; // total number of active fast tracks
+ uint32_t mWriteErrors; // total number of write() errors
+ uint32_t mUnderruns; // total number of underruns
+ uint32_t mOverruns; // total number of overruns
+#ifdef FAST_MIXER_STATISTICS
+ // cycle times in seconds
+ float mMean;
+ float mMinimum;
+ float mMaximum;
+ float mStddev;
+#endif
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_MIXER_H
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
new file mode 100644
index 0000000..4eacacf
--- /dev/null
+++ b/services/audioflinger/FastMixerState.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FastMixerState.h"
+
+namespace android {
+
+FastTrack::FastTrack() :
+ mBufferProvider(NULL), mVolumeProvider(NULL), mGeneration(0)
+{
+}
+
+FastTrack::~FastTrack()
+{
+}
+
+FastMixerState::FastMixerState() :
+ mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
+ mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), mDumpState(NULL)
+{
+}
+
+FastMixerState::~FastMixerState()
+{
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
new file mode 100644
index 0000000..64171ac
--- /dev/null
+++ b/services/audioflinger/FastMixerState.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_MIXER_STATE_H
+#define ANDROID_AUDIO_FAST_MIXER_STATE_H
+
+#include "AudioBufferProvider.h"
+#include "NBAIO.h"
+
+namespace android {
+
+struct FastMixerDumpState;
+
+class VolumeProvider {
+public:
+ // Return the track volume in U4_12 format: left in lower half, right in upper half. The
+ // provider implementation is responsible for validating that the return value is in range.
+ virtual uint32_t getVolumeLR() = 0;
+protected:
+ VolumeProvider() { }
+ virtual ~VolumeProvider() { }
+};
+
+// Represents the state of a fast track
+struct FastTrack {
+ FastTrack();
+ /*virtual*/ ~FastTrack();
+
+ AudioBufferProvider* mBufferProvider; // must not be NULL
+ VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale
+ int mGeneration; // increment when any field is assigned
+};
+
+// Represents a single state of the fast mixer
+struct FastMixerState {
+ FastMixerState();
+ /*virtual*/ ~FastMixerState();
+
+ static const unsigned kMaxFastTracks = 8; // must be between 2 and 32 inclusive
+
+ // all pointer fields use raw pointers; objects are owned and ref-counted by the normal mixer
+ FastTrack mFastTracks[kMaxFastTracks];
+ int mFastTracksGen; // increment when any mFastTracks[i].mGeneration is incremented
+ unsigned mTrackMask; // bit i is set if and only if mFastTracks[i] != NULL
+ NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated
+ int mOutputSinkGen; // increment when mOutputSink is assigned
+ size_t mFrameCount; // number of frames per fast mix buffer
+ enum Command {
+ INITIAL = 0, // used only for the initial state
+ HOT_IDLE = 1, // do nothing
+ COLD_IDLE = 2, // wait for the futex
+ IDLE = 3, // either HOT_IDLE or COLD_IDLE
+ EXIT = 4, // exit from thread
+ // The following commands also process configuration changes, and can be "or"ed:
+ MIX = 0x8, // mix tracks
+ WRITE = 0x10, // write to output sink
+ MIX_WRITE = 0x18, // mix tracks and write to output sink
+ } mCommand;
+ int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex
+ unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once
+ // This might be a one-time configuration rather than per-state
+ FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically
+}; // struct FastMixerState
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_MIXER_STATE_H
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
new file mode 100644
index 0000000..ae263f5
--- /dev/null
+++ b/services/audioflinger/StateQueue.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "StateQueue"
+//#define LOG_NDEBUG 0
+
+#include <time.h>
+#include <cutils/atomic.h>
+#include <utils/Log.h>
+#include "StateQueue.h"
+
+namespace android {
+
+// Constructor and destructor
+
+template<typename T> StateQueue<T>::StateQueue() :
+ mNext(NULL), mAck(NULL), mCurrent(NULL),
+ mMutating(&mStates[0]), mExpecting(NULL),
+ mInMutation(false), mIsDirty(false), mIsInitialized(false)
+{
+}
+
+template<typename T> StateQueue<T>::~StateQueue()
+{
+}
+
+// Observer APIs
+
+template<typename T> const T* StateQueue<T>::poll()
+{
+ const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext);
+ if (next != mCurrent) {
+ mAck = next; // no additional barrier needed
+ mCurrent = next;
+ }
+ return next;
+}
+
+// Mutator APIs
+
+template<typename T> T* StateQueue<T>::begin()
+{
+ ALOG_ASSERT(!mInMutation, "begin() called when in a mutation");
+ mInMutation = true;
+ return mMutating;
+}
+
+template<typename T> void StateQueue<T>::end(bool didModify)
+{
+ ALOG_ASSERT(mInMutation, "end() called when not in a mutation");
+ ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization");
+ if (didModify) {
+ mIsDirty = true;
+ mIsInitialized = true;
+ }
+ mInMutation = false;
+}
+
+template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
+{
+#define PUSH_BLOCK_ACK_NS 3000000L // 3 ms: time between checks for ack in push()
+ // FIXME should be configurable
+ static const struct timespec req = {0, PUSH_BLOCK_ACK_NS};
+
+ ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
+
+ if (mIsDirty) {
+
+ // wait for prior push to be acknowledged
+ if (mExpecting != NULL) {
+ for (;;) {
+ const T *ack = (const T *) mAck; // no additional barrier needed
+ if (ack == mExpecting) {
+ // unnecessary as we're about to rewrite
+ //mExpecting = NULL;
+ break;
+ }
+ if (block == BLOCK_NEVER) {
+ return false;
+ }
+ nanosleep(&req, NULL);
+ }
+ }
+
+ // publish
+ android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext);
+ mExpecting = mMutating;
+
+ // copy with circular wraparound
+ if (++mMutating >= &mStates[kN]) {
+ mMutating = &mStates[0];
+ }
+ *mMutating = *mExpecting;
+ mIsDirty = false;
+
+ }
+
+ // optionally wait for this push or a prior push to be acknowledged
+ if (block == BLOCK_UNTIL_ACKED) {
+ if (mExpecting != NULL) {
+ for (;;) {
+ const T *ack = (const T *) mAck; // no additional barrier needed
+ if (ack == mExpecting) {
+ mExpecting = NULL;
+ break;
+ }
+ nanosleep(&req, NULL);
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace android
+
+// hack for gcc
+#ifdef STATE_QUEUE_INSTANTIATIONS
+#include STATE_QUEUE_INSTANTIATIONS
+#endif
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
new file mode 100644
index 0000000..fe72ddc
--- /dev/null
+++ b/services/audioflinger/StateQueue.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_STATE_QUEUE_H
+#define ANDROID_AUDIO_STATE_QUEUE_H
+
+namespace android {
+
+// manages a FIFO queue of states
+template<typename T> class StateQueue {
+
+public:
+ StateQueue();
+ virtual ~StateQueue();
+
+ // Observer APIs
+
+ // Poll for a state change. Returns a pointer to a read-only state,
+ // or NULL if the state has not been initialized yet.
+ // If a new state has not pushed by mutator since the previous poll,
+ // then the returned pointer will be unchanged.
+ // The previous state pointer is guaranteed to still be valid;
+ // this allows the observer to diff the previous and new states.
+ const T* poll();
+
+ // Mutator APIs
+
+ // Begin a mutation. Returns a pointer to a read/write state, except the
+ // first time it is called the state is write-only and _must_ be initialized.
+ // Mutations cannot be nested.
+ // If the state is dirty and has not been pushed onto the state queue yet, then
+ // this new mutation will be squashed together with the previous one.
+ T* begin();
+
+ // End the current mutation and indicate whether caller modified the state.
+ // If didModify is true, then the state is marked dirty (in need of pushing).
+ // There is no rollback option because modifications are done in place.
+ // Does not automatically push the new state onto the state queue.
+ void end(bool didModify = true);
+
+ // Push a new state, if any, out to the observer via the state queue.
+ // For BLOCK_NEVER, returns:
+ // true if not dirty, or dirty and pushed successfully
+ // false if dirty and not pushed because that would block; remains dirty
+ // For BLOCK_UNTIL_PUSHED and BLOCK_UNTIL_ACKED, always returns true.
+ // No-op if there are no pending modifications (not dirty), except
+ // for BLOCK_UNTIL_ACKED it will wait until a prior push has been acknowledged.
+ // Must not be called in the middle of a mutation.
+ enum block_t {
+ BLOCK_NEVER, // do not block
+ BLOCK_UNTIL_PUSHED, // block until there's a slot available for the push
+ BLOCK_UNTIL_ACKED, // also block until the push is acknowledged by the observer
+ };
+ bool push(block_t block = BLOCK_NEVER);
+
+ // Return whether the current state is dirty (modified and not pushed).
+ bool isDirty() const { return mIsDirty; }
+
+private:
+ static const unsigned kN = 4; // values != 4 are not supported by this code
+ T mStates[kN]; // written by mutator, read by observer
+
+ // "volatile" is meaningless with SMP, but here it indicates that we're using atomic ops
+ volatile const T* mNext; // written by mutator to advance next, read by observer
+ volatile const T* mAck; // written by observer to acknowledge advance of next, read by mutator
+
+ // only used by observer
+ const T* mCurrent; // most recent value returned by poll()
+
+ // only used by mutator
+ T* mMutating; // where updates by mutator are done in place
+ const T* mExpecting; // what the mutator expects mAck to be set to
+ bool mInMutation; // whether we're currently in the middle of a mutation
+ bool mIsDirty; // whether mutating state has been modified since last push
+ bool mIsInitialized; // whether mutating state has been initialized yet
+
+}; // class StateQueue
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_STATE_QUEUE_H
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
new file mode 100644
index 0000000..077582f
--- /dev/null
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FastMixerState.h"
+#include "StateQueue.h"
+
+// FIXME hack for gcc
+
+namespace android {
+
+template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+
+}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index dc3f083..92d1223 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -21,7 +21,6 @@
#include <stdio.h>
#include <sys/types.h>
#include <pthread.h>
-#include <time.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -34,7 +33,6 @@
#include <hardware/hardware.h>
#include <media/AudioSystem.h>
#include <media/mediaplayer.h>
-#include <utils/Condition.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -44,8 +42,6 @@
namespace android {
-#define WAIT_RELEASE_TIMEOUT 250 // 250ms
-
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
// Use "adb shell dumpsys media.camera -v 1" to change it.
@@ -68,13 +64,6 @@
return IPCThreadState::self()->getCallingUid();
}
-static long long getTimeInMs() {
- struct timeval t;
- t.tv_sec = t.tv_usec = 0;
- gettimeofday(&t, NULL);
- return t.tv_sec * 1000LL + t.tv_usec / 1000;
-}
-
// ----------------------------------------------------------------------------
// This is ugly and only safe if we never re-create the CameraService, but
@@ -142,7 +131,7 @@
}
sp<ICamera> CameraService::connect(
- const sp<ICameraClient>& cameraClient, int cameraId, bool force, bool keep) {
+ const sp<ICameraClient>& cameraClient, int cameraId) {
int callingPid = getCallingPid();
sp<CameraHardwareInterface> hardware = NULL;
@@ -168,73 +157,27 @@
return NULL;
}
- if (keep && !checkCallingPermission(String16("android.permission.KEEP_CAMERA"))) {
- ALOGE("connect X (pid %d) rejected (no KEEP_CAMERA permission).", callingPid);
- return NULL;
- }
-
Mutex::Autolock lock(mServiceLock);
- // Check if there is an existing client.
- client = mClient[cameraId].promote();
- if (client != 0 &&
- cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
- LOG1("connect X (pid %d) (the same client)", callingPid);
- return client;
- }
-
- if (!force) {
- if (mClient[cameraId].promote() != 0) {
- ALOGW("connect X (pid %d) rejected (existing client).", callingPid);
- return NULL;
- }
- mClient[cameraId].clear();
- if (mBusy[cameraId]) {
- ALOGW("connect X (pid %d) rejected (camera %d is still busy).",
- callingPid, cameraId);
- return NULL;
- }
- } else { // force == true
- int i = 0;
- long long start_time = getTimeInMs();
- while (i < mNumberOfCameras) {
- if (getTimeInMs() - start_time >= 3000LL) {
- ALOGE("connect X (pid %d) rejected (timeout 3s)", callingPid);
+ if (mClient[cameraId] != 0) {
+ client = mClient[cameraId].promote();
+ if (client != 0) {
+ if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
+ LOG1("CameraService::connect X (pid %d) (the same client)",
+ callingPid);
+ return client;
+ } else {
+ ALOGW("CameraService::connect X (pid %d) rejected (existing client).",
+ callingPid);
return NULL;
}
-
- client = mClient[i].promote();
- if (client != 0) {
- if (client->keep()) {
- ALOGW("connect X (pid %d) rejected (existing client wants to keeps the camera)",
- callingPid);
- return NULL;
- } else {
- ALOGW("New client (pid %d, id=%d). Disconnect the existing client (id=%d).",
- callingPid, cameraId, i);
- // Do not hold mServiceLock because disconnect will try to get it.
- mServiceLock.unlock();
- client->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0, &i);
- client->waitRelease(WAIT_RELEASE_TIMEOUT);
- client->disconnectInternal(false);
- mServiceLock.lock();
- // Restart from the first client because a new client may have connected
- // when mServiceLock is unlocked.
- i = 0;
- continue;
- }
- }
-
- if (mBusy[i]) {
- // Give the client a chance to release the hardware.
- mServiceLock.unlock();
- usleep(10 * 1000);
- mServiceLock.lock();
- i = 0; // Restart from the first client
- continue;
- }
-
- i++;
}
+ mClient[cameraId].clear();
+ }
+
+ if (mBusy[cameraId]) {
+ ALOGW("CameraService::connect X (pid %d) rejected"
+ " (camera %d is still busy).", callingPid, cameraId);
+ return NULL;
}
struct camera_info info;
@@ -252,13 +195,7 @@
return NULL;
}
- client = new Client(this, cameraClient, hardware, cameraId, info.facing,
- callingPid, keep);
- // We need to clear the hardware here. After the destructor of mServiceLock
- // finishes, a new client may connect and disconnect this client. If this
- // reference is not cleared, the destructor of CameraHardwareInterface
- // cannot run. The new client will not be able to connect.
- hardware.clear();
+ client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X (id %d)", cameraId);
return client;
@@ -399,7 +336,7 @@
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const sp<CameraHardwareInterface>& hardware,
- int cameraId, int cameraFacing, int clientPid, bool keep) {
+ int cameraId, int cameraFacing, int clientPid) {
int callingPid = getCallingPid();
LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
@@ -409,7 +346,6 @@
mCameraId = cameraId;
mCameraFacing = cameraFacing;
mClientPid = clientPid;
- mKeep = keep;
mMsgEnabled = 0;
mSurface = 0;
mPreviewWindow = 0;
@@ -544,24 +480,18 @@
}
void CameraService::Client::disconnect() {
- disconnectInternal(true);
-}
-
-void CameraService::Client::disconnectInternal(bool needCheckPid) {
int callingPid = getCallingPid();
- LOG1("disconnectInternal E (pid %d)", callingPid);
+ LOG1("disconnect E (pid %d)", callingPid);
Mutex::Autolock lock(mLock);
- if (needCheckPid) {
- if (checkPid() != NO_ERROR) {
- ALOGW("different client - don't disconnect");
- return;
- }
+ if (checkPid() != NO_ERROR) {
+ ALOGW("different client - don't disconnect");
+ return;
+ }
- if (mClientPid <= 0) {
- LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
- return;
- }
+ if (mClientPid <= 0) {
+ LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
+ return;
}
// Make sure disconnect() is done once and once only, whether it is called
@@ -588,16 +518,8 @@
mCameraService->removeClient(mCameraClient);
mCameraService->setCameraFree(mCameraId);
- mReleaseCondition.signal();
- LOG1("disconnectInternal X (pid %d)", callingPid);
-}
-
-void CameraService::Client::waitRelease(int ms) {
- Mutex::Autolock lock(mLock);
- if (mHardware != 0) {
- mReleaseCondition.waitRelative(mLock, ms * 1000000);
- }
+ LOG1("disconnect X (pid %d)", callingPid);
}
// ----------------------------------------------------------------------------
@@ -1330,11 +1252,6 @@
return -1;
}
-// Whether the client wants to keep the camera from taking
-bool CameraService::Client::keep() const {
- return mKeep;
-}
-
// ----------------------------------------------------------------------------
static const int kDumpLockRetries = 50;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 7972201..5b63399 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -46,8 +46,7 @@
virtual int32_t getNumberOfCameras();
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
- bool force, bool keep);
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
virtual void removeClient(const sp<ICameraClient>& cameraClient);
// returns plain pointer of client. Note that mClientLock should be acquired to
// prevent the client from destruction. The result can be NULL.
@@ -119,8 +118,7 @@
const sp<CameraHardwareInterface>& hardware,
int cameraId,
int cameraFacing,
- int clientPid,
- bool keep);
+ int clientPid);
~Client();
// return our camera client
@@ -179,19 +177,12 @@
const sp<IBinder>& binder,
const sp<ANativeWindow>& window);
- void disconnectInternal(bool needCheckPid);
- bool keep() const;
- void waitRelease(int ms);
-
-
// these are initialized in the constructor.
sp<CameraService> mCameraService; // immutable after constructor
sp<ICameraClient> mCameraClient;
int mCameraId; // immutable after constructor
int mCameraFacing; // immutable after constructor
pid_t mClientPid;
- // Client wants to keep the camera from taking by other clients.
- bool mKeep;
sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
int mPreviewCallbackFlag;
int mOrientation; // Current display orientation
@@ -199,8 +190,6 @@
// Ensures atomicity among the public methods
mutable Mutex mLock;
- // This will get notified when the hardware is released.
- Condition mReleaseCondition;
// This is a binder of Surface or SurfaceTexture.
sp<IBinder> mSurface;
sp<ANativeWindow> mPreviewWindow;